From 39a380282ae2f9af33f561b89febac6d2b7ac230 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 14 Dec 2019 15:37:23 -0800 Subject: [PATCH 01/89] File win32junk.h: Add struct ustname, uname(). --- src/win32junk.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/win32junk.h b/src/win32junk.h index 603751ff..dd1e37a0 100644 --- a/src/win32junk.h +++ b/src/win32junk.h @@ -100,6 +100,10 @@ struct statvfs { unsigned long f_bavail; }; +struct utsname { + char machine[]; +}; + //================================================ // POSIX function prototypes //================================================ @@ -126,6 +130,7 @@ extern int sigtimedwait( sigset_t const* set, siginfo_t* info, timespec cons extern int socketpair( int domain, int type, int protocol, int sv[2] ); extern int statvfs( char const* path, struct statvfs* buf ); extern char* strsignal( int sig ); +extern int uname( utsname* buf ) //================================================ // GNU C Library function prototypes From b3878ce653ef6cb3f59c0e958e3d0f5af20c36f1 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 14 Dec 2019 15:37:46 -0800 Subject: [PATCH 02/89] File CMakeLists.txt: Update Qt path. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b1b6626..df071011 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set (PROJECT_VERSION "${LF_VERSION_MAJOR}.${LF_VERSION_MINOR}.${LF_VERSION_PATCH if(WIN32) # Set the Qt5 path -set(Qt5_DIR "F:\\\\Qt\\\\5.13.1\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") +set(Qt5_DIR "F:\\\\Qt\\\\5.13.2\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") endif(WIN32) project(lf) From da2b17b7520cc9a96f5ce5ce95a9902b39ac1857 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 14 Dec 2019 15:42:45 -0800 Subject: [PATCH 03/89] Script rebuild: Whitespace; small fixes recommended by shellcheck. --- rebuild | 121 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 57 deletions(-) diff --git a/rebuild b/rebuild index 283505a1..4189503b 100755 --- a/rebuild +++ b/rebuild @@ -1,18 +1,9 @@ #!/usr/bin/env bash -if [ -z "${BUILDDIR}" ] -then - export BUILDDIR="/home/lumen/Volumetric/LightField/build" -fi +[ -z "${BUILDDIR}" ] && export BUILDDIR="/home/lumen/Volumetric/LightField/build" -rebuild () { - local BUILD=debug - local CLEAN= - local NUKE= - local QMAKE= - - if ! getopt -Q -q -n 'rebuild' -o 'cnqrx' -l 'clean,nuke-first,qmake-anyway,release' -- "$@"; then - cat < ${BUILDDIR}/.lastbuildmode - NUKE=yes - fi - - local LASTBUILDMODE="$(<${BUILDDIR}/.lastbuildmode)" - if [ -n "${LASTBUILDMODE}" -a "${BUILD}" != "${LASTBUILDMODE}" ] - then - echo Switching from ${LASTBUILDMODE} to ${BUILD}. - NUKE=yes - fi - - if [ -n "${NUKE}" ] - then - unset CLEAN - QMAKE=yes - rm -dr ${BUILDDIR} && mkdir -p ${BUILDDIR} - fi + if [ ! -f "${BUILDDIR}/.lastbuildmode" ] + then + echo "${BUILD}" > "${BUILDDIR}/.lastbuildmode" + NUKE=yes + fi + + LASTBUILDMODE="$(<${BUILDDIR}/.lastbuildmode)" + if [ -n "${LASTBUILDMODE}" ] && [ "${BUILD}" != "${LASTBUILDMODE}" ] + then + echo Switching from "${LASTBUILDMODE}" to "${BUILD}". + NUKE=yes + fi + + if [ -n "${NUKE}" ] + then + unset CLEAN + QMAKE=yes + rm -dr "${BUILDDIR}" && mkdir -p "${BUILDDIR}" + fi else - mkdir -p ${BUILDDIR} + mkdir -p "${BUILDDIR}" fi - cd ${BUILDDIR} + cd "${BUILDDIR}" if [ -n "${CLEAN}" ] then - QMAKE=yes - make distclean + QMAKE=yes + make distclean fi - if [ -n "${QMAKE}" -o ! -f Makefile -o ../qt/lf.pro -nt Makefile ] + if [ -n "${QMAKE}" ] || [ ! -f Makefile ] || [ ../qt/lf.pro -nt Makefile ] then - qmake CONFIG+=${BUILD} ../qt/lf.pro || return - echo "${BUILD}" > ${BUILDDIR}/.lastbuildmode + qmake CONFIG+="${BUILD}" ../qt/lf.pro || return + echo "${BUILD}" > "${BUILDDIR}/.lastbuildmode" fi - if ! make -q + if make -q 1> /dev/null 2>&1 then - rm buildinfo.o + echo "make: Nothing to be done for 'first'." + return fi - make -j${QT_BUILD_JOBS-12} + rm buildinfo.o 1> /dev/null 2>&1 + make -j"${QT_BUILD_JOBS-12}" } rebuild $* From efbe81d8b59096bb6e2668ca81691a2d68aada1e Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 14 Dec 2019 16:48:40 -0800 Subject: [PATCH 04/89] Class UpgradeManager: Add support for multiple architectures, release trains. --- src/upgrademanager.cpp | 150 ++++++++++++++++++++++++++++++++--------- src/upgrademanager.h | 6 ++ 2 files changed, 123 insertions(+), 33 deletions(-) diff --git a/src/upgrademanager.cpp b/src/upgrademanager.cpp index 9e4d08db..5c34ee84 100644 --- a/src/upgrademanager.cpp +++ b/src/upgrademanager.cpp @@ -1,5 +1,7 @@ #include "pch.h" +#include + #include "upgrademanager.h" #include "gpgsignaturechecker.h" @@ -12,32 +14,31 @@ namespace { - QStringList UpgradeKitFileGlobs { - "lightfield-debug_*_amd64.kit", - "lightfield-release_*_amd64.kit" + QStringList const UpgradeKitFileGlobs { + "lightfield-*.kit", }; - QStringList UpgradeKitDirGlobs { - "lightfield-debug_*_amd64", - "lightfield-release_*_amd64" + QStringList const UpgradeKitDirGlobs { + "lightfield-*", }; - QMap BuildTypeToString { + QMap const BuildTypeToString { { BuildType::Release, "release" }, { BuildType::Debug, "debug" }, }; - QMap StringToBuildType { + QMap const StringToBuildType { { "release", BuildType::Release }, { "debug", BuildType::Debug }, }; - QRegularExpression const MetadataFieldMatcherRegex { "^([-0-9A-Za-z]+):\\s*" }; - QRegularExpression const WhitespaceRegex { "\\s+" }; - QRegularExpression const CommentMatcherRegex { "\\s+#.*$" }; + QRegularExpression const MetadataFieldMatcherRegex { "^([-0-9A-Za-z]+):\\s*" }; + QRegularExpression const WhitespaceRegex { "\\s+" }; + QRegularExpression const CommentMatcherRegex { "\\s+#.*$" }; + QRegularExpression const KitFileMatcherRegex { "^lightfield(?:-(.*?))?-(debug|release)_(.*?)_(.*?)\\.kit$" }; + QRegularExpression const KitDirMatcherRegex { "^lightfield(?:-(.*?))?-(debug|release)_(.*?)_(.*?)$" }; - QStringList ExpectedMetadataFields { - "Metadata-Version", + QStringList const ExpectedMetadataV1Fields { "Version", "Build-Type", "Release-Date", @@ -45,6 +46,21 @@ namespace { "Checksums-SHA256" }; + QStringList const ExpectedMetadataV2Fields { + "Architecture", + "Release-Train" + }; + + QStringList _EnsureMapContainsKeys( QMap map, QStringList keyList ) { + QStringList result; + for ( auto const& key : keyList ) { + if ( !map.contains( key ) ) { + result += key; + } + } + return result; + } + } UpgradeManager::UpgradeManager( QObject* parent ): QObject( parent ) { @@ -52,6 +68,13 @@ UpgradeManager::UpgradeManager( QObject* parent ): QObject( parent ) { _stdoutLogger = new StdioLogger { "apt-get/stdout", this }; _processRunner = new ProcessRunner { this }; + { + utsname u; + + uname( &u ); + _architecture = u.machine; + } + QObject::connect( _processRunner, &ProcessRunner::readyReadStandardError, _stderrLogger, &StdioLogger::read ); QObject::connect( _processRunner, &ProcessRunner::readyReadStandardOutput, _stdoutLogger, &StdioLogger::read ); @@ -106,33 +129,80 @@ void UpgradeManager::_checkForUpgrades( QString const& upgradesPath ) { for ( auto kitDirInfo : QDir { UpdatesRootPath }.entryInfoList( UpgradeKitDirGlobs, QDir::Dirs | QDir::Readable | QDir::Executable, QDir::Name ) ) { QString kitPath { kitDirInfo.absoluteFilePath( ) }; QDir kitDir { kitPath }; - debug( " + found unpacked kit in '%s'\n", kitPath.toUtf8( ).data( ) ); + + auto match { KitDirMatcherRegex.match( kitDirInfo.fileName( ) ) }; + if ( !match.hasMatch( ) ) { + debug( " + ignoring unpacked kit '%s': folder name doesn't match schema\n", kitPath.toUtf8( ).data( ) ); + continue; + } + if ( match.captured( 4 ) != _architecture ) { + debug( " + ignoring unpacked kit '%s': wrong architecture\n", kitPath.toUtf8( ).data( ) ); + continue; + } if ( !kitDir.exists( "version.inf" ) ) { - debug( " + deleting bad unpacked kit: version.inf file is missing\n" ); + debug( " + deleting bad unpacked kit '%s': version.inf file is missing\n", kitPath.toUtf8( ).data( ) ); kitDir.removeRecursively( ); continue; } if ( !kitDir.exists( "version.inf.sig" ) ) { - debug( " + deleting bad unpacked kit: version.inf.sig file is missing\n" ); + debug( " + deleting bad unpacked kit '%s': version.inf.sig file is missing\n", kitPath.toUtf8( ).data( ) ); kitDir.removeRecursively( ); continue; } + debug( + "+ UpgradeManager::_checkForUpgrades: found unpacked kit in '%s'\n" + " + Version: %s\n" + " + Build type: %s\n" + " + Release train: %s\n" + " + Architecture: %s\n" + "", + kitPath.toUtf8( ).data( ), + match.captured( 3 ).toUtf8( ).data( ), + match.captured( 2 ).toUtf8( ).data( ), + match.captured( 1 ).toUtf8( ).data( ), + match.captured( 4 ).toUtf8( ).data( ) + ); + _unprocessedUpgradeKits.append( UpgradeKitInfo { kitPath } ); } if ( !upgradesPath.isEmpty( ) ) { debug( "+ UpgradeManager::_checkForUpgrades: looking for upgrade kits in path '%s'\n", upgradesPath.toUtf8( ).data( ) ); for ( auto kitFile : QDir { upgradesPath }.entryInfoList( UpgradeKitFileGlobs, QDir::Files | QDir::Readable, QDir::Name ) ) { - debug( "+ UpgradeManager::_checkForUpgrades: found kit file '%s'\n", kitFile.absoluteFilePath( ).toUtf8( ).data( ) ); + auto kitFilePath { kitFile.absoluteFilePath( ) }; - QFileInfo sigFile { kitFile.absoluteFilePath( ) % ".sig" }; + auto match { KitFileMatcherRegex.match( kitFile.fileName( ) ) }; + if ( !match.hasMatch( ) ) { + debug( " + ignoring kit '%s': file name doesn't match schema\n", kitFilePath.toUtf8( ).data( ) ); + continue; + } + if ( match.captured( 4 ) != _architecture ) { + debug( " + ignoring kit '%s': wrong architecture\n", kitFilePath.toUtf8( ).data( ) ); + continue; + } + + QFileInfo sigFile { kitFilePath % ".sig" }; if ( !sigFile.exists( ) ) { - debug( " + ignoring kit: signature file is missing\n" ); + debug( " + ignoring kit '%s': signature file is missing\n", kitFilePath.toUtf8( ).data( ) ); continue; } + debug( + "+ UpgradeManager::_checkForUpgrades: found kit file '%s'\n" + " + Version: '%s'\n" + " + Build type: '%s'\n" + " + Release train: '%s'\n" + " + Architecture: '%s'\n" + "", + kitFilePath.toUtf8( ).data( ), + match.captured( 3 ).toUtf8( ).data( ), + match.captured( 2 ).toUtf8( ).data( ), + match.captured( 1 ).toUtf8( ).data( ), + match.captured( 4 ).toUtf8( ).data( ) + ); + _unprocessedUpgradeKits.append( UpgradeKitInfo { kitFile, sigFile } ); } } @@ -378,33 +448,38 @@ bool UpgradeManager::_parseVersionInfo( QString const& versionInfoFileName, Upgr ++index; } - bool missingMetadata = false; - for ( auto const& field : ExpectedMetadataFields ) { - if ( !fields.contains( field ) ) { - debug( " + metadata is missing field \"%s\"\n", field.toUtf8( ).data( ) ); - missingMetadata = true; - } - } - if ( missingMetadata ) { - return false; - } - { - update.metadataVersionString = fields["Metadata-Version"]; + update.metadataVersionString = fields.value( "Metadata-Version" ); bool ok = false; - update.metadataVersion = update.metadataVersionString.toInt( &ok ); + update.metadataVersion = update.metadataVersionString.isEmpty( ) ? 0 : update.metadataVersionString.toInt( &ok ); if ( !ok ) { debug( " + bad metadata version \"%s\"\n", update.metadataVersionString.toUtf8( ).data( ) ); return false; } - if ( update.metadataVersion != 1 ) { + if ( ( update.metadataVersion < 1 ) || ( update.metadataVersion > 2 ) ) { debug( " + unknown metadata version \"%s\"\n", update.metadataVersionString.toUtf8( ).data( ) ); return false; } } + QStringList missingMetadataFields; + if ( update.metadataVersion >= 1 ) { + if ( auto missingFields = _EnsureMapContainsKeys( fields, ExpectedMetadataV1Fields ); !missingFields.isEmpty( ) ) { + missingMetadataFields += missingFields; + } + } + if ( update.metadataVersion >= 2 ) { + if ( auto missingFields = _EnsureMapContainsKeys( fields, ExpectedMetadataV2Fields ); !missingFields.isEmpty( ) ) { + missingMetadataFields += missingFields; + } + } + if ( !missingMetadataFields.isEmpty( ) ) { + debug( " + metadata is missing field%s %s\n", missingMetadataFields.count( ) != 1 ? "s" : "", missingMetadataFields.join( ", " ) ); + return false; + } + { update.versionString = fields["Version"]; @@ -465,6 +540,15 @@ bool UpgradeManager::_parseVersionInfo( QString const& versionInfoFileName, Upgr debug( " + file '%s': checksum %s\n", ( path + pieces[1] ).toUtf8( ).data( ), pieces[0].toUtf8( ).data( ) ); } + if ( update.metadataVersion > 1 ) { + update.architecture = fields["Architecture"]; + if ( _architecture != update.architecture ) { + debug( " + wrong architecture '%s'\n", update.architecture.toUtf8( ).data( ) ); + return false; + } + update.releaseTrain = fields["Release-Train"]; + } + return true; } diff --git a/src/upgrademanager.h b/src/upgrademanager.h index c0128776..2f62719f 100644 --- a/src/upgrademanager.h +++ b/src/upgrademanager.h @@ -50,8 +50,12 @@ class UpgradeKitInfo { BuildType buildType { }; + QString architecture; + QDate releaseDate; + QString releaseTrain; + QString description; QMap checksums; @@ -99,6 +103,8 @@ class UpgradeManager: public QObject { QString _stderrJournal; QString _stdoutJournal; + QString _architecture; + void _flushLoggers( ); void _clearJournals( ); void _checkForUpgrades( QString const& upgradesPath ); From bcde0b6a96dd07eedc174fdfde5a714a5932d5eb Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sun, 15 Dec 2019 16:41:08 -0800 Subject: [PATCH 05/89] Release train changes: Start adjusting Debian packaging files. --- debian/{debug-control => control.in} | 0 debian/lightfield-release.install | 6 --- debian/lightfield-release.postinst | 51 ------------------- debian/lightfield-release.postrm | 47 ----------------- debian/lightfield-release.preinst | 33 ------------ debian/lightfield-release.prerm | 43 ---------------- ...ld-debug.install => lightfield.install.in} | 0 ...-debug.postinst => lightfield.postinst.in} | 0 ...ield-debug.postrm => lightfield.postrm.in} | 0 ...ld-debug.preinst => lightfield.preinst.in} | 0 ...tfield-debug.prerm => lightfield.prerm.in} | 0 debian/release-control | 44 ---------------- 12 files changed, 224 deletions(-) rename debian/{debug-control => control.in} (100%) delete mode 100644 debian/lightfield-release.install delete mode 100644 debian/lightfield-release.postinst delete mode 100644 debian/lightfield-release.postrm delete mode 100644 debian/lightfield-release.preinst delete mode 100644 debian/lightfield-release.prerm rename debian/{lightfield-debug.install => lightfield.install.in} (100%) rename debian/{lightfield-debug.postinst => lightfield.postinst.in} (100%) rename debian/{lightfield-debug.postrm => lightfield.postrm.in} (100%) rename debian/{lightfield-debug.preinst => lightfield.preinst.in} (100%) rename debian/{lightfield-debug.prerm => lightfield.prerm.in} (100%) delete mode 100644 debian/release-control diff --git a/debian/debug-control b/debian/control.in similarity index 100% rename from debian/debug-control rename to debian/control.in diff --git a/debian/lightfield-release.install b/debian/lightfield-release.install deleted file mode 100644 index ea4bc7c8..00000000 --- a/debian/lightfield-release.install +++ /dev/null @@ -1,6 +0,0 @@ -files/etc/systemd/system/getty@tty1.service.d/override.conf etc/systemd/system/getty@tty1.service.d -files/lib/systemd/system/clean-up-mount-points.service lib/systemd/system -files/lib/systemd/system/set-projector-power.service lib/systemd/system -files/usr/bin/lf usr/bin -files/usr/bin/mountmon usr/bin -files/usr/bin/set-projector-power usr/bin diff --git a/debian/lightfield-release.postinst b/debian/lightfield-release.postinst deleted file mode 100644 index d5cd0902..00000000 --- a/debian/lightfield-release.postinst +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -# postinst script for lightfield-release -# -# see: dh_installdeb(1) -# -#set -e -# -# summary of how this script can be called: -# * `configure' -# * `abort-upgrade' -# * `abort-remove' `in-favour' -# -# * `abort-remove' -# * `abort-deconfigure' `in-favour' -# `removing' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -#echo "+ lightfield-release.postinst: args are $*" 1>&2 -case "$1" in - configure) - perl -lp -i -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null - - echo "deb file:/var/lib/lightfield/software-updates/lightfield-release_1.0.10.0_amd64 ./" > /etc/apt/sources.list.d/volumetric-lightfield.list - chown lumen:lumen /etc/apt/sources.list.d/volumetric-lightfield.list - chmod 644 /etc/apt/sources.list.d/volumetric-lightfield.list - - systemctl daemon-reload - systemctl set-default multi-user.target - systemctl enable --now set-projector-power.service clean-up-mount-points.service - systemctl enable getty@tty1.service - systemctl daemon-reload - ;; - - abort-upgrade|abort-remove|abort-deconfigure) - ;; - - *) - echo "postinst called with unknown argument '$1'" 1>&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/debian/lightfield-release.postrm b/debian/lightfield-release.postrm deleted file mode 100644 index 427410eb..00000000 --- a/debian/lightfield-release.postrm +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -# postrm script for lightfield-release -# -# see: dh_installdeb(1) -# -#set -e -# -# summary of how this script can be called: -# * `remove' -# * `purge' -# * `upgrade' -# * `failed-upgrade' -# * `abort-install' -# * `abort-install' -# * `abort-upgrade' -# * `disappear' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -#echo "+ lightfield-release.postrm: args are $*" 1>&2 -case "$1" in - remove) - echo + Setting default back to graphical.target - systemctl set-default graphical.target - echo + Getting default - systemctl get-default - echo + Starting tty1 - systemctl start getty@tty1 - ;; - - purge|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) - ;; - - *) - echo "postrm called with unknown argument '$1'" 1>&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/debian/lightfield-release.preinst b/debian/lightfield-release.preinst deleted file mode 100644 index f18ca28b..00000000 --- a/debian/lightfield-release.preinst +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# preinst script for lightfield-release -# -# see: dh_installdeb(1) -# -#set -e -# -# summary of how this script can be called: -# * `install' -# * `install' -# * `upgrade' -# * `abort-upgrade' -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -#echo "+ lightfield-release.preinst: args are $*" 1>&2 -case "$1" in - install|upgrade|abort-upgrade) - ;; - - *) - echo "preinst called with unknown argument '$1'" 1>&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/debian/lightfield-release.prerm b/debian/lightfield-release.prerm deleted file mode 100644 index b66a61cc..00000000 --- a/debian/lightfield-release.prerm +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh -# prerm script for lightfield-release -# -# see: dh_installdeb(1) -# -#set -e -# -# summary of how this script can be called: -# * `remove' -# * `upgrade' -# * `failed-upgrade' -# * `remove' `in-favour' -# * `deconfigure' `in-favour' -# `removing' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -#echo "+ lightfield-release.prerm: args are $*" 1>&2 -case "$1" in - remove) - systemctl daemon-reload - systemctl disable --now set-projector-power.service clean-up-mount-points.service getty@tty1.service - rm -rf /etc/systemd/system/getty@tty1.service.d - systemctl daemon-reload - ;; - - upgrade|deconfigure|failed-upgrade) - ;; - - *) - echo "prerm called with unknown argument '$1'" 1>&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/debian/lightfield-debug.install b/debian/lightfield.install.in similarity index 100% rename from debian/lightfield-debug.install rename to debian/lightfield.install.in diff --git a/debian/lightfield-debug.postinst b/debian/lightfield.postinst.in similarity index 100% rename from debian/lightfield-debug.postinst rename to debian/lightfield.postinst.in diff --git a/debian/lightfield-debug.postrm b/debian/lightfield.postrm.in similarity index 100% rename from debian/lightfield-debug.postrm rename to debian/lightfield.postrm.in diff --git a/debian/lightfield-debug.preinst b/debian/lightfield.preinst.in similarity index 100% rename from debian/lightfield-debug.preinst rename to debian/lightfield.preinst.in diff --git a/debian/lightfield-debug.prerm b/debian/lightfield.prerm.in similarity index 100% rename from debian/lightfield-debug.prerm rename to debian/lightfield.prerm.in diff --git a/debian/release-control b/debian/release-control deleted file mode 100644 index 703b15e3..00000000 --- a/debian/release-control +++ /dev/null @@ -1,44 +0,0 @@ -Source: lightfield -Section: misc -Priority: optional -Maintainer: LightField packager -Build-Depends: - debhelper (>= 11), - libhidapi-dev (>> 0.8), - python3 (>> 3.6), - qtbase5-dev (>= 5.11.1) -Standards-Version: 4.1.3 -Homepage: https://github.com/VolumetricBio/LightField -Vcs-Browser: https://github.com/VolumetricBio/LightField -Vcs-Git: https://github.com/VolumetricBio/LightField.git - -Package: lightfield-release -Architecture: any -Breaks: lightfield-debug -Conflicts: lightfield-debug -Replaces: lightfield-debug -Depends: - lightfield-common (= ${binary:Version}), - ${shlibs:Depends}, - ${misc:Depends}, - libhidapi-libusb0 (>> 0.8), - libqt5dbus5 (>= 5.11.1), - libqt5network5 (>= 5.11.1), - avrdude (>= 6.3), - gpg (>= 2.2.8), - graphicsmagick (>> 1.3), - slic3r (>= 1.2.9), - sudo (>= 1.8.23) -Recommends: - fonts-montserrat (>= 7.200), - fonts-font-awesome (>= 5.0.10) -Description: Printer software for Volumetric's Lumen X 3D printer - release version - -Package: lightfield-common -Architecture: all -Multi-Arch: foreign -Depends: - ${misc:Depends}, - python3 (>= 3.6), - python3-serial (>= 3.4), -Description: Printer software for Volumetric's Lumen X 3D printer - common files From f59be9127557da51cbce513e9ab961866404e118 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 16 Dec 2019 14:27:41 -0800 Subject: [PATCH 06/89] Temporarily commit WIP release train changes. --- change-version-number.sh | 175 +++++++++++++++++----- debian/control.in | 12 +- debian/lightfield-common.postinst | 2 - debian/lightfield-common.postrm | 4 +- debian/lightfield-common.preinst | 4 +- debian/lightfield.postinst.in | 13 +- debian/lightfield.postrm.in | 12 +- debian/lightfield.preinst.in | 4 +- debian/lightfield.prerm.in | 4 +- install-lightfield.sh | 57 ++++++-- make-deb-package.sh | 105 +++++++++++--- make-upgrade-kit.sh | 231 ++++++++++++++++++------------ src/version.h.in | 20 +-- 13 files changed, 443 insertions(+), 200 deletions(-) diff --git a/change-version-number.sh b/change-version-number.sh index 83b6f909..383861fd 100755 --- a/change-version-number.sh +++ b/change-version-number.sh @@ -13,56 +13,167 @@ function error-trap () { exit 1 } +function usage () { + cat <] [-t ] +Changes the LightField version in all the relevant places in the source. + + -a Sets the architecture. Valid values: amd64 arm7l. + Default: ${DEFAULT_ARCHITECTURE} + -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} +HERE +} + +function apply-atsign-substitution () { + perl -lpi -e "s/@@${1}@@/${2}/g;" $3- +} + +function apply-assignment-substitution () { + perl -lpi -e "s/^(\\s*)${1}=.*$/\$1${1}=${2}/g;" $3- +} + +LIGHTFIELD_ROOT="/home/lumen/Volumetric/LightField" +LIGHTFIELD_SRC="${LIGHTFIELD_ROOT}/src" + +RELEASE_TRAIN=base +ARCHITECTURE=$(uname -m) +VERSION= +[ "${ARCHITECTURE}" = "x86_64" ] && ARCHITECTURE=amd64 + +DEFAULT_RELEASE_TRAIN=${RELEASE_TRAIN} +DEFAULT_ARCHITECTURE=${ARCHITECTURE} + if [ -z "$1" ] then - red-bar 'Usage: change-version-number.sh ' + usage exit 1 fi -VER=( $(IFS="."; echo $1) ) -if [ "${#VER[@]}" -lt 3 ] +if ! getopt -Q -q -n 'change-version-number.sh' -o 'a:t:' -- "$@" +then + usage + return +fi + +ARGS=$(getopt -Q -q -n 'change-version-number.sh' -o 'a:t:' -- "$@") +eval set -- "$ARGS" + +while [ -n "${1}" ] +do + case "${1}" in + '-a') + ARCHITECTURE="${2}" + shift + ;; + + '-t') + RELEASE_TRAIN="${2}" + shift + ;; + + *) + VERSION="${1}" + ;; + esac + shift +done + +if [ -z "${VERSION}" ] then - red-bar 'Too few components to version number -- must be at least three.' + usage exit 1 -elif [ "${#VER[@]}" -eq 3 ] +fi + +# shellcheck disable=SC2207 +VER=( $(IFS="."; echo "${VERSION}") ) +COUNT=${#VER[@]} +if [ "${COUNT}" -lt 3 ] then - VER[3]=0 -elif [ "${#VER[@]}" -gt 4 ] + red-bar 'Too few components in version number -- must be at least three.' + exit 1 +elif [ "${COUNT}" -gt 4 ] then - red-bar 'Too many components to version number -- must be at most four.' + red-bar 'Too many components in version number -- must be at most four.' exit 1 fi +if [ "${COUNT}" -eq 3 ] +then + VER[3]=0 +fi STRINGVER="${VER[0]}.${VER[1]}.${VER[2]}.${VER[3]}" -LIGHTFIELD_ROOT="/home/lumen/Volumetric/LightField" -LIGHTFIELD_SRC="${LIGHTFIELD_ROOT}/src" - +# shellcheck disable=SC2164 cd "${LIGHTFIELD_SRC}" blue-bar 'Generating src/version.h from src/version.h.in' -sed \ - -e s/@@LIGHTFIELD_VERSION_STRING@@/${STRINGVER}/g \ - -e s/@@LIGHTFIELD_VERSION_MAJOR@@/${VER[0]}/g \ - -e s/@@LIGHTFIELD_VERSION_MINOR@@/${VER[1]}/g \ - -e s/@@LIGHTFIELD_VERSION_TEENY@@/${VER[2]}/g \ - -e s/@@LIGHTFIELD_VERSION_BUILD@@/${VER[3]}/g \ - < "version.h.in" \ - > "version.h" +sed \ + -e "s/@@VERSION_STRING@@/${STRINGVER}/g" \ + -e "s/@@VERSION_MAJOR@@/${VER[0]}/g" \ + -e "s/@@VERSION_MINOR@@/${VER[1]}/g" \ + -e "s/@@VERSION_TEENY@@/${VER[2]}/g" \ + -e "s/@@VERSION_BUILD@@/${VER[3]}/g" \ + -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g" \ + < version.h.in \ + > version.h +# shellcheck disable=SC2164 cd "${LIGHTFIELD_ROOT}" +if [ -z "${RELEASE_TRAIN}" ] +then + SUFFIX=-${BUILDTYPE} +else + SUFFIX=-${RELEASE_TRAIN}-${BUILDTYPE} +fi + +if [ "${BUILDTYPE}" = release ] +then + ANTIBUILDTYPE=debug +else + ANTIBUILDTYPE=release +fi + +cp ${VERBOSE} debian/control.in debian/control +for a in install preinst postinst prerm postrm +do + cp ${VERBOSE} "debian/lightfield.${a}.in" "debian/lightfield-${SUFFIX}.${a}" +done + blue-bar 'Updating build and packaging scripts' -perl \ - -lpi \ - -e "s/^\s*VERSION=\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?\s*$/VERSION=${STRINGVER}/g;" \ - install-lightfield.sh \ - make-deb-package.sh \ - make-upgrade-kit.sh \ - unpack-kit-manually.sh - -perl \ - -lpi \ - -e "s/lightfield-(debug|release)_\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?_amd64/lightfield-\$1_${STRINGVER}_amd64/g;" \ - debian/lightfield-debug.postinst \ - debian/lightfield-release.postinst + +perl \ + -lpi \ + -e "s/^\\s*ANTIBUILDTYPE=.*$/ANTIBUILDTYPE=\"${ANTIBUILDTYPE}\"/g;" \ + -e "s/@@ARCHITECTURE@@/${ARCHITECTURE}/g;" \ + -e "s/@@BUILDTYPE@@/${BUILDTYPE}/g;" \ + -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g;" \ + -e "s/@@VERSION@@/${STRINGVER}/g;" \ + -e "s/(?:amd64|arm7l)/${ARCHITECTURE}/g;" \ + install-lightfield.sh \ + make-deb-package.sh \ + make-upgrade-kit.sh \ + unpack-kit-manually.sh \ + +perl \ + -lpi \ + -e "s/@@ANTIBUILDTYPE@@/${ANTIBUILDTYPE}/g;" \ + -e "s/@@ARCHITECTURE@@/${ARCHITECTURE}/g;" \ + -e "s/@@BUILDTYPE@@/${BUILDTYPE}/g;" \ + -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g;" \ + -e "s/@@VERSION@@/${STRINGVER}/g;" \ + -e "s/(?:amd64|arm7l)/${ARCHITECTURE}/g;" \ + "debian/lightfield-${SUFFIX}.install" \ + "debian/lightfield-${SUFFIX}.preinst" \ + "debian/lightfield-${SUFFIX}.postinst" \ + "debian/lightfield-${SUFFIX}.prerm" \ + "debian/lightfield-${SUFFIX}.postrm" + +perl \ + -lpi \ + -e "s/@@ARCHITECTURE@@/ARCHITECTURE=\"${ARCHITECTURE}\"/g;" \ + -e "s/@@BUILDTYPE@@/BUILDTYPE=\"${BUILDTYPE}\"/g;" \ + -e "s/@@RELEASE_TRAIN@@/RELEASE_TRAIN=\"${RELEASE_TRAIN}\"/g;" \ + -e "s/@@VERSION@@/VERSION=${STRINGVER}/g;" \ + -e "s/(?:amd64|arm7l)/${ARCHITECTURE}/g;" \ + debian/control diff --git a/debian/control.in b/debian/control.in index c15345e8..f0c1bb26 100644 --- a/debian/control.in +++ b/debian/control.in @@ -12,13 +12,13 @@ Homepage: https://github.com/VolumetricBio/LightField Vcs-Browser: https://github.com/VolumetricBio/LightField Vcs-Git: https://github.com/VolumetricBio/LightField.git -Package: lightfield-debug +Package: lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ Architecture: any -Breaks: lightfield-release -Conflicts: lightfield-release -Replaces: lightfield-release +Breaks: lightfield@@RELEASE_TRAIN@@-@@ANTIBUILDTYPE@@ +Conflicts: lightfield@@RELEASE_TRAIN@@-@@ANTIBUILDTYPE@@ +Replaces: lightfield@@RELEASE_TRAIN@@-@@ANTIBUILDTYPE@@ Depends: - lightfield-common (= ${binary:Version}), + lightfield@@RELEASE_TRAIN@@-common (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, libhidapi-libusb0 (>> 0.8), @@ -34,7 +34,7 @@ Recommends: fonts-font-awesome (>= 5.0.10) Description: Printer software for Volumetric's Lumen X 3D printer - debug version -Package: lightfield-common +Package: lightfield@@RELEASE_TRAIN@@-common Architecture: all Multi-Arch: foreign Depends: diff --git a/debian/lightfield-common.postinst b/debian/lightfield-common.postinst index 0498545a..dc60412f 100644 --- a/debian/lightfield-common.postinst +++ b/debian/lightfield-common.postinst @@ -17,8 +17,6 @@ # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package - -#echo "+ lightfield-common.postinst: args are $*" 1>&2 case "$1" in configure) mkdir -p \ diff --git a/debian/lightfield-common.postrm b/debian/lightfield-common.postrm index 789a65a4..e99aaf3e 100644 --- a/debian/lightfield-common.postrm +++ b/debian/lightfield-common.postrm @@ -1,5 +1,5 @@ #!/bin/sh -# postrm script for lightfield-debug +# postrm script for lightfield-common # # see: dh_installdeb(1) # @@ -18,8 +18,6 @@ # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package - -#echo "+ lightfield-debug.postrm: args are $*" 1>&2 case "$1" in remove) rm /etc/apt/sources.list.d/volumetric-lightfield.list 2>/dev/null diff --git a/debian/lightfield-common.preinst b/debian/lightfield-common.preinst index 43641611..7c118901 100644 --- a/debian/lightfield-common.preinst +++ b/debian/lightfield-common.preinst @@ -1,5 +1,5 @@ #!/bin/sh -# preinst script for lightfield-debug +# preinst script for lightfield-common # # see: dh_installdeb(1) # @@ -13,8 +13,6 @@ # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package - -#echo "+ lightfield-debug.preinst: args are $*" 1>&2 case "$1" in install) rm /etc/sudoers.d/lumen-lightfield 2>/dev/null diff --git a/debian/lightfield.postinst.in b/debian/lightfield.postinst.in index 00da6d2d..0a2a4906 100644 --- a/debian/lightfield.postinst.in +++ b/debian/lightfield.postinst.in @@ -1,5 +1,5 @@ #!/bin/sh -# postinst script for lightfield-debug +# postinst script for lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ # # see: dh_installdeb(1) # @@ -17,13 +17,18 @@ # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package +RELEASE_TRAIN=-@@RELEASE_TRAIN@@ +BUILD=@@BUILD@@ +VERSION=@@VERSION@@ +ARCHITECTURE=@@ARCHITECTURE@@ + +[ "${RELEASE_TRAIN}" = "-base" ] && RELEASE_TRAIN= -#echo "+ lightfield-debug.postinst: args are $*" 1>&2 case "$1" in configure) - perl -lp -i -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null + perl -lpi -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null - echo "deb file:/var/lib/lightfield/software-updates/lightfield-debug_1.0.10.0_amd64 ./" > /etc/apt/sources.list.d/volumetric-lightfield.list + echo "deb file:/var/lib/lightfield/software-updates/lightfield${RELEASE_TRAIN}-${BUILD}_${VERSION}_${ARCHITECTURE} ./" > /etc/apt/sources.list.d/volumetric-lightfield.list chown lumen:lumen /etc/apt/sources.list.d/volumetric-lightfield.list chmod 644 /etc/apt/sources.list.d/volumetric-lightfield.list diff --git a/debian/lightfield.postrm.in b/debian/lightfield.postrm.in index 8765353e..7a249c17 100644 --- a/debian/lightfield.postrm.in +++ b/debian/lightfield.postrm.in @@ -1,5 +1,5 @@ #!/bin/sh -# postrm script for lightfield-debug +# postrm script for lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ # # see: dh_installdeb(1) # @@ -18,19 +18,13 @@ # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package - -#echo "+ lightfield-debug.postrm: args are $*" 1>&2 case "$1" in - remove) - echo + Setting default back to graphical.target + remove|purge) systemctl set-default graphical.target - echo + Getting default - systemctl get-default - echo + Starting tty1 systemctl start getty@tty1 ;; - purge|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) ;; *) diff --git a/debian/lightfield.preinst.in b/debian/lightfield.preinst.in index 78bc4b08..75cc6213 100644 --- a/debian/lightfield.preinst.in +++ b/debian/lightfield.preinst.in @@ -1,5 +1,5 @@ #!/bin/sh -# preinst script for lightfield-debug +# preinst script for lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ # # see: dh_installdeb(1) # @@ -13,8 +13,6 @@ # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package - -#echo "+ lightfield-debug.preinst: args are $*" 1>&2 case "$1" in install|upgrade|abort-upgrade) ;; diff --git a/debian/lightfield.prerm.in b/debian/lightfield.prerm.in index ce68d423..538f0ad6 100644 --- a/debian/lightfield.prerm.in +++ b/debian/lightfield.prerm.in @@ -1,5 +1,5 @@ #!/bin/sh -# prerm script for lightfield-debug +# prerm script for lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ # # see: dh_installdeb(1) # @@ -16,8 +16,6 @@ # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package - -#echo "+ lightfield-debug.prerm: args are $*" 1>&2 case "$1" in remove) systemctl daemon-reload diff --git a/install-lightfield.sh b/install-lightfield.sh index 5ce1d738..51066d08 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -8,11 +8,7 @@ VERSION=1.0.10.0 ## ## ######################################################### -if [ "${UID}" != "0" ] -then - echo This script must be run as root. - exit 1 -fi +[ "${UID}" != "0" ] && exec sudo "${0}" "${@}" function clear () { echo -ne "\x1B[0m\x1B[H\x1B[J\x1B[3J" @@ -33,9 +29,12 @@ function error-trap () { function usage () { cat <] [-t ] +Where: -q Build quietly. + -x Force rebuild all. + -a Sets the architecture. Valid values: amd64 arm7l. + Default: ${DEFAULT_ARCHITECTURE} + -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} EOF } @@ -52,19 +51,45 @@ CHXXXVERBOSE=-c FORCEREBUILD= BUILDQUIETLY= +RELEASE_TRAIN=base +ARCHITECTURE=$(uname -m) +[ "${ARCHITECTURE}" = "x86_64" ] && ARCHITECTURE=amd64 + +DEFAULT_RELEASE_TRAIN=${RELEASE_TRAIN} +DEFAULT_ARCHITECTURE=${ARCHITECTURE} + +if ! getopt -Q -q -n 'install-lightfield.sh' -o 'qxa:t:' -- "$@" +then + usage + return +fi + +ARGS=$(getopt -Q -q -n 'install-lightfield.sh' -o 'qxa:t:' -- "$@") +eval set -- "$ARGS" + while [ -n "$1" ] do case "$1" in - "-q") + '-q') VERBOSE= CHXXXVERBOSE= BUILDQUIETLY=-q ;; - "-x") + '-x') FORCEREBUILD=-x ;; + '-a') + ARCHITECTURE="${2}" + shift + ;; + + '-t') + RELEASE_TRAIN="${2}" + shift + ;; + *) usage exit 1 @@ -135,8 +160,16 @@ install ${VERBOSE} -DT -m 644 Util/constants.py blue-bar • Configuring system -perl -lp -i -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null -echo "deb file:/var/lib/lightfield/software-updates/lightfield-debug_${VERSION}_amd64 ./" > /etc/apt/sources.list.d/volumetric-lightfield.list +perl -lpi -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null + +if [ "${RELEASE_TRAIN}" = "base" ] +then + RELEASE_TRAIN= +else + RELEASE_TRAIN=-${RELEASE_TRAIN} +fi + +echo "deb file:/var/lib/lightfield/software-updates/lightfield${RELEASE_TRAIN}-debug_${VERSION}_${ARCHITECTURE} ./" > /etc/apt/sources.list.d/volumetric-lightfield.list chown ${CHXXXVERBOSE} lumen:lumen /etc/apt/sources.list.d/volumetric-lightfield.list systemctl daemon-reload diff --git a/make-deb-package.sh b/make-deb-package.sh index 120e2297..22bf09f6 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -1,6 +1,9 @@ #!/bin/bash +ARCHITECTURE=amd64 +RELEASE_TRAIN=base VERSION=1.0.10.0 + PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging ######################################################### @@ -18,19 +21,22 @@ function red-bar () { } function error-trap () { - red-bar Failed\! + red-bar "Failed!" exit 1 } function usage () { cat < Sets the architecture. Valid values: amd64 arm7l. + Default: ${DEFAULT_ARCHITECTURE} + -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} + BUILDTYPE is one of + release create a release-build kit + debug create a debug-build kit + both create both kits If the build is successful, the requested package set(s) will be found in ${DEB_BUILD_DIR}/ @@ -44,7 +50,7 @@ PRINTRUN_SRC=/home/lumen/Volumetric/printrun LIGHTFIELD_SRC=/home/lumen/Volumetric/LightField MOUNTMON_SRC="${LIGHTFIELD_SRC}/mountmon" USBDRIVER_SRC="${LIGHTFIELD_SRC}/usb-driver" -PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${VERSION}" +PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" @@ -52,7 +58,20 @@ LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" VERBOSE=-v CHXXXVERBOSE=-c FORCEREBUILD=-x -BUILDTYPE= + +BUILDTYPE=debug + +DEFAULT_ARCHITECTURE=${ARCHITECTURE} +DEFAULT_RELEASE_TRAIN=${RELEASE_TRAIN} + +if ! getopt -Q -q -n 'make-deb-package.sh' -o 'qXa:t:' -- "$@" +then + usage + exit 1 +fi + +ARGS=$(getopt -Q -q -n 'make-deb-package.sh' -o 'qXa:t:' -- "$@") +eval set -- "$ARGS" while [ -n "$1" ] do @@ -66,8 +85,25 @@ do FORCEREBUILD= ;; + "-a") + if [ "${2}" = "amd64" ] || [ "${2}" = "arm7l" ] + then + ARCHITECTURE="${2}" + shift + else + usage + exit 1 + fi + ;; + + "-t") + RELEASE_TRAIN="${2}" + shift + ;; + "release" | "debug" | "both") - BUILDTYPE="$1" + BUILDTYPE="${1}" + break ;; *) @@ -84,17 +120,23 @@ then exit 1 fi +if [ "${RELEASE_TRAIN}" = "base" ] +then + SUFFIX=-${BUILDTYPE} +else + SUFFIX=-${RELEASE_TRAIN}-${BUILDTYPE} +fi + if [ "${BUILDTYPE}" = "both" ] then - ARG=$([ -z ${VERBOSE} ] && echo -q) - $0 "${ARG}" release || exit $? - $0 "${ARG}" debug || exit $? + "$0" "${ARGS}" release || exit $? + "$0" "${ARGS}" debug || exit $? exit 0 fi ################################################## -blue-bar • Setting up build environment +blue-bar "• Setting up build environment" [ -d "${PACKAGE_BUILD_ROOT}" ] || mkdir ${VERBOSE} -p "${PACKAGE_BUILD_ROOT}" [ -h "${PACKAGE_BUILD_ROOT}/latest" ] && rm ${VERBOSE} "${PACKAGE_BUILD_ROOT}/latest" @@ -112,7 +154,7 @@ cd "${USBDRIVER_SRC}" ################################################## -blue-bar • Building "${BUILDTYPE}" version of set-projector-power +blue-bar "• Building ${BUILDTYPE} version of set-projector-power" if [ "${BUILDTYPE}" = "debug" ] then @@ -121,7 +163,7 @@ elif [ "${BUILDTYPE}" = "release" ] then OPTS="-s -O3 -DNDEBUG" fi -g++ -o "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" ${OPTS} -pipe -std=gnu++1z -Wall -W -D_GNU_SOURCE -fPIC dlpc350_usb.cpp dlpc350_api.cpp main.cpp -lhidapi-libusb +g++ -o "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" "${OPTS}" -pipe -std=gnu++1z -Wall -W -D_GNU_SOURCE -fPIC dlpc350_usb.cpp dlpc350_api.cpp main.cpp -lhidapi-libusb ################################################## @@ -129,7 +171,7 @@ cd "${MOUNTMON_SRC}" ################################################## -blue-bar • Building "${BUILDTYPE}" version of Mountmon +blue-bar "• Building ${BUILDTYPE} version of Mountmon" if [ "${BUILDTYPE}" = "debug" ] then @@ -147,7 +189,7 @@ cd "${LIGHTFIELD_SRC}" ################################################## -blue-bar • Building "${BUILDTYPE}" version of LightField +blue-bar "• Building ${BUILDTYPE} version of LightField" if [ "${BUILDTYPE}" = "debug" ] then @@ -162,7 +204,7 @@ install ${VERBOSE} -DT -m 755 build/lf "${LIGHTFIELD_FILES}/usr/bin/lf" ################################################## -blue-bar • Copying files into packaging directory +blue-bar "• Copying LightField files into packaging directory" install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock "${LIGHTFIELD_FILES}/etc/apt/apt.conf.d/99untrustworthy-clock" install ${VERBOSE} -DT -m 644 gpg/new-pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" @@ -189,7 +231,7 @@ cd "${PRINTRUN_SRC}" ################################################## -blue-bar • Copying printrun files into packaging directory +blue-bar "• Copying printrun files into packaging directory" install ${VERBOSE} -DT -m 644 printrun/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/__init__.py" install ${VERBOSE} -DT -m 644 printrun/eventhandler.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/eventhandler.py" @@ -205,14 +247,31 @@ cd "${LIGHTFIELD_PACKAGE}" ################################################## -blue-bar • Building Debian packages +blue-bar "• Building Debian packages" + +## TODO TODO TODO TODO TODO +## TODO need to embed the release train name into the stuff in debian/ +## TODO TODO TODO TODO TODO + +if [ "${BUILDTYPE}" = debug ] +then + ANTIBUILDTYPE=release +else + ANTIBUILDTYPE=debug +fi + +sed \ + -e "s/@@BUILDTYPE@@/${BUILDTYPE}/g" \ + -e "s/@@ANTIBUILDTYPE@@/${ANTIBUILDTYPE}/g" \ + -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g" \ + < debian/control.in \ + > debian/control -cp -v "debian/${BUILDTYPE}-control" debian/control dpkg-buildpackage --build=any,all --no-sign ################################################## -blue-bar • Cleaning up +blue-bar "• Cleaning up" cd .. diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index e7862166..aee3a9be 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -1,6 +1,9 @@ #!/bin/bash +ARCHITECTURE=amd64 +RELEASE_TRAIN=base VERSION=1.0.10.0 + PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging USE_KEY_SET=current @@ -25,29 +28,24 @@ function error-trap () { function usage () { cat < Sets the architecture. Valid values: amd64 arm7l. + Default: ${DEFAULT_ARCHITECTURE} + -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} + BUILDTYPE is one of + release create a release-build kit + debug create a debug-build kit + both create both kits If the build is successful, the requested upgrade kit(s) will be found in - ${KIT_DIR}/lightfield-BUILDTYPE_${VERSION}_amd64.kit + ${KIT_DIR}/lightfield$([ "${RELEASE_TRAIN}" = "base" ] || echo "-${RELEASE_TRAIN}" )-BUILDTYPE_${VERSION}_${ARCHITECTURE}.kit EOF } trap error-trap ERR set -e -LIGHTFIELD_SRC="/home/lumen/Volumetric/LightField" -PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${VERSION}" -DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" -LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" -LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" - -KIT_DIR="${PACKAGE_BUILD_DIR}/kit" - if [ "${USE_KEY_SET}" = "current" ]; then #REPO_KEY_ID=18DDFE4E607507208C9F6E6582768C36BD8725D2 #PKG_KEY_ID=0EF6486549978C0C76B49E99C9FC781B66B69981 @@ -61,8 +59,23 @@ else fi VERBOSE=-v + +ARCHITECTURE=$(uname -m) +[ "${ARCHITECTURE}" = "x86_64" ] && ARCHITECTURE=amd64 BUILDTYPE= +DEFAULT_ARCHITECTURE=${ARCHITECTURE} +DEFAULT_RELEASE_TRAIN=${RELEASE_TRAIN} + +if ! getopt -Q -q -n 'make-upgrade-kit.sh' -o 'qa:t:' -- "$@" +then + usage + exit 1 +fi + +ARGS=$(getopt -Q -q -n 'make-upgrade-kit.sh' -o 'qa:t:' -- "$@") +eval set -- "$ARGS" + while [ -n "$1" ] do case "$1" in @@ -70,8 +83,25 @@ do VERBOSE= ;; + "-a") + if [ "${2}" = "amd64" ] || [ "${2}" = "arm7l" ] + then + ARCHITECTURE="${2}" + shift + else + usage + exit 1 + fi + ;; + + "-t") + RELEASE_TRAIN="${2}" + shift + ;; + "release" | "debug" | "both") - BUILDTYPE=$1 + BUILDTYPE="${1}" + break ;; *) @@ -88,24 +118,36 @@ then exit 1 fi +if [ "${RELEASE_TRAIN}" = "base" ] +then + SUFFIX=${BUILDTYPE} +else + SUFFIX=${RELEASE_TRAIN}-${BUILDTYPE} +fi + +LIGHTFIELD_SRC="/home/lumen/Volumetric/LightField" +PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" +DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" +#LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" +#LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" + +KIT_DIR="${PACKAGE_BUILD_DIR}/kit" + if [ "${BUILDTYPE}" = "both" ] then - ARG=$(if [ -z "${VERBOSE}" ]; then echo -q; fi) - $0 ${ARG} release || exit $? - $0 ${ARG} debug || exit $? + "${0}" "${ARGS}" release || exit $? + "${0}" "${ARGS}" debug || exit $? exit 0 fi REPO_DIR="${PACKAGE_BUILD_DIR}/repo" -DISTRIBUTION=cosmic - RELEASEDATE=$(date "+%Y-%m-%d") cd "${PACKAGE_BUILD_DIR}" -blue-bar • Creating LightField "${VERSION}" "${BUILDTYPE}"-build update kit +blue-bar "• Creating LightField ${VERSION} ${RELEASE_TRAIN} ${BUILDTYPE}-build update kit" -[ -d "${REPO_DIR}" ] && rm ${VERBOSE} -rf "${REPO_DIR}" +[ -d "${REPO_DIR}" ] && rm ${VERBOSE} -rf "${REPO_DIR}" mkdir ${VERBOSE} -p "${REPO_DIR}" mkdir ${VERBOSE} -p "${KIT_DIR}" @@ -115,22 +157,22 @@ install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-common if [ "${BUILDTYPE}" = "release" ] then - install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-release_${VERSION}_amd64.deb" + install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.deb" elif [ "${BUILDTYPE}" = "debug" ] then - install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-debug_${VERSION}_amd64.deb" - if [ -f "${DEB_BUILD_DIR}/lightfield-debug-dbgsym_${VERSION}_amd64.deb" ] + install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.deb" + if [ -f "${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.deb" ] then - install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-debug-dbgsym_${VERSION}_amd64.deb" - elif [ -f "${DEB_BUILD_DIR}/lightfield-debug-dbgsym_${VERSION}_amd64.ddeb" ] + install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.deb" + elif [ -f "${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.ddeb" ] then - install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-debug-dbgsym_${VERSION}_amd64.ddeb" + install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.ddeb" else - red-bar "!!! Unable to find either" - red-bar "!!! ${DEB_BUILD_DIR}/lightfield-debug-dbgsym_${VERSION}_amd64.deb" - red-bar "!!! or" - red-bar "!!! ${DEB_BUILD_DIR}/lightfield-debug-dbgsym_${VERSION}_amd64.ddeb" - red-bar "!!! Build failed!" + red-bar "!!! Unable to find either" + red-bar "!!! ${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.deb" + red-bar "!!! or" + red-bar "!!! ${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.ddeb" + red-bar "!!! Build failed!" fi fi @@ -140,37 +182,43 @@ dpkg-scanpackages . | tee Packages | xz -ceT0 > Packages.xz apt-ftparchive --config-file ${LIGHTFIELD_SRC}/apt-files/release.conf release . | tee Release | xz -ceT0 > Release.xz -gpg \ - ${VERBOSE} \ - --batch \ - --armor \ - --local-user "${REPO_KEY_ID}" \ - --output InRelease \ - --clearsign \ +gpg \ + ${VERBOSE} \ + --batch \ + --armor \ + --local-user "${REPO_KEY_ID}" \ + --output InRelease \ + --clearsign \ Release -gpg \ - ${VERBOSE} \ - --batch \ - --armor \ - --local-user "${REPO_KEY_ID}" \ - --output Release.gpg \ - --detach-sign \ +gpg \ + ${VERBOSE} \ + --batch \ + --armor \ + --local-user "${REPO_KEY_ID}" \ + --output Release.gpg \ + --detach-sign \ Release -rm ${VERBOSE} -f \ - version.inf \ +rm ${VERBOSE} -f \ + version.inf \ version.inf.sig -sha256sum -b * | sed -r -e 's/^/ /' -e 's/ +\*/ /' > .hashes +sha256sum -- -b * | sed -r -e 's/^/ /' -e 's/ +\*/ /' > .hashes ( - sed -e "s/@@VERSION@@/${VERSION}/g" -e "s/@@BUILDTYPE@@/${BUILDTYPE}/g" -e "s/@@RELEASEDATE@@/${RELEASEDATE}/g" "${LIGHTFIELD_SRC}/apt-files/version.inf.in" + sed \ + -e "s/@@ARCHITECTURE@@/${ARCHITECTURE}/g" \ + -e "s/@@BUILDTYPE@@/${BUILDTYPE}/g" \ + -e "s/@@RELEASEDATE@@/${RELEASEDATE}/g" \ + -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g" \ + -e "s/@@VERSION@@/${VERSION}/g" \ + "${LIGHTFIELD_SRC}/apt-files/version.inf.in" # extract description from ${LIGHTFIELD_SRC}/debian/changelog linecount=$(grep -n '^ -- LightField packager' ${LIGHTFIELD_SRC}/debian/changelog | head -1 | cut -d: -f1 || echo 0) - if [ -z "${linecount}" -o \( "${linecount}" -lt 1 \) ] + if [ -z "${linecount}" ] || [ "${linecount}" -lt 1 ] then - red-bar " *** Can't find end of first change in ${LIGHTFIELD_SRC}/debian/changelog, aborting" + red-bar "!!! Can't find end of first change in ${LIGHTFIELD_SRC}/debian/changelog, aborting" exit 1 fi head -$((linecount - 2)) ${LIGHTFIELD_SRC}/debian/changelog | tail +3 | perl -lpe 's/\s+$//; s/^$/./; s/^/ /' @@ -180,53 +228,54 @@ sha256sum -b * | sed -r -e 's/^/ /' -e 's/ +\*/ /' > .hashes ) > version.inf rm .hashes -gpg \ - ${VERBOSE} \ - --batch \ - --armor \ - --local-user "${PKG_KEY_ID}" \ - --output version.inf.sig \ - --detach-sign \ +gpg \ + ${VERBOSE} \ + --batch \ + --armor \ + --local-user "${PKG_KEY_ID}" \ + --output version.inf.sig \ + --detach-sign \ version.inf -rm \ - ${VERBOSE} \ - -f \ - "${KIT_DIR}/lightfield-${BUILDTYPE}_${VERSION}_amd64.kit" \ - "${KIT_DIR}/lightfield-${BUILDTYPE}_${VERSION}_amd64.kit.sig" \ - "${KIT_DIR}/lightfield-${BUILDTYPE}_${VERSION}_amd64.kit.zip" - -tar \ - ${VERBOSE} ${VERBOSE} \ - -c \ - -f "${KIT_DIR}/lightfield-${BUILDTYPE}_${VERSION}_amd64.kit" \ - --owner=root \ - --group=root \ - --sort=name \ +rm \ + ${VERBOSE} \ + -f \ + "${KIT_DIR}/lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit" \ + "${KIT_DIR}/lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit.sig" \ + "${KIT_DIR}/lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit.zip" + +tar \ + ${VERBOSE} ${VERBOSE} \ + -c \ + -f "${KIT_DIR}/lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit" \ + --owner=root \ + --group=root \ + --sort=name \ + -- \ * -cd ${KIT_DIR} +cd "${KIT_DIR}" -gpg \ - ${VERBOSE} \ - --batch \ - --armor \ - --local-user "${PKG_KEY_ID}" \ - --output "lightfield-${BUILDTYPE}_${VERSION}_amd64.kit.sig" \ - --detach-sign \ - "lightfield-${BUILDTYPE}_${VERSION}_amd64.kit" +gpg \ + ${VERBOSE} \ + --batch \ + --armor \ + --local-user "${PKG_KEY_ID}" \ + --output "lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit.sig" \ + --detach-sign \ + "lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit" -zip \ - -0joq \ - "lightfield-${BUILDTYPE}_${VERSION}_amd64.kit.zip" \ - "lightfield-${BUILDTYPE}_${VERSION}_amd64.kit" \ - "lightfield-${BUILDTYPE}_${VERSION}_amd64.kit.sig" +zip \ + -0joq \ + "lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit.zip" \ + "lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit" \ + "lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE}.kit.sig" -blue-bar • Cleaning up +blue-bar "• Cleaning up" cd .. -rm ${VERBOSE} -rf ${REPO_DIR} +rm ${VERBOSE} -rf "${REPO_DIR}" blue-bar "" blue-bar "• Done!" diff --git a/src/version.h.in b/src/version.h.in index ef3d79a0..66e0ca11 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -18,19 +18,21 @@ inline constexpr void DecodeVersionCode( unsigned const versionCode, int& major, build = static_cast( versionCode & 0xFFu ); } -char const* LIGHTFIELD_VERSION_STRING __attribute__(( weak )) = "@@LIGHTFIELD_VERSION_STRING@@"; -unsigned const LIGHTFIELD_VERSION_MAJOR = @@LIGHTFIELD_VERSION_MAJOR@@; -unsigned const LIGHTFIELD_VERSION_MINOR = @@LIGHTFIELD_VERSION_MINOR@@; -unsigned const LIGHTFIELD_VERSION_TEENY = @@LIGHTFIELD_VERSION_TEENY@@; -unsigned const LIGHTFIELD_VERSION_BUILD = @@LIGHTFIELD_VERSION_BUILD@@; -unsigned const LIGHTFIELD_VERSION_CODE = MakeVersionCode( LIGHTFIELD_VERSION_MAJOR, LIGHTFIELD_VERSION_MINOR, LIGHTFIELD_VERSION_TEENY, LIGHTFIELD_VERSION_BUILD ); +char const* LIGHTFIELD_VERSION_STRING __attribute__(( weak )) = "@@VERSION_STRING@@"; +unsigned const LIGHTFIELD_VERSION_MAJOR = @@VERSION_MAJOR@@; +unsigned const LIGHTFIELD_VERSION_MINOR = @@VERSION_MINOR@@; +unsigned const LIGHTFIELD_VERSION_TEENY = @@VERSION_TEENY@@; +unsigned const LIGHTFIELD_VERSION_BUILD = @@VERSION_BUILD@@; +unsigned const LIGHTFIELD_VERSION_CODE = MakeVersionCode( LIGHTFIELD_VERSION_MAJOR, LIGHTFIELD_VERSION_MINOR, LIGHTFIELD_VERSION_TEENY, LIGHTFIELD_VERSION_BUILD ); #if defined _DEBUG -BuildType const LIGHTFIELD_VERSION_BUILD_TYPE = BuildType::Debug; -#elif defined NDEBUG -BuildType const LIGHTFIELD_VERSION_BUILD_TYPE = BuildType::Release; +BuildType const LIGHTFIELD_VERSION_BUILD_TYPE = BuildType::Debug; +#elif defined NDEBUG +BuildType const LIGHTFIELD_VERSION_BUILD_TYPE = BuildType::Release; #else # error Unknown build type: Neither _DEBUG nor NDEBUG are #define:d. #endif +char const* LIGHTFIELD_VERSION_RELEASE_TRAIN __attribute__(( weak )) = "@@RELEASE_TRAIN@@"; + #endif // __VERSION_H__ From 26d7c9cc4f6a4519756237cddf067b500cca2348 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 16 Dec 2019 14:59:10 -0800 Subject: [PATCH 07/89] Fix value of PrinterDefaultHighSpeed after raising it the other day during testing. --- src/constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants.h b/src/constants.h index f2ec1d68..54455476 100644 --- a/src/constants.h +++ b/src/constants.h @@ -50,7 +50,7 @@ double const PrinterMaximumZ = 50.00; // mm double const PrinterRaiseToMaximumZ = 60.00; // mm double const PrinterHighSpeedThresholdZ = 10.00; // mm -double const PrinterDefaultHighSpeed = 600.00; // mm/min +double const PrinterDefaultHighSpeed = 200.00; // mm/min double const PrinterDefaultLowSpeed = 50.00; // mm/min double const AspectRatio5to3 = 5.0 / 3.0; From 6a32ecd4e323a5dc310d05b6e845759ef1db0155 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Wed, 18 Dec 2019 12:20:29 -0800 Subject: [PATCH 08/89] CMakeLists.txt: Update Qt path again. --- CMakeLists.txt | 2 +- mountmon/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index df071011..2d6b0cc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set (PROJECT_VERSION "${LF_VERSION_MAJOR}.${LF_VERSION_MINOR}.${LF_VERSION_PATCH if(WIN32) # Set the Qt5 path -set(Qt5_DIR "F:\\\\Qt\\\\5.13.2\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") +set(Qt5_DIR "F:\\\\Qt\\\\5.14.0\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") endif(WIN32) project(lf) diff --git a/mountmon/CMakeLists.txt b/mountmon/CMakeLists.txt index 5f071dbb..69ae720c 100644 --- a/mountmon/CMakeLists.txt +++ b/mountmon/CMakeLists.txt @@ -13,7 +13,7 @@ set(MOUNTMON_VERSION_PATCH "0") set(PROJECT_VERSION "${MOUNTMON_VERSION_MAJOR}.${MOUNTMON_VERSION_MINOR}.${MOUNTMON_VERSION_PATCH}") if(WIN32) -set (Qt5_DIR "F:\\\\Qt\\\\5.13.1\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") +set (Qt5_DIR "F:\\\\Qt\\\\5.14.0\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") endif(WIN32) project(mountmon) From 976d379a85bbe262bce267aa3b44ec19eae63a07 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 27 Dec 2019 14:54:10 -0800 Subject: [PATCH 09/89] Temporary commit --- change-version-number.sh | 170 ++++++++++++++-------------------- debian/control.in | 8 +- debian/lightfield.postinst.in | 14 ++- install-lightfield.sh | 40 ++++---- make-deb-package.sh | 142 +++++++++++++++------------- make-upgrade-kit.sh | 41 ++------ 6 files changed, 196 insertions(+), 219 deletions(-) diff --git a/change-version-number.sh b/change-version-number.sh index 383861fd..7b37497c 100755 --- a/change-version-number.sh +++ b/change-version-number.sh @@ -1,179 +1,153 @@ #!/bin/bash function blue-bar () { - echo -e "\r\x1B[1;37;44m$*\x1B[K\x1B[0m" 1>&2 + echo -e "\r\e[1;37;44m$*\e[K\e[0m" 1>&2 } function red-bar () { - echo -e "\r\x1B[1;33;41m$*\x1B[K\x1B[0m" 1>&2 + echo -e "\r\e[1;33;41m$*\e[K\e[0m" 1>&2 } function error-trap () { - red-bar Failed\! + red-bar 'Failed!' exit 1 } function usage () { - cat <] [-t ] + cat 1>&2 <] Changes the LightField version in all the relevant places in the source. - -a Sets the architecture. Valid values: amd64 arm7l. - Default: ${DEFAULT_ARCHITECTURE} -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} HERE + exit 1 } function apply-atsign-substitution () { - perl -lpi -e "s/@@${1}@@/${2}/g;" $3- + local CODE='\e[0;30;48;2;146;208;80m»' + local RST='«\e[0m' + # shellcheck disable=SC2145 + echo "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" + perl -ilp -e "s/\\@\\@${1}\\@\\@/${2}/g;" "${@:3}" } function apply-assignment-substitution () { - perl -lpi -e "s/^(\\s*)${1}=.*$/\$1${1}=${2}/g;" $3- + local CODE='\e[0;30;48;2;0;146;208m»' + local RST='«\e[0m' + # shellcheck disable=SC2145 + echo "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" + perl -ilp -e "s/^(\\s*)${1}=.*\$/\$1${1}=${2}/g;" "${@:3}" } LIGHTFIELD_ROOT="/home/lumen/Volumetric/LightField" LIGHTFIELD_SRC="${LIGHTFIELD_ROOT}/src" -RELEASE_TRAIN=base +DEFAULT_RELEASE_TRAIN=base + ARCHITECTURE=$(uname -m) +RELEASE_TRAIN=${DEFAULT_RELEASE_TRAIN} VERSION= -[ "${ARCHITECTURE}" = "x86_64" ] && ARCHITECTURE=amd64 - -DEFAULT_RELEASE_TRAIN=${RELEASE_TRAIN} -DEFAULT_ARCHITECTURE=${ARCHITECTURE} -if [ -z "$1" ] +if [ "${ARCHITECTURE}" = "x86_64" ] then - usage - exit 1 + ARCHITECTURE=amd64 fi -if ! getopt -Q -q -n 'change-version-number.sh' -o 'a:t:' -- "$@" +################################################# + +ARGS=$(getopt -n 'change-version-number.sh' -o 't:' -- "${@}") +# shellcheck disable=SC2181 +if [ ${?} -ne 0 ] then usage - return fi - -ARGS=$(getopt -Q -q -n 'change-version-number.sh' -o 'a:t:' -- "$@") eval set -- "$ARGS" while [ -n "${1}" ] do case "${1}" in - '-a') - ARCHITECTURE="${2}" - shift - ;; - '-t') RELEASE_TRAIN="${2}" shift ;; - *) - VERSION="${1}" + '--') + shift + break ;; esac shift done -if [ -z "${VERSION}" ] +if [ -z "${1}" ] then + red-bar 'No version number given.' usage - exit 1 +elif [ -n "${2}" ] +then + red-bar 'Too many arguments given.' + usage +else + VERSION="${1}" fi -# shellcheck disable=SC2207 -VER=( $(IFS="."; echo "${VERSION}") ) +mapfile -t -d. VER < <(echo -n "${VERSION}") COUNT=${#VER[@]} + if [ "${COUNT}" -lt 3 ] then red-bar 'Too few components in version number -- must be at least three.' - exit 1 + usage elif [ "${COUNT}" -gt 4 ] then red-bar 'Too many components in version number -- must be at most four.' - exit 1 + usage fi + if [ "${COUNT}" -eq 3 ] then VER[3]=0 fi + STRINGVER="${VER[0]}.${VER[1]}.${VER[2]}.${VER[3]}" +if [ -z "${RELEASE_TRAIN}" ] +then + RELEASE_TRAIN=base +fi + +cat < version.h +cp version.h.in version.h -# shellcheck disable=SC2164 -cd "${LIGHTFIELD_ROOT}" +apply-atsign-substitution VERSION_STRING "${STRINGVER}" version.h +apply-atsign-substitution VERSION_MAJOR "${VER[0]}" version.h +apply-atsign-substitution VERSION_MINOR "${VER[1]}" version.h +apply-atsign-substitution VERSION_TEENY "${VER[2]}" version.h +apply-atsign-substitution VERSION_BUILD "${VER[3]}" version.h +apply-atsign-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" version.h -if [ -z "${RELEASE_TRAIN}" ] -then - SUFFIX=-${BUILDTYPE} -else - SUFFIX=-${RELEASE_TRAIN}-${BUILDTYPE} -fi - -if [ "${BUILDTYPE}" = release ] -then - ANTIBUILDTYPE=debug -else - ANTIBUILDTYPE=release -fi +# TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO +exit 69 -cp ${VERBOSE} debian/control.in debian/control -for a in install preinst postinst prerm postrm -do - cp ${VERBOSE} "debian/lightfield.${a}.in" "debian/lightfield-${SUFFIX}.${a}" -done +# shellcheck disable=SC2164 +cd "${LIGHTFIELD_ROOT}" blue-bar 'Updating build and packaging scripts' -perl \ - -lpi \ - -e "s/^\\s*ANTIBUILDTYPE=.*$/ANTIBUILDTYPE=\"${ANTIBUILDTYPE}\"/g;" \ - -e "s/@@ARCHITECTURE@@/${ARCHITECTURE}/g;" \ - -e "s/@@BUILDTYPE@@/${BUILDTYPE}/g;" \ - -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g;" \ - -e "s/@@VERSION@@/${STRINGVER}/g;" \ - -e "s/(?:amd64|arm7l)/${ARCHITECTURE}/g;" \ - install-lightfield.sh \ - make-deb-package.sh \ - make-upgrade-kit.sh \ - unpack-kit-manually.sh \ - -perl \ - -lpi \ - -e "s/@@ANTIBUILDTYPE@@/${ANTIBUILDTYPE}/g;" \ - -e "s/@@ARCHITECTURE@@/${ARCHITECTURE}/g;" \ - -e "s/@@BUILDTYPE@@/${BUILDTYPE}/g;" \ - -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g;" \ - -e "s/@@VERSION@@/${STRINGVER}/g;" \ - -e "s/(?:amd64|arm7l)/${ARCHITECTURE}/g;" \ - "debian/lightfield-${SUFFIX}.install" \ - "debian/lightfield-${SUFFIX}.preinst" \ - "debian/lightfield-${SUFFIX}.postinst" \ - "debian/lightfield-${SUFFIX}.prerm" \ - "debian/lightfield-${SUFFIX}.postrm" - -perl \ - -lpi \ - -e "s/@@ARCHITECTURE@@/ARCHITECTURE=\"${ARCHITECTURE}\"/g;" \ - -e "s/@@BUILDTYPE@@/BUILDTYPE=\"${BUILDTYPE}\"/g;" \ - -e "s/@@RELEASE_TRAIN@@/RELEASE_TRAIN=\"${RELEASE_TRAIN}\"/g;" \ - -e "s/@@VERSION@@/VERSION=${STRINGVER}/g;" \ - -e "s/(?:amd64|arm7l)/${ARCHITECTURE}/g;" \ - debian/control +apply-assignment-substitution ARCHITECTURE "${ARCHITECTURE}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh unpack-kit-manually.sh +apply-assignment-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh unpack-kit-manually.sh +apply-assignment-substitution VERSION "${STRINGVER}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh unpack-kit-manually.sh +perl -lpi -e "s/(amd64|arm7l)/${ARCHITECTURE}/g" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh unpack-kit-manually.sh + +blue-bar 'Done!' diff --git a/debian/control.in b/debian/control.in index f0c1bb26..532dcb0e 100644 --- a/debian/control.in +++ b/debian/control.in @@ -14,9 +14,10 @@ Vcs-Git: https://github.com/VolumetricBio/LightField.git Package: lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ Architecture: any -Breaks: lightfield@@RELEASE_TRAIN@@-@@ANTIBUILDTYPE@@ -Conflicts: lightfield@@RELEASE_TRAIN@@-@@ANTIBUILDTYPE@@ -Replaces: lightfield@@RELEASE_TRAIN@@-@@ANTIBUILDTYPE@@ +Provides: lightfield-@@BUILDTYPE@@ +Breaks: lightfield-@@ANTIBUILDTYPE@@ +Conflicts: lightfield-@@ANTIBUILDTYPE@@ +Replaces: lightfield-@@ANTIBUILDTYPE@@ Depends: lightfield@@RELEASE_TRAIN@@-common (= ${binary:Version}), ${shlibs:Depends}, @@ -37,6 +38,7 @@ Description: Printer software for Volumetric's Lumen X 3D printer - debug versio Package: lightfield@@RELEASE_TRAIN@@-common Architecture: all Multi-Arch: foreign +Provides: lightfield-common Depends: ${misc:Depends}, python3 (>= 3.6), diff --git a/debian/lightfield.postinst.in b/debian/lightfield.postinst.in index 0a2a4906..4b829601 100644 --- a/debian/lightfield.postinst.in +++ b/debian/lightfield.postinst.in @@ -17,18 +17,22 @@ # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package -RELEASE_TRAIN=-@@RELEASE_TRAIN@@ -BUILD=@@BUILD@@ +RELEASE_TRAIN=@@RELEASE_TRAIN@@ +BUILDTYPE=@@BUILDTYPE@@ VERSION=@@VERSION@@ ARCHITECTURE=@@ARCHITECTURE@@ - -[ "${RELEASE_TRAIN}" = "-base" ] && RELEASE_TRAIN= +if [ -z "${RELEASE_TRAIN}" ] || [ "${RELEASE_TRAIN}" = "base" ] +then + SUFFIX=${RELEASE_TRAIN}-${BUILDTYPE} +else + SUFFIX=${BUILDTYPE} +fi case "$1" in configure) perl -lpi -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null - echo "deb file:/var/lib/lightfield/software-updates/lightfield${RELEASE_TRAIN}-${BUILD}_${VERSION}_${ARCHITECTURE} ./" > /etc/apt/sources.list.d/volumetric-lightfield.list + echo "deb file:/var/lib/lightfield/software-updates/lightfield-${SUFFIX}_${VERSION}_${ARCHITECTURE} ./" > /etc/apt/sources.list.d/volumetric-lightfield.list chown lumen:lumen /etc/apt/sources.list.d/volumetric-lightfield.list chmod 644 /etc/apt/sources.list.d/volumetric-lightfield.list diff --git a/install-lightfield.sh b/install-lightfield.sh index 51066d08..fedeedeb 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -33,8 +33,8 @@ Usage: $(basename "$0") [-q] [-x] [-a ] [-t ] Where: -q Build quietly. -x Force rebuild all. -a Sets the architecture. Valid values: amd64 arm7l. - Default: ${DEFAULT_ARCHITECTURE} - -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} + Default: ${ARCHITECTURE} + -t Sets the release train. Default: ${RELEASE_TRAIN} EOF } @@ -52,19 +52,15 @@ FORCEREBUILD= BUILDQUIETLY= RELEASE_TRAIN=base -ARCHITECTURE=$(uname -m) -[ "${ARCHITECTURE}" = "x86_64" ] && ARCHITECTURE=amd64 +ARCHITECTURE=amd64 -DEFAULT_RELEASE_TRAIN=${RELEASE_TRAIN} -DEFAULT_ARCHITECTURE=${ARCHITECTURE} - -if ! getopt -Q -q -n 'install-lightfield.sh' -o 'qxa:t:' -- "$@" +ARGS=$(getopt -n 'install-lightfield.sh' -o 'qxa:t:' -- "${@}") +# shellcheck disable=SC2181 +if [ ${?} -ne 0 ] then usage - return + exit 1 fi - -ARGS=$(getopt -Q -q -n 'install-lightfield.sh' -o 'qxa:t:' -- "$@") eval set -- "$ARGS" while [ -n "$1" ] @@ -81,8 +77,14 @@ do ;; '-a') - ARCHITECTURE="${2}" - shift + if [ "${2}" = "amd64" ] || [ "${2}" = "arm7l" ] + then + ARCHITECTURE="${2}" + shift + else + echo "Unknown architecture '{$2}'." 1>&2 + usage + fi ;; '-t') @@ -90,6 +92,11 @@ do shift ;; + '--') + shift + break + ;; + *) usage exit 1 @@ -162,14 +169,15 @@ blue-bar • Configuring system perl -lpi -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null -if [ "${RELEASE_TRAIN}" = "base" ] +if [ -z "${RELEASE_TRAIN}" ] || [ "${RELEASE_TRAIN}" = "base" ] then RELEASE_TRAIN= + SUFFIX=debug else - RELEASE_TRAIN=-${RELEASE_TRAIN} + SUFFIX=${RELEASE_TRAIN}-debug fi -echo "deb file:/var/lib/lightfield/software-updates/lightfield${RELEASE_TRAIN}-debug_${VERSION}_${ARCHITECTURE} ./" > /etc/apt/sources.list.d/volumetric-lightfield.list +echo "deb file:/var/lib/lightfield/software-updates/lightfield${SUFFIX}_${VERSION}_${ARCHITECTURE} ./" > /etc/apt/sources.list.d/volumetric-lightfield.list chown ${CHXXXVERBOSE} lumen:lumen /etc/apt/sources.list.d/volumetric-lightfield.list systemctl daemon-reload diff --git a/make-deb-package.sh b/make-deb-package.sh index 22bf09f6..acea1cc1 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -1,7 +1,5 @@ #!/bin/bash -ARCHITECTURE=amd64 -RELEASE_TRAIN=base VERSION=1.0.10.0 PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging @@ -26,21 +24,36 @@ function error-trap () { } function usage () { - cat <&2 < Sets the architecture. Valid values: amd64 arm7l. - Default: ${DEFAULT_ARCHITECTURE} - -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} + -t Sets the release train. Default: ${RELEASE_TRAIN} BUILDTYPE is one of release create a release-build kit debug create a debug-build kit both create both kits -If the build is successful, the requested package set(s) will be found in - ${DEB_BUILD_DIR}/ +If the build is successful, the requested package set(s) will be found in a +subdirectory of ${PACKAGE_BUILD_ROOT}/ . EOF + exit 1 +} + +function apply-atsign-substitution () { + local CODE='\e[0;30;48;2;146;208;80m»' + local RST='«\e[0m' + # shellcheck disable=SC2145 + echo "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" + perl -ilp -e "s/\\@\\@${1}\\@\\@/${2}/g;" "${@:3}" +} + +function apply-assignment-substitution () { + local CODE='\e[0;30;48;2;0;146;208m»' + local RST='«\e[0m' + # shellcheck disable=SC2145 + echo "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" + perl -ilp -e "s/^(\\s*)${1}=.*\$/\$1${1}=${2}/g;" "${@:3}" } trap error-trap ERR @@ -50,65 +63,54 @@ PRINTRUN_SRC=/home/lumen/Volumetric/printrun LIGHTFIELD_SRC=/home/lumen/Volumetric/LightField MOUNTMON_SRC="${LIGHTFIELD_SRC}/mountmon" USBDRIVER_SRC="${LIGHTFIELD_SRC}/usb-driver" -PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" -DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" -LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" -LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" VERBOSE=-v CHXXXVERBOSE=-c FORCEREBUILD=-x -BUILDTYPE=debug - -DEFAULT_ARCHITECTURE=${ARCHITECTURE} -DEFAULT_RELEASE_TRAIN=${RELEASE_TRAIN} +BUILDTYPE= +ARCHITECTURE=$(uname -m) +RELEASE_TRAIN=base -if ! getopt -Q -q -n 'make-deb-package.sh' -o 'qXa:t:' -- "$@" +ARGS=$(getopt -n 'make-deb-package.sh' -o 'qXa:t:' -- "$@") +# shellcheck disable=SC2181 +if [ ${?} -ne 0 ] then usage - exit 1 fi - -ARGS=$(getopt -Q -q -n 'make-deb-package.sh' -o 'qXa:t:' -- "$@") eval set -- "$ARGS" while [ -n "$1" ] do case "$1" in - "-q") + '-q') VERBOSE= CHXXXVERBOSE= ;; - "-X") + '-X') FORCEREBUILD= ;; - "-a") - if [ "${2}" = "amd64" ] || [ "${2}" = "arm7l" ] - then - ARCHITECTURE="${2}" - shift - else - usage - exit 1 - fi - ;; - - "-t") + '-t') RELEASE_TRAIN="${2}" shift ;; - "release" | "debug" | "both") - BUILDTYPE="${1}" + 'release' | 'debug' | 'both') + if [ -z "${BUILDTYPE}" ] + then + BUILDTYPE="${1}" + else + echo "Too many build types specified -- use 'both' to build debug and release packages." 1>&2 + usage + fi break ;; *) + echo "Unknown parameter '${1}'." usage - exit 1 ;; esac shift @@ -117,14 +119,6 @@ done if [ -z "${BUILDTYPE}" ] then usage - exit 1 -fi - -if [ "${RELEASE_TRAIN}" = "base" ] -then - SUFFIX=-${BUILDTYPE} -else - SUFFIX=-${RELEASE_TRAIN}-${BUILDTYPE} fi if [ "${BUILDTYPE}" = "both" ] @@ -134,6 +128,26 @@ then exit 0 fi +if [ -z "${RELEASE_TRAIN}" ] || [ "${RELEASE_TRAIN}" = "base" ] +then + SUFFIX=${BUILDTYPE} + RELEASE_TRAIN= +else + SUFFIX=${RELEASE_TRAIN}-${BUILDTYPE} +fi + +if [ "${BUILDTYPE}" = debug ] +then + ANTIBUILDTYPE=release +else + ANTIBUILDTYPE=debug +fi + +PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" +DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" +LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" +LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" + ################################################## blue-bar "• Setting up build environment" @@ -163,14 +177,13 @@ elif [ "${BUILDTYPE}" = "release" ] then OPTS="-s -O3 -DNDEBUG" fi -g++ -o "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" "${OPTS}" -pipe -std=gnu++1z -Wall -W -D_GNU_SOURCE -fPIC dlpc350_usb.cpp dlpc350_api.cpp main.cpp -lhidapi-libusb +# shellcheck disable=SC2086 +g++ -o "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" ${OPTS} -pipe -std=gnu++1z -Wall -W -D_GNU_SOURCE -fPIC dlpc350_usb.cpp dlpc350_api.cpp main.cpp -lhidapi-libusb ################################################## cd "${MOUNTMON_SRC}" -################################################## - blue-bar "• Building ${BUILDTYPE} version of Mountmon" if [ "${BUILDTYPE}" = "debug" ] @@ -180,6 +193,7 @@ elif [ "${BUILDTYPE}" = "release" ] then ./rebuild ${FORCEREBUILD} -r fi +chown ${CHXXXVERBOSE} -R lumen:lumen build install ${VERBOSE} -DT -m 755 build/mountmon "${LIGHTFIELD_FILES}/usr/bin/mountmon" @@ -187,8 +201,6 @@ install ${VERBOSE} -DT -m 755 build/mountmon "${LIGHTFIELD_FILES}/usr/bin/mountm cd "${LIGHTFIELD_SRC}" -################################################## - blue-bar "• Building ${BUILDTYPE} version of LightField" if [ "${BUILDTYPE}" = "debug" ] @@ -229,8 +241,6 @@ chmod ${CHXXXVERBOSE} -R go= "${LIGHTFIELD_FILES}/home/lumen/.gnupg" cd "${PRINTRUN_SRC}" -################################################## - blue-bar "• Copying printrun files into packaging directory" install ${VERBOSE} -DT -m 644 printrun/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/__init__.py" @@ -245,27 +255,27 @@ install ${VERBOSE} -DT -m 644 Util/constants.py "$ cd "${LIGHTFIELD_PACKAGE}" -################################################## - blue-bar "• Building Debian packages" -## TODO TODO TODO TODO TODO -## TODO need to embed the release train name into the stuff in debian/ -## TODO TODO TODO TODO TODO +cp debian/control.in debian/control +for a in install preinst postinst prerm postrm +do + cp "${VERBOSE}" "debian/lightfield.${a}.in" "debian/lightfield-${SUFFIX}.${a}" +done -if [ "${BUILDTYPE}" = debug ] +apply-atsign-substitution BUILDTYPE "${BUILDTYPE}" debian/control +apply-atsign-substitution ANTIBUILDTYPE "${ANTIBUILDTYPE}" debian/control +if [ -n "${RELEASE_TRAIN}" ] then - ANTIBUILDTYPE=release + apply-atsign-substitution RELEASE_TRAIN "-${RELEASE_TRAIN}" debian/control else - ANTIBUILDTYPE=debug + apply-atsign-substitution RELEASE_TRAIN "" debian/control fi -sed \ - -e "s/@@BUILDTYPE@@/${BUILDTYPE}/g" \ - -e "s/@@ANTIBUILDTYPE@@/${ANTIBUILDTYPE}/g" \ - -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g" \ - < debian/control.in \ - > debian/control +apply-assignment-substitution ARCHITECTURE "${ARCHITECTURE}" debian/lightfield.postinst.in +apply-assignment-substitution BUILDTYPE "${BUILDTYPE}" debian/lightfield.postinst.in +apply-assignment-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" debian/lightfield.postinst.in +apply-assignment-substitution VERSION "${VERSION}" debian/lightfield.postinst.in dpkg-buildpackage --build=any,all --no-sign diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index aee3a9be..287dfc30 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -1,6 +1,5 @@ #!/bin/bash -ARCHITECTURE=amd64 RELEASE_TRAIN=base VERSION=1.0.10.0 @@ -30,9 +29,7 @@ function usage () { cat < Sets the architecture. Valid values: amd64 arm7l. - Default: ${DEFAULT_ARCHITECTURE} - -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} + -t Sets the release train. Default: ${RELEASE_TRAIN} BUILDTYPE is one of release create a release-build kit debug create a debug-build kit @@ -60,46 +57,31 @@ fi VERBOSE=-v -ARCHITECTURE=$(uname -m) -[ "${ARCHITECTURE}" = "x86_64" ] && ARCHITECTURE=amd64 BUILDTYPE= +ARCHITECTURE=$(uname -m) +RELEASE_TRAIN=base -DEFAULT_ARCHITECTURE=${ARCHITECTURE} -DEFAULT_RELEASE_TRAIN=${RELEASE_TRAIN} - -if ! getopt -Q -q -n 'make-upgrade-kit.sh' -o 'qa:t:' -- "$@" +ARGS=$(getopt -n 'make-upgrade-kit.sh' -o 'qa:t:' -- "$@") +# shellcheck disable=SC2181 +if [ ${?} -ne 0 ] then usage - exit 1 fi - -ARGS=$(getopt -Q -q -n 'make-upgrade-kit.sh' -o 'qa:t:' -- "$@") eval set -- "$ARGS" while [ -n "$1" ] do case "$1" in - "-q") + '-q') VERBOSE= ;; - "-a") - if [ "${2}" = "amd64" ] || [ "${2}" = "arm7l" ] - then - ARCHITECTURE="${2}" - shift - else - usage - exit 1 - fi - ;; - - "-t") + '-t') RELEASE_TRAIN="${2}" shift ;; - "release" | "debug" | "both") + 'release' | 'debug' | 'both') BUILDTYPE="${1}" break ;; @@ -118,7 +100,7 @@ then exit 1 fi -if [ "${RELEASE_TRAIN}" = "base" ] +if [ -z "${RELEASE_TRAIN}" ] || [ "${RELEASE_TRAIN}" = "base" ] then SUFFIX=${BUILDTYPE} else @@ -128,9 +110,6 @@ fi LIGHTFIELD_SRC="/home/lumen/Volumetric/LightField" PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" -#LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" -#LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" - KIT_DIR="${PACKAGE_BUILD_DIR}/kit" if [ "${BUILDTYPE}" = "both" ] From a0ca85404f11eb21a6bb6cb39b90424e9f4140c9 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Thu, 9 Jan 2020 12:14:24 -0800 Subject: [PATCH 10/89] Add DLP4710 set-projector-power. --- dlp4710/Makefile | 34 +++ dlp4710/set-projector-power.cpp | 428 ++++++++++++++++++++++++++++++++ 2 files changed, 462 insertions(+) create mode 100644 dlp4710/Makefile create mode 100644 dlp4710/set-projector-power.cpp diff --git a/dlp4710/Makefile b/dlp4710/Makefile new file mode 100644 index 00000000..b91086b7 --- /dev/null +++ b/dlp4710/Makefile @@ -0,0 +1,34 @@ +BUILD = debug + +CXX = g++ +LD = g++ +RM = rm + +TARGET = set-projector-power +SOURCES = set-projector-power.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +CFLAGS = -std=gnu++17 -Wall -Wextra -Werror -pedantic +LFLAGS = +ifeq ($(BUILD),debug) +CFLAGS += -D_DEBUG -g -Og +LFLAGS += -g +else ifeq ($(BUILD),release) +CFLAGS += -DNDEBUG -O3 +LFLAGS += -s +endif + +all: $(TARGET) + +%.o: %.cpp + $(CXX) $(CFLAGS) -c $< + +$(TARGET): $(OBJECTS) + $(LD) $(LFLAGS) -o $@ $^ + +clean: + -$(RM) $(TARGET) $(OBJECTS) + +## Dependencies + +set-projector-power.o: set-projector-power.cpp diff --git a/dlp4710/set-projector-power.cpp b/dlp4710/set-projector-power.cpp new file mode 100644 index 00000000..1eebf439 --- /dev/null +++ b/dlp4710/set-projector-power.cpp @@ -0,0 +1,428 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + + enum class Mode { + QueryPowerLevel, + SetPowerLevel, + FirstTimeConfiguration, + }; + + namespace Option { + int const Help = 'h'; + int const FirstTime = 'f'; + int const Monitor = 'm'; + } + + char const ShortOptions[] { ":h?fm" }; + + option const LongOptions[] { + { "help", no_argument, nullptr, Option::Help }, + { "first-time", no_argument, nullptr, Option::FirstTime }, + { "monitor", no_argument, nullptr, Option::Monitor }, + }; + + bool StringToUnsignedLong( char const* ptr, int const radix, unsigned long* result ) { + char* endptr = NULL; + + errno = 0; + unsigned long ret = strtoul( ptr, &endptr, radix ); + if ( endptr != ( ptr + strlen( ptr ) ) ) { + return false; + } + if ( errno == ERANGE && ret == ULONG_MAX ) { + return false; + } + if ( errno != 0 && ret == 0UL ) { + return false; + } + + *result = ret; + return true; + } + + template + bool WriteCommandToProjector( int const fd, char const* format, Args... args ) { + char buf[4096] { }; + snprintf( buf, 4093, format, args... ); + strcat( buf, "\r\n" ); + auto len = strlen( buf ); + auto rc = write( fd, buf, len ); + if ( rc < 0 ) { + perror( "set-projector-power: write" ); + return false; + } else if ( static_cast( rc ) < len ) { + fprintf( stderr, "+ WriteCommandToProjector: short write: %zu expected, %ld written\n", len, rc ); + return false; + } + return true; + } + + bool ReadResponseFromProjector( int const fd, char* buffer, size_t const bufferLength ) { + size_t index = 0; + + memset( buffer, '\0', bufferLength ); + while ( index < bufferLength ) { + auto rc = read( fd, &buffer[index], 1 ); + if ( rc < 0 ) { + perror( "set-projector-power: read" ); + return false; + } else if ( rc == 0 ) { + fprintf( stderr, "+ ReadResponseFromProjector: EOF??\n" ); + return false; + } + + if ( ( index > 0 ) && ( buffer[index - 1] == '\r' ) && ( buffer[index] == '\n' ) ) { + buffer[index - 1] = '\0'; + return true; + } + + ++index; + } + return true; + } + + template + bool SendCommand( int const fd, char* responseBuffer, char const* format, Args... args ) { + if ( !WriteCommandToProjector( fd, format, args... ) ) { + fprintf( stderr, "SendCommand: WriteCommandToProjector failed\n" ); + return false; + } + + if ( !ReadResponseFromProjector( fd, responseBuffer, 4095 ) ) { + fprintf( stderr, "SendCommand: ReadResponseFromProjector failed\n" ); + return false; + } + + return true; + } + + bool IsGoodResponse( char const* result ) { + return ( 0 != strcmp( "ERROR", result ) ); + } + + bool CheckChecksum( unsigned const value, unsigned const checksum ) { + unsigned tempChecksum = 0; + unsigned tempValue = value; + + while ( tempValue > 0 ) { + tempChecksum += tempValue % 10; + tempValue /= 10; + } + + return ( checksum == tempChecksum ); + } + + [[noreturn]] + void PrintUsageAndExit( bool success = false ) { + fprintf( stderr, + "Usage: set-projector-power --first-time\n" + " or: set-projector-power [--monitor] [ []]\n" + "Where:\n" + " -h, -?, --help display this help and exit\n" + " -f, --first-time perform first-time projector configuration\n" + " -m, --monitor monitor projector brightness and temperature\n" + " brightness, range 0..1023\n" + " milliseconds, range 1..65535\n" + ); + exit( success ? EXIT_SUCCESS : EXIT_FAILURE ); + } + + void MonitorPowerAndTemperature( int const fd ) { + unsigned checksum; + unsigned brightness; + unsigned temperature; + siginfo_t sigInfo; + sigset_t sigset; + int sig; + timespec const timeout { 1, 0 }; + char buf[4096] { }; + + sigemptyset( &sigset ); + sigaddset( &sigset, SIGHUP ); + sigaddset( &sigset, SIGINT ); + sigaddset( &sigset, SIGQUIT ); + sigaddset( &sigset, SIGTERM ); + + while ( true ) { + // + // Query LED brightness + // + + if ( !SendCommand( fd, buf, "WT+GLGT" ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: failed to send command" ); + return; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: got negative response '%s'\n", buf ); + return; + } + + errno = 0; + if ( 2 != sscanf( buf, "OK:%u:%u", &brightness, &checksum ) ) { + auto err = errno; + if ( err == 0 ) { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: couldn't parse response '%s'\n", buf ); + } else { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: couldn't parse response '%s': %s [%d]\n", buf, strerror( err ), err ); + } + return; + } + if ( !CheckChecksum( brightness, checksum ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: got bad checksum\n" ); + goto next; + } + + // + // Query LED temperature + // + + if ( !SendCommand( fd, buf, "WT+GTMP" ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: failed to send command" ); + return; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: got negative response '%s'\n", buf ); + return; + } + + errno = 0; + if ( 2 != sscanf( buf, "OK:%u:%u", &temperature, &checksum ) ) { + auto err = errno; + if ( err == 0 ) { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: couldn't parse response '%s'\n", buf ); + } else { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: couldn't parse response '%s': %s [%d]\n", buf, strerror( err ), err ); + } + return; + } + if ( !CheckChecksum( temperature, checksum ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: got bad checksum\n" ); + goto next; + } + + printf( "\rbrightness: %4u, temperature: %4u °C", brightness, temperature ); + + next: + sig = sigtimedwait( &sigset, &sigInfo, &timeout ); + if ( -1 == sig ) { + if ( ( EAGAIN == errno ) || ( EINTR == errno ) ) { + continue; + } else { + fputc( '\n', stdout ); + perror( "set-projector-power: sigtimedwait" ); + } + } else if ( SIGINT == sig ) { + fputc( '\n', stdout ); + exit( 0 ); + } else { + fprintf( stderr, "\n+ sigtimedwait: got unknown signal %d?\n", sig ); + } + } + } + +} + +int main( int argc, char** argv ) { + Mode mode { Mode::QueryPowerLevel }; + unsigned long powerLevel { }; + unsigned long duration { 0 }; + int ret { 1 }; + char buf[4096] { }; + int fd { }; + termios term { }; + bool monitor { }; + + setvbuf( stdout, nullptr, _IONBF, 0 ); + + int opt; + do { + opt = getopt_long( argc, argv, ShortOptions, LongOptions, nullptr ); + if ( -1 == opt ) { + break; + } + + if ( Option::FirstTime == opt ) { + if ( monitor ) { + fprintf( stderr, "set-projector-power: options --first-time and --monitor may not be combined\n" ); + PrintUsageAndExit( ); + } + mode = Mode::FirstTimeConfiguration; + } else if ( Option::Monitor == opt ) { + if ( Mode::FirstTimeConfiguration == mode ) { + fprintf( stderr, "set-projector-power: options --first-time and --monitor may not be combined\n" ); + PrintUsageAndExit( ); + } + monitor = true; + } else if ( Option::Help == opt ) { + PrintUsageAndExit( true ); + } else { + PrintUsageAndExit( ); + } + } while ( opt != -1 ); + + if ( Mode::FirstTimeConfiguration != mode ) { + if ( optind < argc ) { + if ( StringToUnsignedLong( argv[optind], 10, &powerLevel ) ) { + if ( powerLevel > 1023 ) { + fprintf( stderr, "set-projector-power: brightness value %lu out of range\n", powerLevel ); + PrintUsageAndExit( ); + } + mode = Mode::SetPowerLevel; + } + ++optind; + } + if ( optind < argc ) { + if ( StringToUnsignedLong( argv[optind], 10, &duration ) ) { + if ( ( 0 == duration ) || ( duration > 65535 ) ) { + fprintf( stderr, "set-projector-power: duration value %lu out of range\n", duration ); + PrintUsageAndExit( ); + } + } + ++optind; + } + } + + fd = open( "/dev/ttyUSB0", O_RDWR ); + if ( -1 == fd ) { + perror( "set-projector-power: open" ); + goto bail1; + } + + if ( -1 == tcgetattr( fd, &term ) ) { + perror( "set-projector-power: tcgetattr" ); + goto bail2; + } + + if ( -1 == cfsetispeed( &term, B115200 ) ) { + perror( "set-projector-power: cfsetispeed" ); + goto bail2; + } + if ( -1 == cfsetospeed( &term, B115200 ) ) { + perror( "set-projector-power: cfsetospeed" ); + goto bail2; + } + + cfmakeraw( &term ); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 5; + + if ( -1 == tcsetattr( fd, TCSANOW, &term ) ) { + perror( "set-projector-power: tcsetattr" ); + goto bail2; + } + + if ( !SendCommand( fd, buf, "WT+PWRE=%d", 1 ) ) { + fprintf( stderr, "set-projector-power: command WT+PWRE: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+PWRE: got negative response ('%s')\n", buf ); + goto bail2; + } + + switch ( mode ) { + case Mode::FirstTimeConfiguration: + printf( "Setting default boot state\n" ); + if ( !SendCommand( fd, buf, "WT+SPWR=%d", 1 ) ) { + fprintf( stderr, "set-projector-power: command WT+SPWR: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+SPWR: got negative response ('%s')\n", buf ); + goto bail2; + } + + printf( "Setting default LED state\n" ); + if ( !SendCommand( fd, buf, "WT+SLED=%d", 0 ) ) { + fprintf( stderr, "set-projector-power: command WT+SLED: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+SLED: got negative response ('%s')\n", buf ); + goto bail2; + } + + printf( "Setting default brightness\n" ); + if ( !SendCommand( fd, buf, "WT+SBTN=%d", 0 ) ) { + fprintf( stderr, "set-projector-power: command WT+SBTN: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+SBTN: got negative response ('%s')\n", buf ); + goto bail2; + } + + printf( "Done!\n" ); + break; + + case Mode::SetPowerLevel: + if ( !SendCommand( fd, buf, "WT+LEDE=%d", ( 0 == powerLevel ) ? 0 : 1 ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDE: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDE: got negative response ('%s')\n", buf ); + goto bail2; + } + + if ( !SendCommand( fd, buf, "WT+LEDS=%lu", powerLevel ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDS: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDS: got negative response ('%s')\n", buf ); + goto bail2; + } + + if ( duration > 0 ) { + if ( !SendCommand( fd, buf, "WT+LEDT=%lu", duration ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDT: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDT: got negative response ('%s')\n", buf ); + goto bail2; + } + } + // FALLTHROUGH + + case Mode::QueryPowerLevel: + if ( !SendCommand( fd, buf, "WT+LEDR" ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDR: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDR: got negative response ('%s')\n", buf ); + goto bail2; + } + if ( !StringToUnsignedLong( buf, 10, &powerLevel ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDR: couldn't convert '%s' to a number\n", buf ); + goto bail2; + } + printf( "LED brightness: %lu\n", powerLevel ); + break; + } + + if ( monitor ) { + MonitorPowerAndTemperature( fd ); + } + + ret = 0; + +bail2: + close( fd ); + +bail1: + return ret; +} From b74f4185070b2ab446af19c63f418454ab8bff6e Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 16 Dec 2019 14:59:10 -0800 Subject: [PATCH 11/89] Fix value of PrinterDefaultHighSpeed after raising it the other day during testing. --- src/constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants.h b/src/constants.h index f2ec1d68..54455476 100644 --- a/src/constants.h +++ b/src/constants.h @@ -50,7 +50,7 @@ double const PrinterMaximumZ = 50.00; // mm double const PrinterRaiseToMaximumZ = 60.00; // mm double const PrinterHighSpeedThresholdZ = 10.00; // mm -double const PrinterDefaultHighSpeed = 600.00; // mm/min +double const PrinterDefaultHighSpeed = 200.00; // mm/min double const PrinterDefaultLowSpeed = 50.00; // mm/min double const AspectRatio5to3 = 5.0 / 3.0; From e6a51f019d87cef3882b0ea2284caa6112ca3001 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Wed, 18 Dec 2019 12:20:29 -0800 Subject: [PATCH 12/89] CMakeLists.txt: Update Qt path again. --- CMakeLists.txt | 2 +- mountmon/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b1b6626..2d6b0cc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set (PROJECT_VERSION "${LF_VERSION_MAJOR}.${LF_VERSION_MINOR}.${LF_VERSION_PATCH if(WIN32) # Set the Qt5 path -set(Qt5_DIR "F:\\\\Qt\\\\5.13.1\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") +set(Qt5_DIR "F:\\\\Qt\\\\5.14.0\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") endif(WIN32) project(lf) diff --git a/mountmon/CMakeLists.txt b/mountmon/CMakeLists.txt index 5f071dbb..69ae720c 100644 --- a/mountmon/CMakeLists.txt +++ b/mountmon/CMakeLists.txt @@ -13,7 +13,7 @@ set(MOUNTMON_VERSION_PATCH "0") set(PROJECT_VERSION "${MOUNTMON_VERSION_MAJOR}.${MOUNTMON_VERSION_MINOR}.${MOUNTMON_VERSION_PATCH}") if(WIN32) -set (Qt5_DIR "F:\\\\Qt\\\\5.13.1\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") +set (Qt5_DIR "F:\\\\Qt\\\\5.14.0\\\\msvc2017_64\\\\lib\\\\cmake\\\\Qt5") endif(WIN32) project(mountmon) From fb10d66f6adf08ada0964bc1700e124fdb26bf1c Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Thu, 9 Jan 2020 12:14:24 -0800 Subject: [PATCH 13/89] Add DLP4710 set-projector-power. --- dlp4710/Makefile | 34 +++ dlp4710/set-projector-power.cpp | 428 ++++++++++++++++++++++++++++++++ 2 files changed, 462 insertions(+) create mode 100644 dlp4710/Makefile create mode 100644 dlp4710/set-projector-power.cpp diff --git a/dlp4710/Makefile b/dlp4710/Makefile new file mode 100644 index 00000000..b91086b7 --- /dev/null +++ b/dlp4710/Makefile @@ -0,0 +1,34 @@ +BUILD = debug + +CXX = g++ +LD = g++ +RM = rm + +TARGET = set-projector-power +SOURCES = set-projector-power.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +CFLAGS = -std=gnu++17 -Wall -Wextra -Werror -pedantic +LFLAGS = +ifeq ($(BUILD),debug) +CFLAGS += -D_DEBUG -g -Og +LFLAGS += -g +else ifeq ($(BUILD),release) +CFLAGS += -DNDEBUG -O3 +LFLAGS += -s +endif + +all: $(TARGET) + +%.o: %.cpp + $(CXX) $(CFLAGS) -c $< + +$(TARGET): $(OBJECTS) + $(LD) $(LFLAGS) -o $@ $^ + +clean: + -$(RM) $(TARGET) $(OBJECTS) + +## Dependencies + +set-projector-power.o: set-projector-power.cpp diff --git a/dlp4710/set-projector-power.cpp b/dlp4710/set-projector-power.cpp new file mode 100644 index 00000000..1eebf439 --- /dev/null +++ b/dlp4710/set-projector-power.cpp @@ -0,0 +1,428 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + + enum class Mode { + QueryPowerLevel, + SetPowerLevel, + FirstTimeConfiguration, + }; + + namespace Option { + int const Help = 'h'; + int const FirstTime = 'f'; + int const Monitor = 'm'; + } + + char const ShortOptions[] { ":h?fm" }; + + option const LongOptions[] { + { "help", no_argument, nullptr, Option::Help }, + { "first-time", no_argument, nullptr, Option::FirstTime }, + { "monitor", no_argument, nullptr, Option::Monitor }, + }; + + bool StringToUnsignedLong( char const* ptr, int const radix, unsigned long* result ) { + char* endptr = NULL; + + errno = 0; + unsigned long ret = strtoul( ptr, &endptr, radix ); + if ( endptr != ( ptr + strlen( ptr ) ) ) { + return false; + } + if ( errno == ERANGE && ret == ULONG_MAX ) { + return false; + } + if ( errno != 0 && ret == 0UL ) { + return false; + } + + *result = ret; + return true; + } + + template + bool WriteCommandToProjector( int const fd, char const* format, Args... args ) { + char buf[4096] { }; + snprintf( buf, 4093, format, args... ); + strcat( buf, "\r\n" ); + auto len = strlen( buf ); + auto rc = write( fd, buf, len ); + if ( rc < 0 ) { + perror( "set-projector-power: write" ); + return false; + } else if ( static_cast( rc ) < len ) { + fprintf( stderr, "+ WriteCommandToProjector: short write: %zu expected, %ld written\n", len, rc ); + return false; + } + return true; + } + + bool ReadResponseFromProjector( int const fd, char* buffer, size_t const bufferLength ) { + size_t index = 0; + + memset( buffer, '\0', bufferLength ); + while ( index < bufferLength ) { + auto rc = read( fd, &buffer[index], 1 ); + if ( rc < 0 ) { + perror( "set-projector-power: read" ); + return false; + } else if ( rc == 0 ) { + fprintf( stderr, "+ ReadResponseFromProjector: EOF??\n" ); + return false; + } + + if ( ( index > 0 ) && ( buffer[index - 1] == '\r' ) && ( buffer[index] == '\n' ) ) { + buffer[index - 1] = '\0'; + return true; + } + + ++index; + } + return true; + } + + template + bool SendCommand( int const fd, char* responseBuffer, char const* format, Args... args ) { + if ( !WriteCommandToProjector( fd, format, args... ) ) { + fprintf( stderr, "SendCommand: WriteCommandToProjector failed\n" ); + return false; + } + + if ( !ReadResponseFromProjector( fd, responseBuffer, 4095 ) ) { + fprintf( stderr, "SendCommand: ReadResponseFromProjector failed\n" ); + return false; + } + + return true; + } + + bool IsGoodResponse( char const* result ) { + return ( 0 != strcmp( "ERROR", result ) ); + } + + bool CheckChecksum( unsigned const value, unsigned const checksum ) { + unsigned tempChecksum = 0; + unsigned tempValue = value; + + while ( tempValue > 0 ) { + tempChecksum += tempValue % 10; + tempValue /= 10; + } + + return ( checksum == tempChecksum ); + } + + [[noreturn]] + void PrintUsageAndExit( bool success = false ) { + fprintf( stderr, + "Usage: set-projector-power --first-time\n" + " or: set-projector-power [--monitor] [ []]\n" + "Where:\n" + " -h, -?, --help display this help and exit\n" + " -f, --first-time perform first-time projector configuration\n" + " -m, --monitor monitor projector brightness and temperature\n" + " brightness, range 0..1023\n" + " milliseconds, range 1..65535\n" + ); + exit( success ? EXIT_SUCCESS : EXIT_FAILURE ); + } + + void MonitorPowerAndTemperature( int const fd ) { + unsigned checksum; + unsigned brightness; + unsigned temperature; + siginfo_t sigInfo; + sigset_t sigset; + int sig; + timespec const timeout { 1, 0 }; + char buf[4096] { }; + + sigemptyset( &sigset ); + sigaddset( &sigset, SIGHUP ); + sigaddset( &sigset, SIGINT ); + sigaddset( &sigset, SIGQUIT ); + sigaddset( &sigset, SIGTERM ); + + while ( true ) { + // + // Query LED brightness + // + + if ( !SendCommand( fd, buf, "WT+GLGT" ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: failed to send command" ); + return; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: got negative response '%s'\n", buf ); + return; + } + + errno = 0; + if ( 2 != sscanf( buf, "OK:%u:%u", &brightness, &checksum ) ) { + auto err = errno; + if ( err == 0 ) { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: couldn't parse response '%s'\n", buf ); + } else { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: couldn't parse response '%s': %s [%d]\n", buf, strerror( err ), err ); + } + return; + } + if ( !CheckChecksum( brightness, checksum ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GLGT: got bad checksum\n" ); + goto next; + } + + // + // Query LED temperature + // + + if ( !SendCommand( fd, buf, "WT+GTMP" ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: failed to send command" ); + return; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: got negative response '%s'\n", buf ); + return; + } + + errno = 0; + if ( 2 != sscanf( buf, "OK:%u:%u", &temperature, &checksum ) ) { + auto err = errno; + if ( err == 0 ) { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: couldn't parse response '%s'\n", buf ); + } else { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: couldn't parse response '%s': %s [%d]\n", buf, strerror( err ), err ); + } + return; + } + if ( !CheckChecksum( temperature, checksum ) ) { + fprintf( stderr, "\nset-projector-power: command WT+GTMP: got bad checksum\n" ); + goto next; + } + + printf( "\rbrightness: %4u, temperature: %4u °C", brightness, temperature ); + + next: + sig = sigtimedwait( &sigset, &sigInfo, &timeout ); + if ( -1 == sig ) { + if ( ( EAGAIN == errno ) || ( EINTR == errno ) ) { + continue; + } else { + fputc( '\n', stdout ); + perror( "set-projector-power: sigtimedwait" ); + } + } else if ( SIGINT == sig ) { + fputc( '\n', stdout ); + exit( 0 ); + } else { + fprintf( stderr, "\n+ sigtimedwait: got unknown signal %d?\n", sig ); + } + } + } + +} + +int main( int argc, char** argv ) { + Mode mode { Mode::QueryPowerLevel }; + unsigned long powerLevel { }; + unsigned long duration { 0 }; + int ret { 1 }; + char buf[4096] { }; + int fd { }; + termios term { }; + bool monitor { }; + + setvbuf( stdout, nullptr, _IONBF, 0 ); + + int opt; + do { + opt = getopt_long( argc, argv, ShortOptions, LongOptions, nullptr ); + if ( -1 == opt ) { + break; + } + + if ( Option::FirstTime == opt ) { + if ( monitor ) { + fprintf( stderr, "set-projector-power: options --first-time and --monitor may not be combined\n" ); + PrintUsageAndExit( ); + } + mode = Mode::FirstTimeConfiguration; + } else if ( Option::Monitor == opt ) { + if ( Mode::FirstTimeConfiguration == mode ) { + fprintf( stderr, "set-projector-power: options --first-time and --monitor may not be combined\n" ); + PrintUsageAndExit( ); + } + monitor = true; + } else if ( Option::Help == opt ) { + PrintUsageAndExit( true ); + } else { + PrintUsageAndExit( ); + } + } while ( opt != -1 ); + + if ( Mode::FirstTimeConfiguration != mode ) { + if ( optind < argc ) { + if ( StringToUnsignedLong( argv[optind], 10, &powerLevel ) ) { + if ( powerLevel > 1023 ) { + fprintf( stderr, "set-projector-power: brightness value %lu out of range\n", powerLevel ); + PrintUsageAndExit( ); + } + mode = Mode::SetPowerLevel; + } + ++optind; + } + if ( optind < argc ) { + if ( StringToUnsignedLong( argv[optind], 10, &duration ) ) { + if ( ( 0 == duration ) || ( duration > 65535 ) ) { + fprintf( stderr, "set-projector-power: duration value %lu out of range\n", duration ); + PrintUsageAndExit( ); + } + } + ++optind; + } + } + + fd = open( "/dev/ttyUSB0", O_RDWR ); + if ( -1 == fd ) { + perror( "set-projector-power: open" ); + goto bail1; + } + + if ( -1 == tcgetattr( fd, &term ) ) { + perror( "set-projector-power: tcgetattr" ); + goto bail2; + } + + if ( -1 == cfsetispeed( &term, B115200 ) ) { + perror( "set-projector-power: cfsetispeed" ); + goto bail2; + } + if ( -1 == cfsetospeed( &term, B115200 ) ) { + perror( "set-projector-power: cfsetospeed" ); + goto bail2; + } + + cfmakeraw( &term ); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 5; + + if ( -1 == tcsetattr( fd, TCSANOW, &term ) ) { + perror( "set-projector-power: tcsetattr" ); + goto bail2; + } + + if ( !SendCommand( fd, buf, "WT+PWRE=%d", 1 ) ) { + fprintf( stderr, "set-projector-power: command WT+PWRE: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+PWRE: got negative response ('%s')\n", buf ); + goto bail2; + } + + switch ( mode ) { + case Mode::FirstTimeConfiguration: + printf( "Setting default boot state\n" ); + if ( !SendCommand( fd, buf, "WT+SPWR=%d", 1 ) ) { + fprintf( stderr, "set-projector-power: command WT+SPWR: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+SPWR: got negative response ('%s')\n", buf ); + goto bail2; + } + + printf( "Setting default LED state\n" ); + if ( !SendCommand( fd, buf, "WT+SLED=%d", 0 ) ) { + fprintf( stderr, "set-projector-power: command WT+SLED: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+SLED: got negative response ('%s')\n", buf ); + goto bail2; + } + + printf( "Setting default brightness\n" ); + if ( !SendCommand( fd, buf, "WT+SBTN=%d", 0 ) ) { + fprintf( stderr, "set-projector-power: command WT+SBTN: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+SBTN: got negative response ('%s')\n", buf ); + goto bail2; + } + + printf( "Done!\n" ); + break; + + case Mode::SetPowerLevel: + if ( !SendCommand( fd, buf, "WT+LEDE=%d", ( 0 == powerLevel ) ? 0 : 1 ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDE: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDE: got negative response ('%s')\n", buf ); + goto bail2; + } + + if ( !SendCommand( fd, buf, "WT+LEDS=%lu", powerLevel ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDS: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDS: got negative response ('%s')\n", buf ); + goto bail2; + } + + if ( duration > 0 ) { + if ( !SendCommand( fd, buf, "WT+LEDT=%lu", duration ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDT: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDT: got negative response ('%s')\n", buf ); + goto bail2; + } + } + // FALLTHROUGH + + case Mode::QueryPowerLevel: + if ( !SendCommand( fd, buf, "WT+LEDR" ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDR: failed to send command" ); + goto bail2; + } + if ( !IsGoodResponse( buf ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDR: got negative response ('%s')\n", buf ); + goto bail2; + } + if ( !StringToUnsignedLong( buf, 10, &powerLevel ) ) { + fprintf( stderr, "set-projector-power: command WT+LEDR: couldn't convert '%s' to a number\n", buf ); + goto bail2; + } + printf( "LED brightness: %lu\n", powerLevel ); + break; + } + + if ( monitor ) { + MonitorPowerAndTemperature( fd ); + } + + ret = 0; + +bail2: + close( fd ); + +bail1: + return ret; +} From 1da56f6a496f8180ce1cecfdc5f5b73f2f6532ef Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Thu, 9 Jan 2020 17:12:37 -0800 Subject: [PATCH 14/89] DLP4710: Adjust stuff for new projector. --- dlp4710/90-dlp4710.rules | 3 +++ dlp4710/set-projector-power.cpp | 2 +- install-lightfield.sh | 15 ++++++++------- make-deb-package.sh | 19 ++++++++----------- system-stuff/reset-lumen-projector-port | 4 ++++ system-stuff/set-projector-power.service | 2 +- uninstall-lightfield.sh | 2 +- 7 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 dlp4710/90-dlp4710.rules create mode 100755 system-stuff/reset-lumen-projector-port diff --git a/dlp4710/90-dlp4710.rules b/dlp4710/90-dlp4710.rules new file mode 100644 index 00000000..5b972838 --- /dev/null +++ b/dlp4710/90-dlp4710.rules @@ -0,0 +1,3 @@ +SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", GROUP="plugdev" +SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", KERNEL=="ttyUSB[0-9]*", MODE="0666", GROUP="plugdev", SYMLINK+="lumen-projector" +SUBSYSTEM=="usb", ATTRS{idVendor}=="27b1", ATTRS{idProduct}=="0001", KERNEL=="ttyACM[0-9]*", MODE="0666", GROUP="plugdev", SYMLINK+="lumen-arduino" diff --git a/dlp4710/set-projector-power.cpp b/dlp4710/set-projector-power.cpp index 1eebf439..96024eac 100644 --- a/dlp4710/set-projector-power.cpp +++ b/dlp4710/set-projector-power.cpp @@ -293,7 +293,7 @@ int main( int argc, char** argv ) { } } - fd = open( "/dev/ttyUSB0", O_RDWR ); + fd = open( "/dev/lumen-projector", O_RDWR ); if ( -1 == fd ) { perror( "set-projector-power: open" ); goto bail1; diff --git a/install-lightfield.sh b/install-lightfield.sh index 5ce1d738..6cc1fbf9 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -45,7 +45,7 @@ set -e PRINTRUN_SRC=/home/lumen/Volumetric/printrun LIGHTFIELD_SRC=/home/lumen/Volumetric/LightField MOUNTMON_SRC=${LIGHTFIELD_SRC}/mountmon -USBDRIVER_SRC=${LIGHTFIELD_SRC}/usb-driver +PROJECTORDRIVER_SRC=${LIGHTFIELD_SRC}/dlp4710 VERBOSE=-v CHXXXVERBOSE=-c @@ -76,9 +76,9 @@ done clear blue-bar • Building debugging version of set-projector-power -cd ${USBDRIVER_SRC} -[ -f set-projector-power ] && rm ${VERBOSE} -f set-projector-power -g++ -o set-projector-power -pipe -g -Og -D_DEBUG -std=gnu++1z -Wall -W -D_REENTRANT -fPIC dlpc350_usb.cpp dlpc350_api.cpp main.cpp -lhidapi-libusb +cd ${PROJECTORDRIVER_SRC} +[ -n "${FORCEREBUILD}" ] && make BUILD=debug clean +make BUILD=debug blue-bar • Building debugging version of Mountmon cd ${MOUNTMON_SRC} @@ -88,7 +88,7 @@ blue-bar • Building debugging version of LightField cd ${LIGHTFIELD_SRC} ./rebuild ${FORCEREBUILD} ${BUILDQUIETLY} -chown ${CHXXXVERBOSE} -R lumen:lumen ${USBDRIVER_SRC} +chown ${CHXXXVERBOSE} -R lumen:lumen ${PROJECTORDRIVER_SRC} chown ${CHXXXVERBOSE} -R lumen:lumen ${MOUNTMON_SRC}/build chown ${CHXXXVERBOSE} -R lumen:lumen ${LIGHTFIELD_SRC}/build @@ -113,13 +113,14 @@ install ${VERBOSE} -DT -m 600 -o lumen -g lumen gpg/pubring.gpg install ${VERBOSE} -DT -m 600 -o lumen -g lumen gpg/trustdb.gpg /home/lumen/.gnupg/trustdb.gpg install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service /lib/systemd/system/clean-up-mount-points.service install ${VERBOSE} -DT -m 644 system-stuff/set-projector-power.service /lib/systemd/system/set-projector-power.service -install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules /lib/udev/rules.d/90-dlpc350.rules +install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules /lib/udev/rules.d/90-dlp4710.rules install ${VERBOSE} -DT -m 755 build/lf /usr/bin/lf install ${VERBOSE} -DT -m 755 mountmon/build/mountmon /usr/bin/mountmon -install ${VERBOSE} -DT -m 755 usb-driver/set-projector-power /usr/bin/set-projector-power +install ${VERBOSE} -DT -m 755 dlp4710/set-projector-power /usr/bin/set-projector-power install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py /usr/share/lightfield/libexec/stdio-shepherd/printer.py install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py /usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port /usr/share/lightfield/libexec/reset-lumen-arduino-port +install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-projector-port /usr/share/lightfield/libexec/reset-lumen-projector-port install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf chmod 700 /home/lumen/.gnupg [ -f /home/lumen/.gnupg/pubring.kbx ] && rm ${VERBOSE} /home/lumen/.gnupg/pubring.kbx diff --git a/make-deb-package.sh b/make-deb-package.sh index 120e2297..915a8202 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -43,7 +43,7 @@ set -e PRINTRUN_SRC=/home/lumen/Volumetric/printrun LIGHTFIELD_SRC=/home/lumen/Volumetric/LightField MOUNTMON_SRC="${LIGHTFIELD_SRC}/mountmon" -USBDRIVER_SRC="${LIGHTFIELD_SRC}/usb-driver" +PROJECTORDRIVER_SRC="${LIGHTFIELD_SRC}/dlp4710" PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${VERSION}" DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" @@ -108,20 +108,16 @@ ln ${VERBOSE} -s "${PACKAGE_BUILD_DIR}" "${PACKAGE_BUILD_ROOT}/latest" ################################################## -cd "${USBDRIVER_SRC}" +cd "${PROJECTORDRIVER_SRC}" ################################################## blue-bar • Building "${BUILDTYPE}" version of set-projector-power -if [ "${BUILDTYPE}" = "debug" ] -then - OPTS="-g -Og -D_DEBUG" -elif [ "${BUILDTYPE}" = "release" ] -then - OPTS="-s -O3 -DNDEBUG" -fi -g++ -o "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" ${OPTS} -pipe -std=gnu++1z -Wall -W -D_GNU_SOURCE -fPIC dlpc350_usb.cpp dlpc350_api.cpp main.cpp -lhidapi-libusb +cd ${PROJECTORDRIVER_SRC} +[ -n "${FORCEREBUILD}" ] && make BUILD="${BUILDTYPE}" clean +make BUILD="${BUILDTYPE}" +install ${VERBOSE} -DT -m 755 dlp4710/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" ################################################## @@ -175,8 +171,9 @@ install ${VERBOSE} -DT -m 600 gpg/new-pubring.kbx "$ install ${VERBOSE} -DT -m 600 gpg/trustdb.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/trustdb.gpg" install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service "${LIGHTFIELD_FILES}/lib/systemd/system/clean-up-mount-points.service" install ${VERBOSE} -DT -m 644 system-stuff/set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" -install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlpc350.rules" +install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlp4710.rules" install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-arduino-port" +install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-projector-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-projector-port" install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/printer.py" install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py" install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" diff --git a/system-stuff/reset-lumen-projector-port b/system-stuff/reset-lumen-projector-port new file mode 100755 index 00000000..c6b3dec8 --- /dev/null +++ b/system-stuff/reset-lumen-projector-port @@ -0,0 +1,4 @@ +#!/bin/bash + +stty -F /dev/lumen-projector -a 1>/dev/null 2>&1 +sleep 1 diff --git a/system-stuff/set-projector-power.service b/system-stuff/set-projector-power.service index a5ecdcb4..9e0c8307 100644 --- a/system-stuff/set-projector-power.service +++ b/system-stuff/set-projector-power.service @@ -11,7 +11,7 @@ RequiresMountsFor=/usr Type=oneshot RemainAfterExit=yes User=root -ExecStart=/usr/bin/set-projector-power 0 +ExecStart=/usr/share/lightfield/libexec/reset-lumen-projector-port ExecStop=/usr/bin/set-projector-power 0 Restart=no diff --git a/uninstall-lightfield.sh b/uninstall-lightfield.sh index e6d9734c..890c0b20 100755 --- a/uninstall-lightfield.sh +++ b/uninstall-lightfield.sh @@ -29,7 +29,7 @@ rm -f \ /home/lumen/.gnupg/trustdb.gpg \ /lib/systemd/system/clean-up-mount-points.service \ /lib/systemd/system/set-projector-power.service \ - /lib/udev/rules.d/90-dlpc350.rules \ + /lib/udev/rules.d/90-dlp4710.rules \ /usr/bin/lf \ /usr/bin/mountmon \ /usr/bin/set-projector-power \ From cf39f116752f94d5d1e319df8233aa22502ff985 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Thu, 9 Jan 2020 17:20:33 -0800 Subject: [PATCH 15/89] Version 1.0.11.0. --- debian/changelog | 6 ++++++ debian/lightfield-common.install | 25 +++++++++++++------------ debian/lightfield-debug.postinst | 2 +- debian/lightfield-release.postinst | 2 +- install-lightfield.sh | 2 +- make-deb-package.sh | 2 +- make-upgrade-kit.sh | 2 +- src/version.h | 4 ++-- unpack-kit-manually.sh | 2 +- 9 files changed, 27 insertions(+), 20 deletions(-) diff --git a/debian/changelog b/debian/changelog index 950b8ec8..27d7ba5c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +lightfield (1.0.11.0) stable; urgency=medium + + * New: Support for DLP4710 projector. + + -- LightField packager Thu, 09 Jan 2020 17:18:06 -0800 + lightfield (1.0.10.0) stable; urgency=medium * Fixed: LightField will now notice a USB stick that was connected before it starts. diff --git a/debian/lightfield-common.install b/debian/lightfield-common.install index f45cf155..2b417a51 100644 --- a/debian/lightfield-common.install +++ b/debian/lightfield-common.install @@ -1,12 +1,13 @@ -files/etc/apt/trusted.gpg.d/volumetric-keyring.gpg etc/apt/trusted.gpg.d -files/etc/sudoers.d/lumen-lightfield etc/sudoers.d -files/home/lumen/.bash_profile home/lumen -files/home/lumen/.real_bash_profile home/lumen -files/home/lumen/.gnupg/pubring.gpg home/lumen/.gnupg -files/home/lumen/.gnupg/pubring.kbx home/lumen/.gnupg -files/home/lumen/.gnupg/trustdb.gpg home/lumen/.gnupg -files/lib/udev/rules.d/90-dlpc350.rules lib/udev/rules.d -files/usr/share/lightfield/libexec/reset-lumen-arduino-port usr/share/lightfield/libexec -files/usr/share/lightfield/libexec/printrun/* usr/share/lightfield/libexec/printrun -files/usr/share/lightfield/libexec/stdio-shepherd/* usr/share/lightfield/libexec/stdio-shepherd -files/usr/share/X11/xorg.conf.d/99-waveshare.conf usr/share/X11/xorg.conf.d +files/etc/apt/trusted.gpg.d/volumetric-keyring.gpg etc/apt/trusted.gpg.d +files/etc/sudoers.d/lumen-lightfield etc/sudoers.d +files/home/lumen/.bash_profile home/lumen +files/home/lumen/.real_bash_profile home/lumen +files/home/lumen/.gnupg/pubring.gpg home/lumen/.gnupg +files/home/lumen/.gnupg/pubring.kbx home/lumen/.gnupg +files/home/lumen/.gnupg/trustdb.gpg home/lumen/.gnupg +files/lib/udev/rules.d/90-dlp4710.rules lib/udev/rules.d +files/usr/share/lightfield/libexec/reset-lumen-arduino-port usr/share/lightfield/libexec +files/usr/share/lightfield/libexec/reset-lumen-projector-port usr/share/lightfield/libexec +files/usr/share/lightfield/libexec/printrun/* usr/share/lightfield/libexec/printrun +files/usr/share/lightfield/libexec/stdio-shepherd/* usr/share/lightfield/libexec/stdio-shepherd +files/usr/share/X11/xorg.conf.d/99-waveshare.conf usr/share/X11/xorg.conf.d diff --git a/debian/lightfield-debug.postinst b/debian/lightfield-debug.postinst index 00da6d2d..004b189d 100644 --- a/debian/lightfield-debug.postinst +++ b/debian/lightfield-debug.postinst @@ -23,7 +23,7 @@ case "$1" in configure) perl -lp -i -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null - echo "deb file:/var/lib/lightfield/software-updates/lightfield-debug_1.0.10.0_amd64 ./" > /etc/apt/sources.list.d/volumetric-lightfield.list + echo "deb file:/var/lib/lightfield/software-updates/lightfield-debug_1.0.11.0_amd64 ./" > /etc/apt/sources.list.d/volumetric-lightfield.list chown lumen:lumen /etc/apt/sources.list.d/volumetric-lightfield.list chmod 644 /etc/apt/sources.list.d/volumetric-lightfield.list diff --git a/debian/lightfield-release.postinst b/debian/lightfield-release.postinst index d5cd0902..20e51be7 100644 --- a/debian/lightfield-release.postinst +++ b/debian/lightfield-release.postinst @@ -23,7 +23,7 @@ case "$1" in configure) perl -lp -i -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null - echo "deb file:/var/lib/lightfield/software-updates/lightfield-release_1.0.10.0_amd64 ./" > /etc/apt/sources.list.d/volumetric-lightfield.list + echo "deb file:/var/lib/lightfield/software-updates/lightfield-release_1.0.11.0_amd64 ./" > /etc/apt/sources.list.d/volumetric-lightfield.list chown lumen:lumen /etc/apt/sources.list.d/volumetric-lightfield.list chmod 644 /etc/apt/sources.list.d/volumetric-lightfield.list diff --git a/install-lightfield.sh b/install-lightfield.sh index 6cc1fbf9..5a99809f 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION=1.0.10.0 +VERSION=1.0.11.0 ######################################################### ## ## diff --git a/make-deb-package.sh b/make-deb-package.sh index 915a8202..417d7dfe 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION=1.0.10.0 +VERSION=1.0.11.0 PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging ######################################################### diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index e7862166..28e1b51a 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION=1.0.10.0 +VERSION=1.0.11.0 PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging USE_KEY_SET=current diff --git a/src/version.h b/src/version.h index f6e269f1..a11e79fb 100644 --- a/src/version.h +++ b/src/version.h @@ -18,10 +18,10 @@ inline constexpr void DecodeVersionCode( unsigned const versionCode, int& major, build = static_cast( versionCode & 0xFFu ); } -char const* LIGHTFIELD_VERSION_STRING __attribute__(( weak )) = "1.0.10.0"; +char const* LIGHTFIELD_VERSION_STRING __attribute__(( weak )) = "1.0.11.0"; unsigned const LIGHTFIELD_VERSION_MAJOR = 1; unsigned const LIGHTFIELD_VERSION_MINOR = 0; -unsigned const LIGHTFIELD_VERSION_TEENY = 10; +unsigned const LIGHTFIELD_VERSION_TEENY = 11; unsigned const LIGHTFIELD_VERSION_BUILD = 0; unsigned const LIGHTFIELD_VERSION_CODE = MakeVersionCode( LIGHTFIELD_VERSION_MAJOR, LIGHTFIELD_VERSION_MINOR, LIGHTFIELD_VERSION_TEENY, LIGHTFIELD_VERSION_BUILD ); diff --git a/unpack-kit-manually.sh b/unpack-kit-manually.sh index 6a87c893..43d13eb5 100755 --- a/unpack-kit-manually.sh +++ b/unpack-kit-manually.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION=1.0.10.0 +VERSION=1.0.11.0 PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging ######################################################### From b8025bf056f2d745d695d9a844f7a33f80512097 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 10 Jan 2020 09:51:01 -0800 Subject: [PATCH 16/89] Update dimensions of projector display. --- src/constants.cpp | 20 ++++++++++---------- system-stuff/99-waveshare.conf | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/constants.cpp b/src/constants.cpp index 5cb756af..df841fc4 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -1,19 +1,19 @@ #include "pch.h" #include "constants.h" -QRegularExpression const EndsWithWhitespaceRegex { "\\s+$" }; -QRegularExpression const NewLineRegex { "\\r?\\n" }; +QRegularExpression const EndsWithWhitespaceRegex { "\\s+$" }; +QRegularExpression const NewLineRegex { "\\r?\\n" }; -QSize const SmallMainWindowSize { 800, 480 }; -QSize const SmallMainButtonSize { 218, 74 }; -QSize const SmallMaximalRightHandPaneSize { 564, 424 }; +QSize const SmallMainWindowSize { 800, 480 }; +QSize const SmallMainButtonSize { 218, 74 }; +QSize const SmallMaximalRightHandPaneSize { 564, 424 }; -QSize MainWindowSize { 1024, 600 }; -QSize MainButtonSize { 279, 93 }; -QSize MaximalRightHandPaneSize { 722, 530 }; +QSize MainWindowSize { 1024, 600 }; +QSize MainButtonSize { 279, 93 }; +QSize MaximalRightHandPaneSize { 722, 530 }; -QSize const ButtonPadding { 20, 4 }; -QSize const ProjectorWindowSize { 1280, 800 }; +QSize const ButtonPadding { 20, 4 }; +QSize const ProjectorWindowSize { 1920, 1080 }; QString const AptSourcesFilePath { "/etc/apt/sources.list.d/volumetric-lightfield.list" }; QString const JobWorkingDirectoryPath { "/var/cache/lightfield/print-jobs" }; diff --git a/system-stuff/99-waveshare.conf b/system-stuff/99-waveshare.conf index abbe72cf..3f63e5c8 100644 --- a/system-stuff/99-waveshare.conf +++ b/system-stuff/99-waveshare.conf @@ -11,7 +11,7 @@ Section "Monitor" Identifier "DP-1" Modeline "1024x600_60.0" 49.00 1024 1029 1042 1312 600 602 605 622 -hsync -vsync Option "PreferredMode" "1024x600_60.0" - Option "Position" "0 800" + Option "Position" "0 1080" Option "DPMS" "false" Option "Primary" "true" EndSection @@ -19,8 +19,8 @@ EndSection Section "Monitor" # Projector Identifier "DP-2" - Modeline "1280x800_60.0" 83.50 1280 1352 1480 1680 800 810 816 831 +hsync +vsync - Option "PreferredMode" "1280x800_60.0" + Modeline "1920x1080_60.0" 148.50 1920 2008 2096 2200 1080 1084 1089 1125 -hsync -vsync + Option "PreferredMode" "1920x1080_60.0" Option "Position" "0 0" Option "DPMS" "false" Option "Primary" "false" @@ -33,7 +33,7 @@ Section "Screen" DefaultDepth 24 SubSection "Display" Depth 24 - Virtual 1280 1400 # ( max(1280,1024), (800+600) ) + Virtual 1920 1680 # ( max(1920,1024), (1080+600) ) EndSubSection EndSection From 04bb5a320350cc270202b8d73ee9271a5b0a94b5 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 10 Jan 2020 10:05:42 -0800 Subject: [PATCH 17/89] DLP4710: Small fixes. --- dlp4710/Makefile | 2 +- make-deb-package.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dlp4710/Makefile b/dlp4710/Makefile index b91086b7..58a25106 100644 --- a/dlp4710/Makefile +++ b/dlp4710/Makefile @@ -8,7 +8,7 @@ TARGET = set-projector-power SOURCES = set-projector-power.cpp OBJECTS = $(SOURCES:.cpp=.o) -CFLAGS = -std=gnu++17 -Wall -Wextra -Werror -pedantic +CFLAGS = -std=gnu++17 -Wall -Wextra -pedantic LFLAGS = ifeq ($(BUILD),debug) CFLAGS += -D_DEBUG -g -Og diff --git a/make-deb-package.sh b/make-deb-package.sh index 417d7dfe..63063394 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -28,9 +28,9 @@ Usage: $(basename "$0") [-q] BUILDTYPE Where: -q build quietly -X don't force rebuild BUILDTYPE is one of - release create a release-build kit - debug create a debug-build kit - both create both kits + release create release-build package set + debug create debug-build package set + both create both package sets If the build is successful, the requested package set(s) will be found in ${DEB_BUILD_DIR}/ @@ -117,7 +117,7 @@ blue-bar • Building "${BUILDTYPE}" version of set-projector-power cd ${PROJECTORDRIVER_SRC} [ -n "${FORCEREBUILD}" ] && make BUILD="${BUILDTYPE}" clean make BUILD="${BUILDTYPE}" -install ${VERBOSE} -DT -m 755 dlp4710/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" +install ${VERBOSE} -DT -m 755 set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" ################################################## From c9a61e63314c4be3c34fb7a48b8b4dc9cc0876db Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 10 Jan 2020 16:08:52 -0800 Subject: [PATCH 18/89] Fix main window position. --- src/app.cpp | 2 ++ src/app.h | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 4f822f2a..0da1ba67 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -109,6 +109,8 @@ void App::_parseCommandLine( ) { if ( MoveMainWindow ) { g_settings.mainWindowPosition.setY( 0 ); g_settings.projectorWindowPosition.setY( MainWindowSize.height( ) ); + } else { + g_settings.mainWindowPosition.setY( ProjectorWindowSize.height( ) ); } } diff --git a/src/app.h b/src/app.h index 09664627..5261a92a 100644 --- a/src/app.h +++ b/src/app.h @@ -12,9 +12,9 @@ class AppSettings { public: - QPoint mainWindowPosition { 0, 800 }; - QPoint projectorWindowPosition { 0, 0 }; - QPoint projectorOffset { 0, 0 }; + QPoint mainWindowPosition { 0, 0 }; + QPoint projectorWindowPosition { 0, 0 }; + QPoint projectorOffset { 0, 0 }; Theme theme { }; bool frameless { false }; From 4bc21da15800a488243d9d54ce32677d11184b8f Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 10 Jan 2020 16:44:09 -0800 Subject: [PATCH 19/89] Adjust touchscreen transformation matrix. --- system-stuff/99-waveshare.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-stuff/99-waveshare.conf b/system-stuff/99-waveshare.conf index 3f63e5c8..91a67d83 100644 --- a/system-stuff/99-waveshare.conf +++ b/system-stuff/99-waveshare.conf @@ -3,7 +3,7 @@ Section "InputClass" MatchIsTouchscreen "on" MatchUSBID "0eef:0005" Driver "libinput" - Option "TransformationMatrix" "0.8 0 0 0 0.428571 0.571429 0 0 1" + Option "TransformationMatrix" "0.5333333 0 0 0 0.3571429 0.6428571 0 0 1" EndSection Section "Monitor" From 1a2f7b0916983fa79800d6ae53ca524e05f6e0ea Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 11 Jan 2020 09:38:53 -0800 Subject: [PATCH 20/89] DLP4710: Tweak udev rules. --- dlp4710/90-dlp4710.rules | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlp4710/90-dlp4710.rules b/dlp4710/90-dlp4710.rules index 5b972838..33fb2cc3 100644 --- a/dlp4710/90-dlp4710.rules +++ b/dlp4710/90-dlp4710.rules @@ -1,3 +1,3 @@ -SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", GROUP="plugdev" -SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", KERNEL=="ttyUSB[0-9]*", MODE="0666", GROUP="plugdev", SYMLINK+="lumen-projector" -SUBSYSTEM=="usb", ATTRS{idVendor}=="27b1", ATTRS{idProduct}=="0001", KERNEL=="ttyACM[0-9]*", MODE="0666", GROUP="plugdev", SYMLINK+="lumen-arduino" +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", KERNEL=="ttyUSB[0-9]*", MODE="0666", GROUP="plugdev", SYMLINK+="lumen-projector" +ATTRS{idVendor}=="27b1", ATTRS{idProduct}=="0001", KERNEL=="ttyACM[0-9]*", MODE="0666", GROUP="plugdev", SYMLINK+="lumen-arduino" From e9931005b643f5ae40538302c00ea4b811f81613 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 11 Jan 2020 15:55:34 -0800 Subject: [PATCH 21/89] Rotate keys. --- gpg/new-pubring.gpg | Bin 9378 -> 0 bytes gpg/old-pubring.gpg | Bin 0 -> 4689 bytes gpg/pubring.gpg | Bin 4689 -> 9378 bytes gpg/{new-pubring.kbx => pubring.kbx} | Bin gpg/trustdb.gpg | Bin 1520 -> 1520 bytes make-upgrade-kit.sh | 10 ++++------ 6 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 gpg/new-pubring.gpg create mode 100644 gpg/old-pubring.gpg rename gpg/{new-pubring.kbx => pubring.kbx} (100%) diff --git a/gpg/new-pubring.gpg b/gpg/new-pubring.gpg deleted file mode 100644 index 7682049bb6ee5aa018ab3f41b96d5c1f03d4d37c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9378 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 Xn*5hTMjSN1_DIRgV4$M^4_5vIQ!lB| literal 0 HcmV?d00001 diff --git a/gpg/pubring.gpg b/gpg/pubring.gpg index 041ebb2015a374f4b8473dc26b1f9fa94c94f704..7682049bb6ee5aa018ab3f41b96d5c1f03d4d37c 100644 GIT binary patch delta 4489 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 delta 32 ocmeys{efGAF})z2nVFH5k%@sJcD=h|953hfiH5=()lael0Eux3lK=n! diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index 28e1b51a..20fb7488 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -48,14 +48,12 @@ LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" KIT_DIR="${PACKAGE_BUILD_DIR}/kit" -if [ "${USE_KEY_SET}" = "current" ]; then - #REPO_KEY_ID=18DDFE4E607507208C9F6E6582768C36BD8725D2 - #PKG_KEY_ID=0EF6486549978C0C76B49E99C9FC781B66B69981 - REPO_KEY_ID=lightfield-repo-maint@volumetricbio.com - PKG_KEY_ID=lightfield-packager@volumetricbio.com -elif [ "${USE_KEY_SET}" = "future" ]; then +if [ "${USE_KEY_SET}" = "current" ] +then REPO_KEY_ID=E91BD3361F39D49C78B1E3A2B55C0E8D4B632A66 PKG_KEY_ID=78DAD29978EB392992D7FE0423025033D9E840F7 +#elif [ "${USE_KEY_SET}" = "future" ] +#then else red-bar "The key set '${USE_KEY_SET}' is unrecognized." fi From dfe9f72a7edc60de33e28ffb12d6cdcc023c5397 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 11 Jan 2020 15:55:34 -0800 Subject: [PATCH 22/89] Rotate keys. --- gpg/new-pubring.gpg | Bin 9378 -> 0 bytes gpg/old-pubring.gpg | Bin 0 -> 4689 bytes gpg/pubring.gpg | Bin 4689 -> 9378 bytes gpg/{new-pubring.kbx => pubring.kbx} | Bin gpg/trustdb.gpg | Bin 1520 -> 1520 bytes make-deb-package.sh | 6 +++--- make-upgrade-kit.sh | 10 ++++------ 7 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 gpg/new-pubring.gpg create mode 100644 gpg/old-pubring.gpg rename gpg/{new-pubring.kbx => pubring.kbx} (100%) diff --git a/gpg/new-pubring.gpg b/gpg/new-pubring.gpg deleted file mode 100644 index 7682049bb6ee5aa018ab3f41b96d5c1f03d4d37c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9378 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 Xn*5hTMjSN1_DIRgV4$M^4_5vIQ!lB| literal 0 HcmV?d00001 diff --git a/gpg/pubring.gpg b/gpg/pubring.gpg index 041ebb2015a374f4b8473dc26b1f9fa94c94f704..7682049bb6ee5aa018ab3f41b96d5c1f03d4d37c 100644 GIT binary patch delta 4489 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 delta 32 ocmeys{efGAF})z2nVFH5k%@sJcD=h|953hfiH5=()lael0Eux3lK=n! diff --git a/make-deb-package.sh b/make-deb-package.sh index 63063394..92ba6444 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -161,13 +161,13 @@ install ${VERBOSE} -DT -m 755 build/lf "${LIGHTFIELD_FILES}/usr/bin/lf" blue-bar • Copying files into packaging directory install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock "${LIGHTFIELD_FILES}/etc/apt/apt.conf.d/99untrustworthy-clock" -install ${VERBOSE} -DT -m 644 gpg/new-pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" +install ${VERBOSE} -DT -m 644 gpg/pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" install ${VERBOSE} -DT -m 440 system-stuff/lumen-lightfield "${LIGHTFIELD_FILES}/etc/sudoers.d/lumen-lightfield" install ${VERBOSE} -DT -m 644 system-stuff/getty@tty1.service.d_override.conf "${LIGHTFIELD_FILES}/etc/systemd/system/getty@tty1.service.d/override.conf" install ${VERBOSE} -DT -m 600 system-stuff/lumen-bash_profile "${LIGHTFIELD_FILES}/home/lumen/.bash_profile" install ${VERBOSE} -DT -m 600 system-stuff/lumen-real_bash_profile "${LIGHTFIELD_FILES}/home/lumen/.real_bash_profile" -install ${VERBOSE} -DT -m 600 gpg/new-pubring.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.gpg" -install ${VERBOSE} -DT -m 600 gpg/new-pubring.kbx "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.kbx" +install ${VERBOSE} -DT -m 600 gpg/pubring.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.gpg" +install ${VERBOSE} -DT -m 600 gpg/pubring.kbx "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.kbx" install ${VERBOSE} -DT -m 600 gpg/trustdb.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/trustdb.gpg" install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service "${LIGHTFIELD_FILES}/lib/systemd/system/clean-up-mount-points.service" install ${VERBOSE} -DT -m 644 system-stuff/set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index 28e1b51a..20fb7488 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -48,14 +48,12 @@ LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" KIT_DIR="${PACKAGE_BUILD_DIR}/kit" -if [ "${USE_KEY_SET}" = "current" ]; then - #REPO_KEY_ID=18DDFE4E607507208C9F6E6582768C36BD8725D2 - #PKG_KEY_ID=0EF6486549978C0C76B49E99C9FC781B66B69981 - REPO_KEY_ID=lightfield-repo-maint@volumetricbio.com - PKG_KEY_ID=lightfield-packager@volumetricbio.com -elif [ "${USE_KEY_SET}" = "future" ]; then +if [ "${USE_KEY_SET}" = "current" ] +then REPO_KEY_ID=E91BD3361F39D49C78B1E3A2B55C0E8D4B632A66 PKG_KEY_ID=78DAD29978EB392992D7FE0423025033D9E840F7 +#elif [ "${USE_KEY_SET}" = "future" ] +#then else red-bar "The key set '${USE_KEY_SET}' is unrecognized." fi From 8f945ef23c05f7780ba16f58f4b7af480653c8d1 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sun, 12 Jan 2020 13:30:52 -0800 Subject: [PATCH 23/89] Update .gitignore . --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 81c9421e..2a7e238a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ copy-to-usb-stick.sh deps-junk-1.txt deps-junk-2.txt deps-junk-3.txt +dlp4710/set-projector-power +dlp4710/set-projector-power.o do-it.sh gpg/gpg-agent.conf gpg/openpgp-revocs.d From 03ed3bf6814b72263d8f3b76af9f51ab93d5b930 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sun, 12 Jan 2020 13:35:21 -0800 Subject: [PATCH 24/89] Debian packaging: Do first-time DLP4710 projector configuration from the post-install script. --- debian/lightfield-debug.postinst | 2 ++ debian/lightfield-release.postinst | 2 ++ 2 files changed, 4 insertions(+) diff --git a/debian/lightfield-debug.postinst b/debian/lightfield-debug.postinst index 004b189d..effe19cc 100644 --- a/debian/lightfield-debug.postinst +++ b/debian/lightfield-debug.postinst @@ -32,6 +32,8 @@ case "$1" in systemctl enable --now set-projector-power.service clean-up-mount-points.service systemctl enable getty@tty1.service systemctl daemon-reload + + set-projector-power --first-time ;; abort-upgrade|abort-remove|abort-deconfigure) diff --git a/debian/lightfield-release.postinst b/debian/lightfield-release.postinst index 20e51be7..3b70fd11 100644 --- a/debian/lightfield-release.postinst +++ b/debian/lightfield-release.postinst @@ -32,6 +32,8 @@ case "$1" in systemctl enable --now set-projector-power.service clean-up-mount-points.service systemctl enable getty@tty1.service systemctl daemon-reload + + set-projector-power --first-time ;; abort-upgrade|abort-remove|abort-deconfigure) From 83579e551089ab74c22bfc1d15a7c332d3c6a98e Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 13 Jan 2020 16:16:51 -0800 Subject: [PATCH 25/89] version.h shouldn't be under version control, because it's generated from version.h.in by change-version-number.sh. --- src/version.h | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 src/version.h diff --git a/src/version.h b/src/version.h deleted file mode 100644 index f6e269f1..00000000 --- a/src/version.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef __VERSION_H__ -#define __VERSION_H__ - -enum class BuildType { - unknown, - Release, - Debug, -}; - -inline constexpr unsigned MakeVersionCode( unsigned const major, unsigned const minor, unsigned const teeny, unsigned const build = 0 ) { - return ( ( major & 0xFFu ) << 24u ) | ( ( minor & 0xFFu ) << 16u ) | ( ( teeny & 0xFFu ) << 8u ) | ( build & 0xFFu ); -} - -inline constexpr void DecodeVersionCode( unsigned const versionCode, int& major, int& minor, int& teeny, int& build ) { - major = static_cast( ( versionCode >> 24u ) & 0xFFu ); - minor = static_cast( ( versionCode >> 16u ) & 0xFFu ); - teeny = static_cast( ( versionCode >> 8u ) & 0xFFu ); - build = static_cast( versionCode & 0xFFu ); -} - -char const* LIGHTFIELD_VERSION_STRING __attribute__(( weak )) = "1.0.10.0"; -unsigned const LIGHTFIELD_VERSION_MAJOR = 1; -unsigned const LIGHTFIELD_VERSION_MINOR = 0; -unsigned const LIGHTFIELD_VERSION_TEENY = 10; -unsigned const LIGHTFIELD_VERSION_BUILD = 0; -unsigned const LIGHTFIELD_VERSION_CODE = MakeVersionCode( LIGHTFIELD_VERSION_MAJOR, LIGHTFIELD_VERSION_MINOR, LIGHTFIELD_VERSION_TEENY, LIGHTFIELD_VERSION_BUILD ); - -#if defined _DEBUG -BuildType const LIGHTFIELD_VERSION_BUILD_TYPE = BuildType::Debug; -#elif defined NDEBUG -BuildType const LIGHTFIELD_VERSION_BUILD_TYPE = BuildType::Release; -#else -# error Unknown build type: Neither _DEBUG nor NDEBUG are #define:d. -#endif - -#endif // __VERSION_H__ From c4cf52d8063f2bc61d367c3d81de91c43cfc6b93 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 13 Jan 2020 20:09:30 -0800 Subject: [PATCH 26/89] More work on multiple release trains. --- change-version-number.sh | 51 +++++++++++++++++++++++----------------- install-lightfield.sh | 5 ++-- make-deb-package.sh | 8 +++---- make-upgrade-kit.sh | 9 +++---- 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/change-version-number.sh b/change-version-number.sh index 7b37497c..a5af2552 100755 --- a/change-version-number.sh +++ b/change-version-number.sh @@ -18,44 +18,45 @@ function usage () { Usage: $(basename "$0") [-t ] Changes the LightField version in all the relevant places in the source. + -a Sets the architecture. Default: ${DEFAULT_ARCHITECTURE} -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} HERE exit 1 } function apply-atsign-substitution () { - local CODE='\e[0;30;48;2;146;208;80m»' - local RST='«\e[0m' + local CODE='\e[0;30;48;2;146;208;80m' + local RST='\e[0m' # shellcheck disable=SC2145 - echo "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" - perl -ilp -e "s/\\@\\@${1}\\@\\@/${2}/g;" "${@:3}" + echo -e "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" + sed -i "s/@@${1}@@/${2}/g" "${@:3}" } function apply-assignment-substitution () { - local CODE='\e[0;30;48;2;0;146;208m»' - local RST='«\e[0m' + local CODE='\e[0;30;48;2;0;146;208m' + local RST='\e[0m' # shellcheck disable=SC2145 - echo "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" - perl -ilp -e "s/^(\\s*)${1}=.*\$/\$1${1}=${2}/g;" "${@:3}" + echo -e "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" + sed -i 's/^'"${1}"'=.*$/'"${1}"'='"${2}"'/g' "${@:3}" } LIGHTFIELD_ROOT="/home/lumen/Volumetric/LightField" LIGHTFIELD_SRC="${LIGHTFIELD_ROOT}/src" DEFAULT_RELEASE_TRAIN=base +DEFAULT_ARCHITECTURE=$(uname -m) +if [ "${DEFAULT_ARCHITECTURE}" = "x86_64" ] +then + DEFAULT_ARCHITECTURE=amd64 +fi -ARCHITECTURE=$(uname -m) +ARCHITECTURE=${DEFAULT_ARCHITECTURE} RELEASE_TRAIN=${DEFAULT_RELEASE_TRAIN} VERSION= -if [ "${ARCHITECTURE}" = "x86_64" ] -then - ARCHITECTURE=amd64 -fi - ################################################# -ARGS=$(getopt -n 'change-version-number.sh' -o 't:' -- "${@}") +ARGS=$(getopt -n 'change-version-number.sh' -o 'a:t:' -- "${@}") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] then @@ -66,6 +67,16 @@ eval set -- "$ARGS" while [ -n "${1}" ] do case "${1}" in + '-a') + if [ "${2}" != "amd64" ] && [ "${2}" != "arm7l" ] + then + red-bar "Unrecognized argument to -a switch; valid values are amd64 and arm7l." + usage + fi + ARCHITECTURE="${2}" + shift + ;; + '-t') RELEASE_TRAIN="${2}" shift @@ -137,17 +148,13 @@ apply-atsign-substitution VERSION_TEENY "${VER[2]}" version.h apply-atsign-substitution VERSION_BUILD "${VER[3]}" version.h apply-atsign-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" version.h -# TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO -exit 69 - # shellcheck disable=SC2164 cd "${LIGHTFIELD_ROOT}" blue-bar 'Updating build and packaging scripts' -apply-assignment-substitution ARCHITECTURE "${ARCHITECTURE}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh unpack-kit-manually.sh -apply-assignment-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh unpack-kit-manually.sh -apply-assignment-substitution VERSION "${STRINGVER}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh unpack-kit-manually.sh -perl -lpi -e "s/(amd64|arm7l)/${ARCHITECTURE}/g" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh unpack-kit-manually.sh +apply-assignment-substitution ARCHITECTURE "${ARCHITECTURE}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh +apply-assignment-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh +apply-assignment-substitution VERSION "${STRINGVER}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh blue-bar 'Done!' diff --git a/install-lightfield.sh b/install-lightfield.sh index fedeedeb..4d2c7b23 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -1,6 +1,8 @@ #!/bin/bash VERSION=1.0.10.0 +RELEASE_TRAIN=base +ARCHITECTURE=amd64 ######################################################### ## ## @@ -51,9 +53,6 @@ CHXXXVERBOSE=-c FORCEREBUILD= BUILDQUIETLY= -RELEASE_TRAIN=base -ARCHITECTURE=amd64 - ARGS=$(getopt -n 'install-lightfield.sh' -o 'qxa:t:' -- "${@}") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] diff --git a/make-deb-package.sh b/make-deb-package.sh index acea1cc1..26e4f145 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -1,7 +1,9 @@ #!/bin/bash VERSION=1.0.10.0 - +BUILDTYPE= +ARCHITECTURE=amd64 +RELEASE_TRAIN=base PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging ######################################################### @@ -68,10 +70,6 @@ VERBOSE=-v CHXXXVERBOSE=-c FORCEREBUILD=-x -BUILDTYPE= -ARCHITECTURE=$(uname -m) -RELEASE_TRAIN=base - ARGS=$(getopt -n 'make-deb-package.sh' -o 'qXa:t:' -- "$@") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index 287dfc30..b5502038 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -1,8 +1,9 @@ #!/bin/bash -RELEASE_TRAIN=base VERSION=1.0.10.0 - +BUILDTYPE= +ARCHITECTURE=amd64 +RELEASE_TRAIN=base PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging USE_KEY_SET=current @@ -57,10 +58,6 @@ fi VERBOSE=-v -BUILDTYPE= -ARCHITECTURE=$(uname -m) -RELEASE_TRAIN=base - ARGS=$(getopt -n 'make-upgrade-kit.sh' -o 'qa:t:' -- "$@") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] From f56c1f37cb057f74d8e758660a8cb870795d343a Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 11 Jan 2020 15:55:34 -0800 Subject: [PATCH 27/89] Rotate keys. --- gpg/new-pubring.gpg | Bin 9378 -> 0 bytes gpg/old-pubring.gpg | Bin 0 -> 4689 bytes gpg/pubring.gpg | Bin 4689 -> 9378 bytes gpg/{new-pubring.kbx => pubring.kbx} | Bin gpg/trustdb.gpg | Bin 1520 -> 1520 bytes make-upgrade-kit.sh | 18 ++++++++++++------ 6 files changed, 12 insertions(+), 6 deletions(-) delete mode 100644 gpg/new-pubring.gpg create mode 100644 gpg/old-pubring.gpg rename gpg/{new-pubring.kbx => pubring.kbx} (100%) diff --git a/gpg/new-pubring.gpg b/gpg/new-pubring.gpg deleted file mode 100644 index 7682049bb6ee5aa018ab3f41b96d5c1f03d4d37c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9378 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 Xn*5hTMjSN1_DIRgV4$M^4_5vIQ!lB| literal 0 HcmV?d00001 diff --git a/gpg/pubring.gpg b/gpg/pubring.gpg index 041ebb2015a374f4b8473dc26b1f9fa94c94f704..7682049bb6ee5aa018ab3f41b96d5c1f03d4d37c 100644 GIT binary patch delta 4489 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 delta 32 ocmeys{efGAF})z2nVFH5k%@sJcD=h|953hfiH5=()lael0Eux3lK=n! diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index b5502038..62ebb579 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -44,14 +44,20 @@ EOF trap error-trap ERR set -e -if [ "${USE_KEY_SET}" = "current" ]; then - #REPO_KEY_ID=18DDFE4E607507208C9F6E6582768C36BD8725D2 - #PKG_KEY_ID=0EF6486549978C0C76B49E99C9FC781B66B69981 - REPO_KEY_ID=lightfield-repo-maint@volumetricbio.com - PKG_KEY_ID=lightfield-packager@volumetricbio.com -elif [ "${USE_KEY_SET}" = "future" ]; then +LIGHTFIELD_SRC="/home/lumen/Volumetric/LightField" +PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${VERSION}" +DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" +LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" +LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" + +KIT_DIR="${PACKAGE_BUILD_DIR}/kit" + +if [ "${USE_KEY_SET}" = "current" ] +then REPO_KEY_ID=E91BD3361F39D49C78B1E3A2B55C0E8D4B632A66 PKG_KEY_ID=78DAD29978EB392992D7FE0423025033D9E840F7 +#elif [ "${USE_KEY_SET}" = "future" ] +#then else red-bar "The key set '${USE_KEY_SET}' is unrecognized." fi From 6a19ebc97f281ff4c88108564ceb97123db2901a Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 11 Jan 2020 15:55:34 -0800 Subject: [PATCH 28/89] Rotate keys. --- make-deb-package.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/make-deb-package.sh b/make-deb-package.sh index 26e4f145..20374feb 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -217,13 +217,13 @@ install ${VERBOSE} -DT -m 755 build/lf "${LIGHTFIELD_FILES}/usr/bin/lf" blue-bar "• Copying LightField files into packaging directory" install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock "${LIGHTFIELD_FILES}/etc/apt/apt.conf.d/99untrustworthy-clock" -install ${VERBOSE} -DT -m 644 gpg/new-pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" +install ${VERBOSE} -DT -m 644 gpg/pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" install ${VERBOSE} -DT -m 440 system-stuff/lumen-lightfield "${LIGHTFIELD_FILES}/etc/sudoers.d/lumen-lightfield" install ${VERBOSE} -DT -m 644 system-stuff/getty@tty1.service.d_override.conf "${LIGHTFIELD_FILES}/etc/systemd/system/getty@tty1.service.d/override.conf" install ${VERBOSE} -DT -m 600 system-stuff/lumen-bash_profile "${LIGHTFIELD_FILES}/home/lumen/.bash_profile" install ${VERBOSE} -DT -m 600 system-stuff/lumen-real_bash_profile "${LIGHTFIELD_FILES}/home/lumen/.real_bash_profile" -install ${VERBOSE} -DT -m 600 gpg/new-pubring.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.gpg" -install ${VERBOSE} -DT -m 600 gpg/new-pubring.kbx "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.kbx" +install ${VERBOSE} -DT -m 600 gpg/pubring.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.gpg" +install ${VERBOSE} -DT -m 600 gpg/pubring.kbx "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.kbx" install ${VERBOSE} -DT -m 600 gpg/trustdb.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/trustdb.gpg" install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service "${LIGHTFIELD_FILES}/lib/systemd/system/clean-up-mount-points.service" install ${VERBOSE} -DT -m 644 system-stuff/set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" From 3509b780a4ab2719d529d52c1167473bafbdc969 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 14 Jan 2020 10:14:56 -0800 Subject: [PATCH 29/89] make-upgrade-kit.sh: Rearrange variables; eliminate duplicate initializations. --- make-upgrade-kit.sh | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index 62ebb579..afe84e4e 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -4,6 +4,7 @@ VERSION=1.0.10.0 BUILDTYPE= ARCHITECTURE=amd64 RELEASE_TRAIN=base +LIGHTFIELD_SRC=/home/lumen/Volumetric/LightField PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging USE_KEY_SET=current @@ -44,14 +45,6 @@ EOF trap error-trap ERR set -e -LIGHTFIELD_SRC="/home/lumen/Volumetric/LightField" -PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${VERSION}" -DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" -LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" -LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" - -KIT_DIR="${PACKAGE_BUILD_DIR}/kit" - if [ "${USE_KEY_SET}" = "current" ] then REPO_KEY_ID=E91BD3361F39D49C78B1E3A2B55C0E8D4B632A66 @@ -110,11 +103,6 @@ else SUFFIX=${RELEASE_TRAIN}-${BUILDTYPE} fi -LIGHTFIELD_SRC="/home/lumen/Volumetric/LightField" -PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" -DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" -KIT_DIR="${PACKAGE_BUILD_DIR}/kit" - if [ "${BUILDTYPE}" = "both" ] then "${0}" "${ARGS}" release || exit $? @@ -122,6 +110,9 @@ then exit 0 fi +PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" +DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" +KIT_DIR="${PACKAGE_BUILD_DIR}/kit" REPO_DIR="${PACKAGE_BUILD_DIR}/repo" RELEASEDATE=$(date "+%Y-%m-%d") From 28b03f07166e41278ae68247effad24744964260 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 14 Jan 2020 11:14:43 -0800 Subject: [PATCH 30/89] make-deb-package.sh: Update apply-*-substitution functions. --- make-deb-package.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/make-deb-package.sh b/make-deb-package.sh index 20374feb..ecbf46aa 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -43,19 +43,19 @@ EOF } function apply-atsign-substitution () { - local CODE='\e[0;30;48;2;146;208;80m»' - local RST='«\e[0m' + local CODE='\e[0;30;48;2;146;208;80m' + local RST='\e[0m' # shellcheck disable=SC2145 - echo "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" - perl -ilp -e "s/\\@\\@${1}\\@\\@/${2}/g;" "${@:3}" + echo -e "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" + sed -i "s/@@${1}@@/${2}/g" "${@:3}" } function apply-assignment-substitution () { - local CODE='\e[0;30;48;2;0;146;208m»' - local RST='«\e[0m' + local CODE='\e[0;30;48;2;0;146;208m' + local RST='\e[0m' # shellcheck disable=SC2145 - echo "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" - perl -ilp -e "s/^(\\s*)${1}=.*\$/\$1${1}=${2}/g;" "${@:3}" + echo -e "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" + sed -i 's/^'"${1}"'=.*$/'"${1}"'='"${2}"'/g' "${@:3}" } trap error-trap ERR From c18c8b0d3961291763b94ea84768a921dc33b662 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Thu, 16 Jan 2020 18:45:28 -0800 Subject: [PATCH 31/89] Multiple release trains: This ought to be it! --- change-version-number.sh | 76 ++------ ...ghtfield.install.in => lightfield.install} | 0 debian/lightfield.postinst.in | 8 +- ...lightfield.postrm.in => lightfield.postrm} | 2 +- ...ghtfield.preinst.in => lightfield.preinst} | 2 +- .../{lightfield.prerm.in => lightfield.prerm} | 2 +- install-lightfield.sh | 83 +++----- make-deb-package.sh | 184 +++++++++--------- make-upgrade-kit.sh | 106 +++++----- shared-stuff.sh | 49 +++++ usb-driver/Makefile | 36 ++++ 11 files changed, 288 insertions(+), 260 deletions(-) rename debian/{lightfield.install.in => lightfield.install} (100%) rename debian/{lightfield.postrm.in => lightfield.postrm} (94%) rename debian/{lightfield.preinst.in => lightfield.preinst} (91%) rename debian/{lightfield.prerm.in => lightfield.prerm} (94%) create mode 100644 shared-stuff.sh create mode 100644 usb-driver/Makefile diff --git a/change-version-number.sh b/change-version-number.sh index a5af2552..a6b0933f 100755 --- a/change-version-number.sh +++ b/change-version-number.sh @@ -1,62 +1,30 @@ #!/bin/bash +# shellcheck disable=SC2164 -function blue-bar () { - echo -e "\r\e[1;37;44m$*\e[K\e[0m" 1>&2 -} +LIGHTFIELD_ROOT="/home/lumen/Volumetric/LightField" -function red-bar () { - echo -e "\r\e[1;33;41m$*\e[K\e[0m" 1>&2 -} - -function error-trap () { - red-bar 'Failed!' - exit 1 -} +######################################################### +## ## +## No user-serviceable parts below this point. ## +## ## +######################################################### function usage () { cat 1>&2 <] Changes the LightField version in all the relevant places in the source. - -a Sets the architecture. Default: ${DEFAULT_ARCHITECTURE} -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} + Version number. If the fourth element is omitted, + it is defaulted to 0. HERE exit 1 } -function apply-atsign-substitution () { - local CODE='\e[0;30;48;2;146;208;80m' - local RST='\e[0m' - # shellcheck disable=SC2145 - echo -e "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" - sed -i "s/@@${1}@@/${2}/g" "${@:3}" -} - -function apply-assignment-substitution () { - local CODE='\e[0;30;48;2;0;146;208m' - local RST='\e[0m' - # shellcheck disable=SC2145 - echo -e "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" - sed -i 's/^'"${1}"'=.*$/'"${1}"'='"${2}"'/g' "${@:3}" -} - -LIGHTFIELD_ROOT="/home/lumen/Volumetric/LightField" -LIGHTFIELD_SRC="${LIGHTFIELD_ROOT}/src" - -DEFAULT_RELEASE_TRAIN=base -DEFAULT_ARCHITECTURE=$(uname -m) -if [ "${DEFAULT_ARCHITECTURE}" = "x86_64" ] -then - DEFAULT_ARCHITECTURE=amd64 -fi - -ARCHITECTURE=${DEFAULT_ARCHITECTURE} -RELEASE_TRAIN=${DEFAULT_RELEASE_TRAIN} -VERSION= - -################################################# +# shellcheck disable=SC1090 +source "${LIGHTFIELD_ROOT}/shared-stuff.sh" -ARGS=$(getopt -n 'change-version-number.sh' -o 'a:t:' -- "${@}") +ARGS=$(getopt -n 'change-version-number.sh' -o 't:' -- "${@}") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] then @@ -67,16 +35,6 @@ eval set -- "$ARGS" while [ -n "${1}" ] do case "${1}" in - '-a') - if [ "${2}" != "amd64" ] && [ "${2}" != "arm7l" ] - then - red-bar "Unrecognized argument to -a switch; valid values are amd64 and arm7l." - usage - fi - ARCHITECTURE="${2}" - shift - ;; - '-t') RELEASE_TRAIN="${2}" shift @@ -134,8 +92,7 @@ VERSION: ${VERSION} STRINGVER: ${STRINGVER} HERE -# shellcheck disable=SC2164 -cd "${LIGHTFIELD_SRC}" +cd "${LIGHTFIELD_ROOT}/src" blue-bar 'Generating src/version.h from src/version.h.in' @@ -148,13 +105,12 @@ apply-atsign-substitution VERSION_TEENY "${VER[2]}" version.h apply-atsign-substitution VERSION_BUILD "${VER[3]}" version.h apply-atsign-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" version.h -# shellcheck disable=SC2164 cd "${LIGHTFIELD_ROOT}" blue-bar 'Updating build and packaging scripts' -apply-assignment-substitution ARCHITECTURE "${ARCHITECTURE}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh -apply-assignment-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh -apply-assignment-substitution VERSION "${STRINGVER}" install-lightfield.sh make-deb-package.sh make-upgrade-kit.sh +apply-assignment-substitution ARCHITECTURE "${ARCHITECTURE}" shared-stuff.sh +apply-assignment-substitution RELEASE_TRAIN "${RELEASE_TRAIN}" shared-stuff.sh +apply-assignment-substitution VERSION "${STRINGVER}" shared-stuff.sh blue-bar 'Done!' diff --git a/debian/lightfield.install.in b/debian/lightfield.install similarity index 100% rename from debian/lightfield.install.in rename to debian/lightfield.install diff --git a/debian/lightfield.postinst.in b/debian/lightfield.postinst.in index 4b829601..9b89a01c 100644 --- a/debian/lightfield.postinst.in +++ b/debian/lightfield.postinst.in @@ -1,5 +1,5 @@ #!/bin/sh -# postinst script for lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ +# postinst script for lightfield # # see: dh_installdeb(1) # @@ -21,11 +21,11 @@ RELEASE_TRAIN=@@RELEASE_TRAIN@@ BUILDTYPE=@@BUILDTYPE@@ VERSION=@@VERSION@@ ARCHITECTURE=@@ARCHITECTURE@@ -if [ -z "${RELEASE_TRAIN}" ] || [ "${RELEASE_TRAIN}" = "base" ] +if [ "${RELEASE_TRAIN}" = "base" ] then - SUFFIX=${RELEASE_TRAIN}-${BUILDTYPE} -else SUFFIX=${BUILDTYPE} +else + SUFFIX=${RELEASE_TRAIN}-${BUILDTYPE} fi case "$1" in diff --git a/debian/lightfield.postrm.in b/debian/lightfield.postrm similarity index 94% rename from debian/lightfield.postrm.in rename to debian/lightfield.postrm index 7a249c17..bbed455a 100644 --- a/debian/lightfield.postrm.in +++ b/debian/lightfield.postrm @@ -1,5 +1,5 @@ #!/bin/sh -# postrm script for lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ +# postrm script for lightfield # # see: dh_installdeb(1) # diff --git a/debian/lightfield.preinst.in b/debian/lightfield.preinst similarity index 91% rename from debian/lightfield.preinst.in rename to debian/lightfield.preinst index 75cc6213..b6b40f1a 100644 --- a/debian/lightfield.preinst.in +++ b/debian/lightfield.preinst @@ -1,5 +1,5 @@ #!/bin/sh -# preinst script for lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ +# preinst script for lightfield # # see: dh_installdeb(1) # diff --git a/debian/lightfield.prerm.in b/debian/lightfield.prerm similarity index 94% rename from debian/lightfield.prerm.in rename to debian/lightfield.prerm index 538f0ad6..0601fbe1 100644 --- a/debian/lightfield.prerm.in +++ b/debian/lightfield.prerm @@ -1,5 +1,5 @@ #!/bin/sh -# prerm script for lightfield@@RELEASE_TRAIN@@-@@BUILDTYPE@@ +# prerm script for lightfield # # see: dh_installdeb(1) # diff --git a/install-lightfield.sh b/install-lightfield.sh index 4d2c7b23..2e11cbe5 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -1,8 +1,6 @@ #!/bin/bash -VERSION=1.0.10.0 -RELEASE_TRAIN=base -ARCHITECTURE=amd64 +LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField ######################################################### ## ## @@ -12,48 +10,28 @@ ARCHITECTURE=amd64 [ "${UID}" != "0" ] && exec sudo "${0}" "${@}" -function clear () { - echo -ne "\x1B[0m\x1B[H\x1B[J\x1B[3J" -} - -function blue-bar () { - echo -e "\r\x1B[1;37;44m$*\x1B[K\x1B[0m" 1>&2 -} - -function red-bar () { - echo -e "\r\x1B[1;33;41m$*\x1B[K\x1B[0m" 1>&2 -} - -function error-trap () { - red-bar Failed\! - exit 1 -} - function usage () { cat <] [-t ] +Usage: $(basename "$0") [-q] [-x] [-t ] Where: -q Build quietly. -x Force rebuild all. - -a Sets the architecture. Valid values: amd64 arm7l. - Default: ${ARCHITECTURE} - -t Sets the release train. Default: ${RELEASE_TRAIN} + -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} EOF } -trap error-trap ERR -set -e +# shellcheck disable=SC1090 +source "${LIGHTFIELD_ROOT}/shared-stuff.sh" PRINTRUN_SRC=/home/lumen/Volumetric/printrun -LIGHTFIELD_SRC=/home/lumen/Volumetric/LightField -MOUNTMON_SRC=${LIGHTFIELD_SRC}/mountmon -USBDRIVER_SRC=${LIGHTFIELD_SRC}/usb-driver +MOUNTMON_SRC=${LIGHTFIELD_ROOT}/mountmon +USBDRIVER_SRC=${LIGHTFIELD_ROOT}/usb-driver VERBOSE=-v CHXXXVERBOSE=-c -FORCEREBUILD= BUILDQUIETLY= +FORCEREBUILD= -ARGS=$(getopt -n 'install-lightfield.sh' -o 'qxa:t:' -- "${@}") +ARGS=$(getopt -n 'install-lightfield.sh' -o 'qxt:' -- "${@}") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] then @@ -75,17 +53,6 @@ do FORCEREBUILD=-x ;; - '-a') - if [ "${2}" = "amd64" ] || [ "${2}" = "arm7l" ] - then - ARCHITECTURE="${2}" - shift - else - echo "Unknown architecture '{$2}'." 1>&2 - usage - fi - ;; - '-t') RELEASE_TRAIN="${2}" shift @@ -104,24 +71,37 @@ do shift done -clear +if [ -z "${RELEASE_TRAIN}" ] +then + RELEASE_TRAIN=base +fi + +if [ "${RELEASE_TRAIN}" = "base" ] +then + SUFFIX=debug +else + SUFFIX=${RELEASE_TRAIN}-debug +fi blue-bar • Building debugging version of set-projector-power +# shellcheck disable=SC2164 cd ${USBDRIVER_SRC} -[ -f set-projector-power ] && rm ${VERBOSE} -f set-projector-power -g++ -o set-projector-power -pipe -g -Og -D_DEBUG -std=gnu++1z -Wall -W -D_REENTRANT -fPIC dlpc350_usb.cpp dlpc350_api.cpp main.cpp -lhidapi-libusb +[ "${FORCEREBUILD}" = "-x" ] && make clean +make blue-bar • Building debugging version of Mountmon +# shellcheck disable=SC2164 cd ${MOUNTMON_SRC} ./rebuild ${FORCEREBUILD} ${BUILDQUIETLY} blue-bar • Building debugging version of LightField -cd ${LIGHTFIELD_SRC} +# shellcheck disable=SC2164 +cd ${LIGHTFIELD_ROOT} ./rebuild ${FORCEREBUILD} ${BUILDQUIETLY} chown ${CHXXXVERBOSE} -R lumen:lumen ${USBDRIVER_SRC} chown ${CHXXXVERBOSE} -R lumen:lumen ${MOUNTMON_SRC}/build -chown ${CHXXXVERBOSE} -R lumen:lumen ${LIGHTFIELD_SRC}/build +chown ${CHXXXVERBOSE} -R lumen:lumen ${LIGHTFIELD_ROOT}/build blue-bar • Creating any missing directories [ ! -d /var/cache/lightfield/print-jobs ] && mkdir ${VERBOSE} -p /var/cache/lightfield/print-jobs @@ -155,6 +135,7 @@ install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare.conf chmod 700 /home/lumen/.gnupg [ -f /home/lumen/.gnupg/pubring.kbx ] && rm ${VERBOSE} /home/lumen/.gnupg/pubring.kbx +# shellcheck disable=SC2164 cd ${PRINTRUN_SRC} install ${VERBOSE} -DT -m 644 printrun/__init__.py /usr/share/lightfield/libexec/printrun/printrun/__init__.py install ${VERBOSE} -DT -m 644 printrun/eventhandler.py /usr/share/lightfield/libexec/printrun/printrun/eventhandler.py @@ -168,14 +149,6 @@ blue-bar • Configuring system perl -lpi -e 's/^(?!##LF## )/##LF## /;' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null -if [ -z "${RELEASE_TRAIN}" ] || [ "${RELEASE_TRAIN}" = "base" ] -then - RELEASE_TRAIN= - SUFFIX=debug -else - SUFFIX=${RELEASE_TRAIN}-debug -fi - echo "deb file:/var/lib/lightfield/software-updates/lightfield${SUFFIX}_${VERSION}_${ARCHITECTURE} ./" > /etc/apt/sources.list.d/volumetric-lightfield.list chown ${CHXXXVERBOSE} lumen:lumen /etc/apt/sources.list.d/volumetric-lightfield.list diff --git a/make-deb-package.sh b/make-deb-package.sh index ecbf46aa..5db06d78 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -1,10 +1,10 @@ #!/bin/bash +# shellcheck disable=SC2103 +# shellcheck disable=SC2164 -VERSION=1.0.10.0 BUILDTYPE= -ARCHITECTURE=amd64 -RELEASE_TRAIN=base -PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging +LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField +PACKAGE_BUILD_ROOT=${LIGHTFIELD_ROOT}/packaging ######################################################### ## ## @@ -12,25 +12,12 @@ PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging ## ## ######################################################### -function blue-bar () { - echo -e "\r\x1B[1;37;44m$*\x1B[K\x1B[0m" 1>&2 -} - -function red-bar () { - echo -e "\r\x1B[1;33;41m$*\x1B[K\x1B[0m" 1>&2 -} - -function error-trap () { - red-bar "Failed!" - exit 1 -} - function usage () { cat 1>&2 <] BUILDTYPE Where: -q build quietly -X don't force rebuild - -t Sets the release train. Default: ${RELEASE_TRAIN} + -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} BUILDTYPE is one of release create a release-build kit debug create a debug-build kit @@ -42,35 +29,14 @@ EOF exit 1 } -function apply-atsign-substitution () { - local CODE='\e[0;30;48;2;146;208;80m' - local RST='\e[0m' - # shellcheck disable=SC2145 - echo -e "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" - sed -i "s/@@${1}@@/${2}/g" "${@:3}" -} - -function apply-assignment-substitution () { - local CODE='\e[0;30;48;2;0;146;208m' - local RST='\e[0m' - # shellcheck disable=SC2145 - echo -e "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" - sed -i 's/^'"${1}"'=.*$/'"${1}"'='"${2}"'/g' "${@:3}" -} - -trap error-trap ERR -set -e - -PRINTRUN_SRC=/home/lumen/Volumetric/printrun -LIGHTFIELD_SRC=/home/lumen/Volumetric/LightField -MOUNTMON_SRC="${LIGHTFIELD_SRC}/mountmon" -USBDRIVER_SRC="${LIGHTFIELD_SRC}/usb-driver" +# shellcheck disable=SC1090 +source "${LIGHTFIELD_ROOT}/shared-stuff.sh" VERBOSE=-v CHXXXVERBOSE=-c FORCEREBUILD=-x -ARGS=$(getopt -n 'make-deb-package.sh' -o 'qXa:t:' -- "$@") +ARGS=$(getopt -n 'make-deb-package.sh' -o 'qXt:' -- "$@") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] then @@ -95,14 +61,8 @@ do shift ;; - 'release' | 'debug' | 'both') - if [ -z "${BUILDTYPE}" ] - then - BUILDTYPE="${1}" - else - echo "Too many build types specified -- use 'both' to build debug and release packages." 1>&2 - usage - fi + --) + shift break ;; @@ -114,22 +74,37 @@ do shift done -if [ -z "${BUILDTYPE}" ] +if [ -z "${1}" ] +then + red-bar 'No build type given.' + usage +elif [ -n "${2}" ] then + red-bar 'Too many arguments given.' usage +else + if [ "${1}" = "debug" ] || [ "${1}" = "release" ] + then + BUILDTYPE="${1}" + elif [ "${1}" = "both" ] + then + "$0" "${ARGS}" release || exit $? + "$0" "${ARGS}" debug || exit $? + exit 0 + else + red-bar "Unknown build type '${1}'." + usage + fi fi -if [ "${BUILDTYPE}" = "both" ] +if [ -z "${RELEASE_TRAIN}" ] then - "$0" "${ARGS}" release || exit $? - "$0" "${ARGS}" debug || exit $? - exit 0 + RELEASE_TRAIN=base fi -if [ -z "${RELEASE_TRAIN}" ] || [ "${RELEASE_TRAIN}" = "base" ] +if [ "${RELEASE_TRAIN}" = "base" ] then SUFFIX=${BUILDTYPE} - RELEASE_TRAIN= else SUFFIX=${RELEASE_TRAIN}-${BUILDTYPE} fi @@ -141,11 +116,40 @@ else ANTIBUILDTYPE=debug fi -PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" +PRINTRUN_SRC=/home/lumen/Volumetric/printrun +MOUNTMON_SRC="${LIGHTFIELD_ROOT}/mountmon" +if [ "${RELEASE_TRAIN}" = "dlp4710" ] +then + PROJECTOR_SRC="${LIGHTFIELD_ROOT}/dlp4710" +else + PROJECTOR_SRC="${LIGHTFIELD_ROOT}/usb-driver" +fi + +PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${VERSION}-${SUFFIX}" DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" LIGHTFIELD_PACKAGE="${DEB_BUILD_DIR}/lightfield-${VERSION}" LIGHTFIELD_FILES="${LIGHTFIELD_PACKAGE}/files" +#cat <&2 -} - -function red-bar () { - echo -e "\r\x1B[1;33;41m$*\x1B[K\x1B[0m" 1>&2 -} - -function error-trap () { - red-bar Failed\! - exit 1 -} - function usage () { cat < Sets the release train. Default: ${RELEASE_TRAIN} + -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} BUILDTYPE is one of release create a release-build kit debug create a debug-build kit both create both kits If the build is successful, the requested upgrade kit(s) will be found in - ${KIT_DIR}/lightfield$([ "${RELEASE_TRAIN}" = "base" ] || echo "-${RELEASE_TRAIN}" )-BUILDTYPE_${VERSION}_${ARCHITECTURE}.kit + ${KIT_DIR}/lightfield$([ "${DEFAULT_RELEASE_TRAIN}" = "base" ] || echo "-${DEFAULT_RELEASE_TRAIN}" )-BUILDTYPE_${VERSION}_${DEFAULT_ARCHITECTURE}.kit EOF + exit 1 } -trap error-trap ERR -set -e - -if [ "${USE_KEY_SET}" = "current" ] -then - REPO_KEY_ID=E91BD3361F39D49C78B1E3A2B55C0E8D4B632A66 - PKG_KEY_ID=78DAD29978EB392992D7FE0423025033D9E840F7 -#elif [ "${USE_KEY_SET}" = "future" ] -#then -else - red-bar "The key set '${USE_KEY_SET}' is unrecognized." -fi +# shellcheck disable=SC1090 +source "${LIGHTFIELD_ROOT}/shared-stuff.sh" VERBOSE=-v @@ -82,21 +59,48 @@ do break ;; + --) + shift + break + ;; + *) + echo "Unknown parameter '${1}'." usage - exit 1 ;; esac shift done -if [ -z "${BUILDTYPE}" ] +if [ -z "${1}" ] then + red-bar 'No build type given.' usage - exit 1 +elif [ -n "${2}" ] +then + red-bar 'Too many arguments given.' + usage +else + if [ "${1}" = "debug" ] || [ "${1}" = "release" ] + then + BUILDTYPE="${1}" + elif [ "${1}" = "both" ] + then + "$0" "${ARGS}" release || exit $? + "$0" "${ARGS}" debug || exit $? + exit 0 + else + red-bar "Unknown build type '${1}'." + usage + fi +fi + +if [ -z "${RELEASE_TRAIN}" ] +then + RELEASE_TRAIN=base fi -if [ -z "${RELEASE_TRAIN}" ] || [ "${RELEASE_TRAIN}" = "base" ] +if [ "${RELEASE_TRAIN}" = "base" ] then SUFFIX=${BUILDTYPE} else @@ -110,7 +114,19 @@ then exit 0 fi -PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${SUFFIX}-${VERSION}" +if [ "${USE_KEY_SET}" = "current" ] +then + REPO_KEY_ID=E91BD3361F39D49C78B1E3A2B55C0E8D4B632A66 + PKG_KEY_ID=78DAD29978EB392992D7FE0423025033D9E840F7 +#elif [ "${USE_KEY_SET}" = "future" ] +#then +# no future keys currently +else + red-bar "The key set '${USE_KEY_SET}' is unrecognized." + usage +fi + +PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${VERSION}-${SUFFIX}" DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" KIT_DIR="${PACKAGE_BUILD_DIR}/kit" REPO_DIR="${PACKAGE_BUILD_DIR}/repo" @@ -125,7 +141,7 @@ blue-bar "• Creating LightField ${VERSION} ${RELEASE_TRAIN} ${BUILDTYPE}-build mkdir ${VERBOSE} -p "${REPO_DIR}" mkdir ${VERBOSE} -p "${KIT_DIR}" -install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${LIGHTFIELD_SRC}/fonts-montserrat_7.200_all.deb" +install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${LIGHTFIELD_ROOT}/fonts-montserrat_7.200_all.deb" install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-common_${VERSION}_all.deb" if [ "${BUILDTYPE}" = "release" ] @@ -153,7 +169,7 @@ cd "${REPO_DIR}" dpkg-scanpackages . | tee Packages | xz -ceT0 > Packages.xz -apt-ftparchive --config-file ${LIGHTFIELD_SRC}/apt-files/release.conf release . | tee Release | xz -ceT0 > Release.xz +apt-ftparchive --config-file ${LIGHTFIELD_ROOT}/apt-files/release.conf release . | tee Release | xz -ceT0 > Release.xz gpg \ ${VERBOSE} \ @@ -185,16 +201,16 @@ sha256sum -- -b * | sed -r -e 's/^/ /' -e 's/ +\*/ /' > .hashes -e "s/@@RELEASEDATE@@/${RELEASEDATE}/g" \ -e "s/@@RELEASE_TRAIN@@/${RELEASE_TRAIN}/g" \ -e "s/@@VERSION@@/${VERSION}/g" \ - "${LIGHTFIELD_SRC}/apt-files/version.inf.in" + "${LIGHTFIELD_ROOT}/apt-files/version.inf.in" - # extract description from ${LIGHTFIELD_SRC}/debian/changelog - linecount=$(grep -n '^ -- LightField packager' ${LIGHTFIELD_SRC}/debian/changelog | head -1 | cut -d: -f1 || echo 0) + # extract description from ${LIGHTFIELD_ROOT}/debian/changelog + linecount=$(grep -n '^ -- LightField packager' ${LIGHTFIELD_ROOT}/debian/changelog | head -1 | cut -d: -f1 || echo 0) if [ -z "${linecount}" ] || [ "${linecount}" -lt 1 ] then - red-bar "!!! Can't find end of first change in ${LIGHTFIELD_SRC}/debian/changelog, aborting" + red-bar "!!! Can't find end of first change in ${LIGHTFIELD_ROOT}/debian/changelog, aborting" exit 1 fi - head -$((linecount - 2)) ${LIGHTFIELD_SRC}/debian/changelog | tail +3 | perl -lpe 's/\s+$//; s/^$/./; s/^/ /' + head -$((linecount - 2)) ${LIGHTFIELD_ROOT}/debian/changelog | tail +3 | perl -lpe 's/\s+$//; s/^$/./; s/^/ /' echo 'Checksums-SHA256:' cat .hashes diff --git a/shared-stuff.sh b/shared-stuff.sh new file mode 100644 index 00000000..2b797f04 --- /dev/null +++ b/shared-stuff.sh @@ -0,0 +1,49 @@ +#!/bin/bash +#shellcheck disable=SC2034 + +function clear () { + echo -ne "\x1B[0m\x1B[H\x1B[J\x1B[3J" +} + +function blue-bar () { + echo -e "\r\e[1;37;44m$*\e[K\e[0m" 1>&2 +} + +function red-bar () { + echo -e "\r\e[1;33;41m$*\e[K\e[0m" 1>&2 +} + +function error-trap () { + red-bar 'Failed!' + exit 1 +} + +function apply-atsign-substitution () { + local CODE='\e[0;30;48;2;146;208;80m' + local RST='\e[0m' + # shellcheck disable=SC2145 + echo -e "Substituting value ${CODE}${2}${RST} for token ${CODE}@@${1}@@${RST} in files ${CODE}${@:3}${RST}" + sed -i "s/@@${1}@@/${2}/g" "${@:3}" +} + +function apply-assignment-substitution () { + local CODE='\e[0;30;48;2;0;146;208m' + local RST='\e[0m' + # shellcheck disable=SC2145 + echo -e "Substituting value ${CODE}${2}${RST} into variable assignment ${CODE}${1}${RST} in files ${CODE}${@:3}${RST}" + sed -i 's/^'"${1}"'=.*$/'"${1}"'='"${2}"'/g' "${@:3}" +} + +DEFAULT_RELEASE_TRAIN=base +DEFAULT_ARCHITECTURE=$(uname -m) +if [ "${DEFAULT_ARCHITECTURE}" = "x86_64" ] +then + DEFAULT_ARCHITECTURE=amd64 +fi + +ARCHITECTURE=amd64 +RELEASE_TRAIN=base +VERSION=1.0.10.0 + +trap error-trap ERR +set -e diff --git a/usb-driver/Makefile b/usb-driver/Makefile new file mode 100644 index 00000000..99ceaf18 --- /dev/null +++ b/usb-driver/Makefile @@ -0,0 +1,36 @@ +BUILD = debug + +CXX = g++ +LD = g++ +RM = rm + +TARGET = set-projector-power +SOURCES = dlpc350_usb.cpp dlpc350_api.cpp main.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +CFLAGS = -std=gnu++17 -Wall -Wextra -Werror -pedantic +LFLAGS = +ifeq ($(BUILD),debug) +CFLAGS += -D_DEBUG -g -Og +LFLAGS += -g +else ifeq ($(BUILD),release) +CFLAGS += -DNDEBUG -O3 +LFLAGS += -s +endif + +all: $(TARGET) + +%.o: %.cpp + $(CXX) $(CFLAGS) -c $< + +$(TARGET): $(OBJECTS) + $(LD) $(LFLAGS) -o $@ $^ -lhidapi-libusb + +clean: + @$(RM) $(TARGET) $(OBJECTS) + +## Dependencies + +dlpc350_usb.o: dlpc350_usb.cpp +dlpc350_api.o: dlpc350_api.cpp +main.o: main.cpp From 17500cb523b08e7164ce5a9fe46e6ce6d6ae83ae Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 17 Jan 2020 13:56:25 -0800 Subject: [PATCH 32/89] Multiple release trains: Add DLP4710 support. --- change-version-number.sh | 5 +- ...install => lightfield-common-base.install} | 0 debian/lightfield-common-dlp4710.install | 12 +++ install-lightfield.sh | 93 +++++++++++-------- make-deb-package.sh | 91 +++++++++--------- make-upgrade-kit.sh | 23 ++--- system-stuff/99-waveshare-dlp4710.conf | 48 ++++++++++ ...veshare.conf => 99-waveshare-dlpc350.conf} | 0 .../dlp4710-reset-lumen-projector-port | 4 + .../dlp4710-set-projector-power.service | 19 ++++ ...ce => dlpc350-set-projector-power.service} | 0 11 files changed, 195 insertions(+), 100 deletions(-) rename debian/{lightfield-common.install => lightfield-common-base.install} (100%) create mode 100644 debian/lightfield-common-dlp4710.install create mode 100644 system-stuff/99-waveshare-dlp4710.conf rename system-stuff/{99-waveshare.conf => 99-waveshare-dlpc350.conf} (100%) create mode 100755 system-stuff/dlp4710-reset-lumen-projector-port create mode 100644 system-stuff/dlp4710-set-projector-power.service rename system-stuff/{set-projector-power.service => dlpc350-set-projector-power.service} (100%) diff --git a/change-version-number.sh b/change-version-number.sh index a6b0933f..6055b74b 100755 --- a/change-version-number.sh +++ b/change-version-number.sh @@ -15,8 +15,11 @@ Usage: $(basename "$0") [-t ] Changes the LightField version in all the relevant places in the source. -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} + Available release trains: + base LightField with DLPC350 support + dlpc4710 LightField with DLP4710 support Version number. If the fourth element is omitted, - it is defaulted to 0. + it defaults to 0. HERE exit 1 } diff --git a/debian/lightfield-common.install b/debian/lightfield-common-base.install similarity index 100% rename from debian/lightfield-common.install rename to debian/lightfield-common-base.install diff --git a/debian/lightfield-common-dlp4710.install b/debian/lightfield-common-dlp4710.install new file mode 100644 index 00000000..bf6cc8e9 --- /dev/null +++ b/debian/lightfield-common-dlp4710.install @@ -0,0 +1,12 @@ +files/etc/apt/trusted.gpg.d/volumetric-keyring.gpg etc/apt/trusted.gpg.d +files/etc/sudoers.d/lumen-lightfield etc/sudoers.d +files/home/lumen/.bash_profile home/lumen +files/home/lumen/.real_bash_profile home/lumen +files/home/lumen/.gnupg/pubring.gpg home/lumen/.gnupg +files/home/lumen/.gnupg/pubring.kbx home/lumen/.gnupg +files/home/lumen/.gnupg/trustdb.gpg home/lumen/.gnupg +files/lib/udev/rules.d/90-dlp4710.rules lib/udev/rules.d +files/usr/share/lightfield/libexec/reset-lumen-arduino-port usr/share/lightfield/libexec +files/usr/share/lightfield/libexec/printrun/* usr/share/lightfield/libexec/printrun +files/usr/share/lightfield/libexec/stdio-shepherd/* usr/share/lightfield/libexec/stdio-shepherd +files/usr/share/X11/xorg.conf.d/99-waveshare.conf usr/share/X11/xorg.conf.d diff --git a/install-lightfield.sh b/install-lightfield.sh index 2e11cbe5..787432d1 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -12,10 +12,9 @@ LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField function usage () { cat <] -Where: -q Build quietly. - -x Force rebuild all. - -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} +Usage: $(basename "$0") [-q] [-x] +Where: -q Build quietly. + -x Force rebuild all. EOF } @@ -24,14 +23,13 @@ source "${LIGHTFIELD_ROOT}/shared-stuff.sh" PRINTRUN_SRC=/home/lumen/Volumetric/printrun MOUNTMON_SRC=${LIGHTFIELD_ROOT}/mountmon -USBDRIVER_SRC=${LIGHTFIELD_ROOT}/usb-driver VERBOSE=-v CHXXXVERBOSE=-c BUILDQUIETLY= FORCEREBUILD= -ARGS=$(getopt -n 'install-lightfield.sh' -o 'qxt:' -- "${@}") +ARGS=$(getopt -n 'install-lightfield.sh' -o 'qx' -- "${@}") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] then @@ -53,11 +51,6 @@ do FORCEREBUILD=-x ;; - '-t') - RELEASE_TRAIN="${2}" - shift - ;; - '--') shift break @@ -83,9 +76,20 @@ else SUFFIX=${RELEASE_TRAIN}-debug fi +if [ "${RELEASE_TRAIN}" = "base" ] +then + PROJECTOR_SRC=${LIGHTFIELD_ROOT}/usb-driver +elif [ "${RELEASE_TRAIN}" = "dlp4710" ] +then + PROJECTOR_SRC=${LIGHTFIELD_ROOT}/dlp4710 +else + red-bar "Unrecognized release train name '${RELEASE_TRAIN}'." + usage +fi + blue-bar • Building debugging version of set-projector-power # shellcheck disable=SC2164 -cd ${USBDRIVER_SRC} +cd ${PROJECTOR_SRC} [ "${FORCEREBUILD}" = "-x" ] && make clean make @@ -99,7 +103,7 @@ blue-bar • Building debugging version of LightField cd ${LIGHTFIELD_ROOT} ./rebuild ${FORCEREBUILD} ${BUILDQUIETLY} -chown ${CHXXXVERBOSE} -R lumen:lumen ${USBDRIVER_SRC} +chown ${CHXXXVERBOSE} -R lumen:lumen ${PROJECTOR_SRC} chown ${CHXXXVERBOSE} -R lumen:lumen ${MOUNTMON_SRC}/build chown ${CHXXXVERBOSE} -R lumen:lumen ${LIGHTFIELD_ROOT}/build @@ -114,36 +118,47 @@ chown ${CHXXXVERBOSE} -R lumen:lumen /var/lib/lightfield chown ${CHXXXVERBOSE} -R lumen:lumen /var/log/lightfield blue-bar • Installing files -install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock /etc/apt/apt.conf.d/99untrustworthy-clock -install ${VERBOSE} -DT -m 644 gpg/pubring.gpg /etc/apt/trusted.gpg.d/volumetric-keyring.gpg -install ${VERBOSE} -DT -m 440 system-stuff/lumen-lightfield /etc/sudoers.d/lumen-lightfield -install ${VERBOSE} -DT -m 644 system-stuff/getty@tty1.service.d_override.conf /etc/systemd/system/getty@tty1.service.d/override.conf -install ${VERBOSE} -DT -m 644 -o lumen -g lumen system-stuff/lumen-bash_profile /home/lumen/.bash_profile -install ${VERBOSE} -DT -m 644 -o lumen -g lumen system-stuff/lumen-real_bash_profile /home/lumen/.real_bash_profile -install ${VERBOSE} -DT -m 600 -o lumen -g lumen gpg/pubring.gpg /home/lumen/.gnupg/pubring.gpg -install ${VERBOSE} -DT -m 600 -o lumen -g lumen gpg/trustdb.gpg /home/lumen/.gnupg/trustdb.gpg -install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service /lib/systemd/system/clean-up-mount-points.service -install ${VERBOSE} -DT -m 644 system-stuff/set-projector-power.service /lib/systemd/system/set-projector-power.service -install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules /lib/udev/rules.d/90-dlpc350.rules -install ${VERBOSE} -DT -m 755 build/lf /usr/bin/lf -install ${VERBOSE} -DT -m 755 mountmon/build/mountmon /usr/bin/mountmon -install ${VERBOSE} -DT -m 755 usb-driver/set-projector-power /usr/bin/set-projector-power -install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py /usr/share/lightfield/libexec/stdio-shepherd/printer.py -install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py /usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py -install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port /usr/share/lightfield/libexec/reset-lumen-arduino-port -install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf -chmod 700 /home/lumen/.gnupg +install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock /etc/apt/apt.conf.d/99untrustworthy-clock +install ${VERBOSE} -DT -m 644 gpg/pubring.gpg /etc/apt/trusted.gpg.d/volumetric-keyring.gpg +install ${VERBOSE} -DT -m 440 system-stuff/lumen-lightfield /etc/sudoers.d/lumen-lightfield +install ${VERBOSE} -DT -m 644 system-stuff/getty@tty1.service.d_override.conf /etc/systemd/system/getty@tty1.service.d/override.conf +install ${VERBOSE} -DT -m 644 -o lumen -g lumen system-stuff/lumen-bash_profile /home/lumen/.bash_profile +install ${VERBOSE} -DT -m 644 -o lumen -g lumen system-stuff/lumen-real_bash_profile /home/lumen/.real_bash_profile +install ${VERBOSE} -DT -m 600 -o lumen -g lumen gpg/pubring.gpg /home/lumen/.gnupg/pubring.gpg +install ${VERBOSE} -DT -m 600 -o lumen -g lumen gpg/trustdb.gpg /home/lumen/.gnupg/trustdb.gpg +install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service /lib/systemd/system/clean-up-mount-points.service +install ${VERBOSE} -DT -m 755 build/lf /usr/bin/lf +install ${VERBOSE} -DT -m 755 mountmon/build/mountmon /usr/bin/mountmon +install ${VERBOSE} -DT -m 755 usb-driver/set-projector-power /usr/bin/set-projector-power +install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py /usr/share/lightfield/libexec/stdio-shepherd/printer.py +install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py /usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py +install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port /usr/share/lightfield/libexec/reset-lumen-arduino-port + +if [ "${RELEASE_TRAIN}" = "base" ] +then + install ${VERBOSE} -DT -m 644 system-stuff/dlpc350-set-projector-power.service /lib/systemd/system/set-projector-power.service + install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules /lib/udev/rules.d/90-dlpc350.rules + install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlpc350.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf +elif [ "${RELEASE_TRAIN}" = "dlp4710" ] +then + install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service /lib/systemd/system/set-projector-power.service + install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules /lib/udev/rules.d/90-dlp4710.rules + install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-reset-lumen-projector-port /usr/share/lightfield/libexec/reset-lumen-projector-port + install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf +fi + [ -f /home/lumen/.gnupg/pubring.kbx ] && rm ${VERBOSE} /home/lumen/.gnupg/pubring.kbx +chmod ${CHXXXVERBOSE} -R go= /home/lumen/.gnupg # shellcheck disable=SC2164 cd ${PRINTRUN_SRC} -install ${VERBOSE} -DT -m 644 printrun/__init__.py /usr/share/lightfield/libexec/printrun/printrun/__init__.py -install ${VERBOSE} -DT -m 644 printrun/eventhandler.py /usr/share/lightfield/libexec/printrun/printrun/eventhandler.py -install ${VERBOSE} -DT -m 644 printrun/gcoder.py /usr/share/lightfield/libexec/printrun/printrun/gcoder.py -install ${VERBOSE} -DT -m 644 printrun/printcore.py /usr/share/lightfield/libexec/printrun/printrun/printcore.py -install ${VERBOSE} -DT -m 644 printrun/utils.py /usr/share/lightfield/libexec/printrun/printrun/utils.py -install ${VERBOSE} -DT -m 644 printrun/plugins/__init__.py /usr/share/lightfield/libexec/printrun/printrun/plugins/__init__.py -install ${VERBOSE} -DT -m 644 Util/constants.py /usr/share/lightfield/libexec/printrun/Util/constants.py +install ${VERBOSE} -DT -m 644 printrun/__init__.py /usr/share/lightfield/libexec/printrun/printrun/__init__.py +install ${VERBOSE} -DT -m 644 printrun/eventhandler.py /usr/share/lightfield/libexec/printrun/printrun/eventhandler.py +install ${VERBOSE} -DT -m 644 printrun/gcoder.py /usr/share/lightfield/libexec/printrun/printrun/gcoder.py +install ${VERBOSE} -DT -m 644 printrun/printcore.py /usr/share/lightfield/libexec/printrun/printrun/printcore.py +install ${VERBOSE} -DT -m 644 printrun/utils.py /usr/share/lightfield/libexec/printrun/printrun/utils.py +install ${VERBOSE} -DT -m 644 printrun/plugins/__init__.py /usr/share/lightfield/libexec/printrun/printrun/plugins/__init__.py +install ${VERBOSE} -DT -m 644 Util/constants.py /usr/share/lightfield/libexec/printrun/Util/constants.py blue-bar • Configuring system diff --git a/make-deb-package.sh b/make-deb-package.sh index 5db06d78..75c5a97e 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -14,14 +14,13 @@ PACKAGE_BUILD_ROOT=${LIGHTFIELD_ROOT}/packaging function usage () { cat 1>&2 <] BUILDTYPE -Where: -q build quietly - -X don't force rebuild - -t Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} - BUILDTYPE is one of - release create a release-build kit - debug create a debug-build kit - both create both kits +Usage: $(basename "$0") [-q] [-X] BUILDTYPE +Where: -q build quietly + -X don't force rebuild + BUILDTYPE is one of + release create a release-build kit + debug create a debug-build kit + both create both kits If the build is successful, the requested package set(s) will be found in a subdirectory of ${PACKAGE_BUILD_ROOT}/ . @@ -36,7 +35,7 @@ VERBOSE=-v CHXXXVERBOSE=-c FORCEREBUILD=-x -ARGS=$(getopt -n 'make-deb-package.sh' -o 'qXt:' -- "$@") +ARGS=$(getopt -n 'make-deb-package.sh' -o 'qX' -- "$@") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] then @@ -56,11 +55,6 @@ do FORCEREBUILD= ;; - '-t') - RELEASE_TRAIN="${2}" - shift - ;; - --) shift break @@ -213,22 +207,32 @@ install ${VERBOSE} -DT -m 755 build/lf "${LIGHTFIELD_FILES}/usr/bin/lf" blue-bar "• Copying LightField files into packaging directory" -install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock "${LIGHTFIELD_FILES}/etc/apt/apt.conf.d/99untrustworthy-clock" -install ${VERBOSE} -DT -m 644 gpg/pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" -install ${VERBOSE} -DT -m 440 system-stuff/lumen-lightfield "${LIGHTFIELD_FILES}/etc/sudoers.d/lumen-lightfield" -install ${VERBOSE} -DT -m 644 system-stuff/getty@tty1.service.d_override.conf "${LIGHTFIELD_FILES}/etc/systemd/system/getty@tty1.service.d/override.conf" -install ${VERBOSE} -DT -m 600 system-stuff/lumen-bash_profile "${LIGHTFIELD_FILES}/home/lumen/.bash_profile" -install ${VERBOSE} -DT -m 600 system-stuff/lumen-real_bash_profile "${LIGHTFIELD_FILES}/home/lumen/.real_bash_profile" -install ${VERBOSE} -DT -m 600 gpg/pubring.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.gpg" -install ${VERBOSE} -DT -m 600 gpg/pubring.kbx "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.kbx" -install ${VERBOSE} -DT -m 600 gpg/trustdb.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/trustdb.gpg" -install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service "${LIGHTFIELD_FILES}/lib/systemd/system/clean-up-mount-points.service" -install ${VERBOSE} -DT -m 644 system-stuff/set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" -install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlpc350.rules" -install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-arduino-port" -install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/printer.py" -install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py" -install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" +install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock "${LIGHTFIELD_FILES}/etc/apt/apt.conf.d/99untrustworthy-clock" +install ${VERBOSE} -DT -m 644 gpg/pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" +install ${VERBOSE} -DT -m 440 system-stuff/lumen-lightfield "${LIGHTFIELD_FILES}/etc/sudoers.d/lumen-lightfield" +install ${VERBOSE} -DT -m 644 system-stuff/getty@tty1.service.d_override.conf "${LIGHTFIELD_FILES}/etc/systemd/system/getty@tty1.service.d/override.conf" +install ${VERBOSE} -DT -m 600 system-stuff/lumen-bash_profile "${LIGHTFIELD_FILES}/home/lumen/.bash_profile" +install ${VERBOSE} -DT -m 600 system-stuff/lumen-real_bash_profile "${LIGHTFIELD_FILES}/home/lumen/.real_bash_profile" +install ${VERBOSE} -DT -m 600 gpg/pubring.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.gpg" +install ${VERBOSE} -DT -m 600 gpg/pubring.kbx "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.kbx" +install ${VERBOSE} -DT -m 600 gpg/trustdb.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/trustdb.gpg" +install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service "${LIGHTFIELD_FILES}/lib/systemd/system/clean-up-mount-points.service" +install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-arduino-port" +install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/printer.py" +install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py" + +if [ "${RELEASE_TRAIN}" = "base" ] +then + install ${VERBOSE} -DT -m 644 system-stuff/dlpc350-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" + install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlpc350.rules" + install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlpc350.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" +elif [ "${RELEASE_TRAIN}" = "dlp4710" ] +then + install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" + install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlp4710.rules" + install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-reset-lumen-projector-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-projector-port" + install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" +fi chmod ${CHXXXVERBOSE} -R go= "${LIGHTFIELD_FILES}/home/lumen/.gnupg" @@ -238,13 +242,13 @@ blue-bar "• Copying printrun files into packaging directory" cd "${PRINTRUN_SRC}" -install ${VERBOSE} -DT -m 644 printrun/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/__init__.py" -install ${VERBOSE} -DT -m 644 printrun/eventhandler.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/eventhandler.py" -install ${VERBOSE} -DT -m 644 printrun/gcoder.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/gcoder.py" -install ${VERBOSE} -DT -m 644 printrun/printcore.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/printcore.py" -install ${VERBOSE} -DT -m 644 printrun/utils.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/utils.py" -install ${VERBOSE} -DT -m 644 printrun/plugins/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/plugins/__init__.py" -install ${VERBOSE} -DT -m 644 Util/constants.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/Util/constants.py" +install ${VERBOSE} -DT -m 644 printrun/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/__init__.py" +install ${VERBOSE} -DT -m 644 printrun/eventhandler.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/eventhandler.py" +install ${VERBOSE} -DT -m 644 printrun/gcoder.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/gcoder.py" +install ${VERBOSE} -DT -m 644 printrun/printcore.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/printcore.py" +install ${VERBOSE} -DT -m 644 printrun/utils.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/utils.py" +install ${VERBOSE} -DT -m 644 printrun/plugins/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/plugins/__init__.py" +install ${VERBOSE} -DT -m 644 Util/constants.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/Util/constants.py" ################################################## @@ -252,12 +256,13 @@ blue-bar "• Building Debian packages" cd "${LIGHTFIELD_PACKAGE}" -mv debian/control.in debian/control -mv debian/lightfield.install debian/lightfield-"${SUFFIX}".install -mv debian/lightfield.preinst debian/lightfield-"${SUFFIX}".preinst -mv debian/lightfield.prerm debian/lightfield-"${SUFFIX}".prerm -mv debian/lightfield.postinst.in debian/lightfield-"${SUFFIX}".postinst -mv debian/lightfield.postrm debian/lightfield-"${SUFFIX}".postrm +mv debian/control.in debian/control +mv debian/lightfield.install debian/lightfield-"${SUFFIX}".install +mv debian/lightfield-common-"${RELEASE_TRAIN}".install debian/lightfield-common-"${SUFFIX}".install +mv debian/lightfield.preinst debian/lightfield-"${SUFFIX}".preinst +mv debian/lightfield.prerm debian/lightfield-"${SUFFIX}".prerm +mv debian/lightfield.postinst.in debian/lightfield-"${SUFFIX}".postinst +mv debian/lightfield.postrm debian/lightfield-"${SUFFIX}".postrm apply-atsign-substitution BUILDTYPE "${BUILDTYPE}" debian/control apply-atsign-substitution ANTIBUILDTYPE "${ANTIBUILDTYPE}" debian/control diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index cbf7f72a..f57f901a 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -16,12 +16,11 @@ BUILDTYPE= function usage () { cat < Sets the release train. Default: ${DEFAULT_RELEASE_TRAIN} - BUILDTYPE is one of - release create a release-build kit - debug create a debug-build kit - both create both kits +Where: -q build quietly + BUILDTYPE is one of + release create a release-build kit + debug create a debug-build kit + both create both kits If the build is successful, the requested upgrade kit(s) will be found in ${KIT_DIR}/lightfield$([ "${DEFAULT_RELEASE_TRAIN}" = "base" ] || echo "-${DEFAULT_RELEASE_TRAIN}" )-BUILDTYPE_${VERSION}_${DEFAULT_ARCHITECTURE}.kit @@ -34,7 +33,7 @@ source "${LIGHTFIELD_ROOT}/shared-stuff.sh" VERBOSE=-v -ARGS=$(getopt -n 'make-upgrade-kit.sh' -o 'qa:t:' -- "$@") +ARGS=$(getopt -n 'make-upgrade-kit.sh' -o 'q' -- "$@") # shellcheck disable=SC2181 if [ ${?} -ne 0 ] then @@ -49,16 +48,6 @@ do VERBOSE= ;; - '-t') - RELEASE_TRAIN="${2}" - shift - ;; - - 'release' | 'debug' | 'both') - BUILDTYPE="${1}" - break - ;; - --) shift break diff --git a/system-stuff/99-waveshare-dlp4710.conf b/system-stuff/99-waveshare-dlp4710.conf new file mode 100644 index 00000000..91a67d83 --- /dev/null +++ b/system-stuff/99-waveshare-dlp4710.conf @@ -0,0 +1,48 @@ +Section "InputClass" + Identifier "WaveShare WS170120 configuration override" + MatchIsTouchscreen "on" + MatchUSBID "0eef:0005" + Driver "libinput" + Option "TransformationMatrix" "0.5333333 0 0 0 0.3571429 0.6428571 0 0 1" +EndSection + +Section "Monitor" + # Touchscreen + Identifier "DP-1" + Modeline "1024x600_60.0" 49.00 1024 1029 1042 1312 600 602 605 622 -hsync -vsync + Option "PreferredMode" "1024x600_60.0" + Option "Position" "0 1080" + Option "DPMS" "false" + Option "Primary" "true" +EndSection + +Section "Monitor" + # Projector + Identifier "DP-2" + Modeline "1920x1080_60.0" 148.50 1920 2008 2096 2200 1080 1084 1089 1125 -hsync -vsync + Option "PreferredMode" "1920x1080_60.0" + Option "Position" "0 0" + Option "DPMS" "false" + Option "Primary" "false" +EndSection + +Section "Screen" + Identifier "Screen0" + Device "Intel" + Monitor "DP-1" + DefaultDepth 24 + SubSection "Display" + Depth 24 + Virtual 1920 1680 # ( max(1920,1024), (1080+600) ) + EndSubSection +EndSection + +Section "Device" + Identifier "Intel" + Driver "intel" + BusID "PCI:0:2:0" + Option "Monitor-DP1" "DP-1" + Option "Monitor-DP2" "DP-2" + Option "ModeDebug" "true" +EndSection + diff --git a/system-stuff/99-waveshare.conf b/system-stuff/99-waveshare-dlpc350.conf similarity index 100% rename from system-stuff/99-waveshare.conf rename to system-stuff/99-waveshare-dlpc350.conf diff --git a/system-stuff/dlp4710-reset-lumen-projector-port b/system-stuff/dlp4710-reset-lumen-projector-port new file mode 100755 index 00000000..c6b3dec8 --- /dev/null +++ b/system-stuff/dlp4710-reset-lumen-projector-port @@ -0,0 +1,4 @@ +#!/bin/bash + +stty -F /dev/lumen-projector -a 1>/dev/null 2>&1 +sleep 1 diff --git a/system-stuff/dlp4710-set-projector-power.service b/system-stuff/dlp4710-set-projector-power.service new file mode 100644 index 00000000..9e0c8307 --- /dev/null +++ b/system-stuff/dlp4710-set-projector-power.service @@ -0,0 +1,19 @@ +# lightfield.service +# +# lightfield systemd unit file: +# Makes sure the projector is off on startup and shutdown. + +[Unit] +Description=Reset Lumen X projector +RequiresMountsFor=/usr + +[Service] +Type=oneshot +RemainAfterExit=yes +User=root +ExecStart=/usr/share/lightfield/libexec/reset-lumen-projector-port +ExecStop=/usr/bin/set-projector-power 0 +Restart=no + +[Install] +WantedBy=basic.target diff --git a/system-stuff/set-projector-power.service b/system-stuff/dlpc350-set-projector-power.service similarity index 100% rename from system-stuff/set-projector-power.service rename to system-stuff/dlpc350-set-projector-power.service From 57e2d5a0c15681baa01f4a0112b8943c9ef01c6c Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 17 Jan 2020 13:57:49 -0800 Subject: [PATCH 33/89] Update .gitignore. --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 81c9421e..ab1efc30 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ gpg/gpg-agent.conf gpg/openpgp-revocs.d gpg/private-keys-v1.d gpg/random_seed +installer model-library mountmon/.gdbinit mountmon/.qmake.stash @@ -20,5 +21,9 @@ mountmon/build newConfigureUbuntuAndInstallLightField.sh out packaging +src/version.h superclean.sh usb-driver/set-projector-power +usb-driver/dlpc350_api.o +usb-driver/dlpc350_usb.o +usb-driver/main.o From e17a3ff18ec0bb9dc8b140b9b4e3f0f0c7fa6562 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 20 Jan 2020 20:18:04 -0800 Subject: [PATCH 34/89] install-lightfield.sh: don't fail if fails --- install-lightfield.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install-lightfield.sh b/install-lightfield.sh index 787432d1..b43d3ee3 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -90,7 +90,7 @@ fi blue-bar • Building debugging version of set-projector-power # shellcheck disable=SC2164 cd ${PROJECTOR_SRC} -[ "${FORCEREBUILD}" = "-x" ] && make clean +[ "${FORCEREBUILD}" = "-x" ] && make clean || true make blue-bar • Building debugging version of Mountmon From ea56398752198bf4f554bf44c5b41dd2189c91bd Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 20 Jan 2020 20:30:50 -0800 Subject: [PATCH 35/89] Advanced tab: Re-enable temperature setting. --- src/advancedtab.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/advancedtab.h b/src/advancedtab.h index 70c92a84..fe9a6c55 100644 --- a/src/advancedtab.h +++ b/src/advancedtab.h @@ -1,7 +1,7 @@ #ifndef __ADVANCEDTAB_H__ #define __ADVANCEDTAB_H__ -#undef ENABLE_TEMPERATURE_SETTING +#define ENABLE_TEMPERATURE_SETTING #include "tabbase.h" From f9203b362ec5b591b3aef7ff26b6988429ae166c Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 17:13:29 -0800 Subject: [PATCH 36/89] stdio-shepherd: Open the right device. --- stdio-shepherd/printer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdio-shepherd/printer.py b/stdio-shepherd/printer.py index b1b6aa56..345ebad2 100644 --- a/stdio-shepherd/printer.py +++ b/stdio-shepherd/printer.py @@ -111,7 +111,7 @@ def on_online(self): if(self.onlinecb): self.onlinecb() - def connect(self,port=None,baud=250000): + def connect(self,port='/dev/lumen-arduino',baud=250000): baselist=[] for g in ['/dev/ttyUSB*', '/dev/ttyACM*']: baselist += glob.glob(g) From 6af996977725f6103c1d01c29c21130881a8a782 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 17:13:29 -0800 Subject: [PATCH 37/89] stdio-shepherd: Open the right device. --- stdio-shepherd/printer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdio-shepherd/printer.py b/stdio-shepherd/printer.py index b1b6aa56..345ebad2 100644 --- a/stdio-shepherd/printer.py +++ b/stdio-shepherd/printer.py @@ -111,7 +111,7 @@ def on_online(self): if(self.onlinecb): self.onlinecb() - def connect(self,port=None,baud=250000): + def connect(self,port='/dev/lumen-arduino',baud=250000): baselist=[] for g in ['/dev/ttyUSB*', '/dev/ttyACM*']: baselist += glob.glob(g) From 7e887ab92679f7a00c46faf0d43c062ca822b78e Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 18:07:55 -0800 Subject: [PATCH 38/89] A few small fixups after the merge. --- change-version-number.sh | 12 ++++++------ make-deb-package.sh | 4 ++-- usb-driver/Makefile | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/change-version-number.sh b/change-version-number.sh index 6055b74b..c482c392 100755 --- a/change-version-number.sh +++ b/change-version-number.sh @@ -88,12 +88,12 @@ then RELEASE_TRAIN=base fi -cat < Date: Tue, 21 Jan 2020 18:21:19 -0800 Subject: [PATCH 39/89] shared-stuff.sh: Update version number. --- shared-stuff.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-stuff.sh b/shared-stuff.sh index 2b797f04..18b8b903 100644 --- a/shared-stuff.sh +++ b/shared-stuff.sh @@ -43,7 +43,7 @@ fi ARCHITECTURE=amd64 RELEASE_TRAIN=base -VERSION=1.0.10.0 +VERSION=1.0.11.0 trap error-trap ERR set -e From 125776b9bce99828a2c731f6edeadb3fe1e69207 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 19:33:26 -0800 Subject: [PATCH 40/89] Fix permissions on script "reset-lumen-projector-port". --- install-lightfield.sh | 3 ++- make-deb-package.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/install-lightfield.sh b/install-lightfield.sh index e3c69c96..3cdd8f5a 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -90,6 +90,7 @@ fi blue-bar • Building debugging version of set-projector-power # shellcheck disable=SC2164 cd ${PROJECTOR_SRC} +# shellcheck disable=SC2015 [ "${FORCEREBUILD}" = "-x" ] && make BUILD=debug clean || true make BUILD=debug @@ -143,7 +144,7 @@ elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service /lib/systemd/system/set-projector-power.service install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules /lib/udev/rules.d/90-dlp4710.rules - install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-reset-lumen-projector-port /usr/share/lightfield/libexec/reset-lumen-projector-port + install ${VERBOSE} -DT -m 755 system-stuff/dlp4710-reset-lumen-projector-port /usr/share/lightfield/libexec/reset-lumen-projector-port install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf fi diff --git a/make-deb-package.sh b/make-deb-package.sh index bddf2c3e..efd69550 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -230,7 +230,7 @@ elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlp4710.rules" - install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-reset-lumen-projector-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-projector-port" + install ${VERBOSE} -DT -m 755 system-stuff/dlp4710-reset-lumen-projector-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-projector-port" install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" fi From 2f5050592775ab46893281507db44c025a264bde Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 20:09:10 -0800 Subject: [PATCH 41/89] install-lightfield.sh, make-deb-package.sh: Install set-projector-power for the right projector. --- install-lightfield.sh | 4 +++- make-deb-package.sh | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/install-lightfield.sh b/install-lightfield.sh index e3c69c96..4a5a1a4c 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -90,6 +90,7 @@ fi blue-bar • Building debugging version of set-projector-power # shellcheck disable=SC2164 cd ${PROJECTOR_SRC} +# shellcheck disable=SC2015 [ "${FORCEREBUILD}" = "-x" ] && make BUILD=debug clean || true make BUILD=debug @@ -129,7 +130,6 @@ install ${VERBOSE} -DT -m 600 -o lumen -g lumen gpg/trustdb.gpg install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service /lib/systemd/system/clean-up-mount-points.service install ${VERBOSE} -DT -m 755 build/lf /usr/bin/lf install ${VERBOSE} -DT -m 755 mountmon/build/mountmon /usr/bin/mountmon -install ${VERBOSE} -DT -m 755 usb-driver/set-projector-power /usr/bin/set-projector-power install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py /usr/share/lightfield/libexec/stdio-shepherd/printer.py install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py /usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port /usr/share/lightfield/libexec/reset-lumen-arduino-port @@ -138,11 +138,13 @@ if [ "${RELEASE_TRAIN}" = "base" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlpc350-set-projector-power.service /lib/systemd/system/set-projector-power.service install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules /lib/udev/rules.d/90-dlpc350.rules + install ${VERBOSE} -DT -m 755 usb-driver/set-projector-power /usr/bin/set-projector-power install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlpc350.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service /lib/systemd/system/set-projector-power.service install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules /lib/udev/rules.d/90-dlp4710.rules + install ${VERBOSE} -DT -m 755 dlp4710/set-projector-power /usr/bin/set-projector-power install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-reset-lumen-projector-port /usr/share/lightfield/libexec/reset-lumen-projector-port install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf fi diff --git a/make-deb-package.sh b/make-deb-package.sh index bddf2c3e..bca9b704 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -167,8 +167,6 @@ cd "${PROJECTOR_SRC}" [ -n "${FORCEREBUILD}" ] && make BUILD=debug clean || true make BUILD=debug -install ${VERBOSE} -DT -m 755 set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" - ################################################## blue-bar "• Building ${BUILDTYPE} version of Mountmon" @@ -225,11 +223,13 @@ if [ "${RELEASE_TRAIN}" = "base" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlpc350-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlpc350.rules" + install ${VERBOSE} -DT -m 755 usb-driver/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlpc350.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlp4710.rules" + install ${VERBOSE} -DT -m 755 dlp4710/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-reset-lumen-projector-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-projector-port" install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" fi From 837b888b9873f3c23585aed596f5a2ff4de5c44e Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 20:35:00 -0800 Subject: [PATCH 42/89] install-lightfield.sh, make-deb-package.sh: Make sure the DLP4710 set-projector-power binary is installed set-uid root. --- install-lightfield.sh | 2 +- make-deb-package.sh | 59 ++++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/install-lightfield.sh b/install-lightfield.sh index 3703c339..f39245e7 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -144,7 +144,7 @@ elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service /lib/systemd/system/set-projector-power.service install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules /lib/udev/rules.d/90-dlp4710.rules - install ${VERBOSE} -DT -m 755 dlp4710/set-projector-power /usr/bin/set-projector-power + install ${VERBOSE} -DT -m 4111 dlp4710/set-projector-power /usr/bin/set-projector-power install ${VERBOSE} -DT -m 755 system-stuff/dlp4710-reset-lumen-projector-port /usr/share/lightfield/libexec/reset-lumen-projector-port install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf fi diff --git a/make-deb-package.sh b/make-deb-package.sh index dc2b6cda..23b87fc9 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -164,6 +164,7 @@ blue-bar "• Building ${BUILDTYPE} version of set-projector-power" cd "${PROJECTOR_SRC}" +# shellcheck disable=SC2015 [ -n "${FORCEREBUILD}" ] && make BUILD=debug clean || true make BUILD=debug @@ -205,33 +206,33 @@ install ${VERBOSE} -DT -m 755 build/lf "${LIGHTFIELD_FILES}/usr/bin/lf" blue-bar "• Copying LightField files into packaging directory" -install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock "${LIGHTFIELD_FILES}/etc/apt/apt.conf.d/99untrustworthy-clock" -install ${VERBOSE} -DT -m 644 gpg/pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" -install ${VERBOSE} -DT -m 440 system-stuff/lumen-lightfield "${LIGHTFIELD_FILES}/etc/sudoers.d/lumen-lightfield" -install ${VERBOSE} -DT -m 644 system-stuff/getty@tty1.service.d_override.conf "${LIGHTFIELD_FILES}/etc/systemd/system/getty@tty1.service.d/override.conf" -install ${VERBOSE} -DT -m 600 system-stuff/lumen-bash_profile "${LIGHTFIELD_FILES}/home/lumen/.bash_profile" -install ${VERBOSE} -DT -m 600 system-stuff/lumen-real_bash_profile "${LIGHTFIELD_FILES}/home/lumen/.real_bash_profile" -install ${VERBOSE} -DT -m 600 gpg/pubring.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.gpg" -install ${VERBOSE} -DT -m 600 gpg/pubring.kbx "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.kbx" -install ${VERBOSE} -DT -m 600 gpg/trustdb.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/trustdb.gpg" -install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service "${LIGHTFIELD_FILES}/lib/systemd/system/clean-up-mount-points.service" -install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-arduino-port" -install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/printer.py" -install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py" +install ${VERBOSE} -DT -m 644 system-stuff/99untrustworthy-clock "${LIGHTFIELD_FILES}/etc/apt/apt.conf.d/99untrustworthy-clock" +install ${VERBOSE} -DT -m 644 gpg/pubring.gpg "${LIGHTFIELD_FILES}/etc/apt/trusted.gpg.d/volumetric-keyring.gpg" +install ${VERBOSE} -DT -m 440 system-stuff/lumen-lightfield "${LIGHTFIELD_FILES}/etc/sudoers.d/lumen-lightfield" +install ${VERBOSE} -DT -m 644 system-stuff/getty@tty1.service.d_override.conf "${LIGHTFIELD_FILES}/etc/systemd/system/getty@tty1.service.d/override.conf" +install ${VERBOSE} -DT -m 600 system-stuff/lumen-bash_profile "${LIGHTFIELD_FILES}/home/lumen/.bash_profile" +install ${VERBOSE} -DT -m 600 system-stuff/lumen-real_bash_profile "${LIGHTFIELD_FILES}/home/lumen/.real_bash_profile" +install ${VERBOSE} -DT -m 600 gpg/pubring.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.gpg" +install ${VERBOSE} -DT -m 600 gpg/pubring.kbx "${LIGHTFIELD_FILES}/home/lumen/.gnupg/pubring.kbx" +install ${VERBOSE} -DT -m 600 gpg/trustdb.gpg "${LIGHTFIELD_FILES}/home/lumen/.gnupg/trustdb.gpg" +install ${VERBOSE} -DT -m 644 system-stuff/clean-up-mount-points.service "${LIGHTFIELD_FILES}/lib/systemd/system/clean-up-mount-points.service" +install ${VERBOSE} -DT -m 755 system-stuff/reset-lumen-arduino-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-arduino-port" +install ${VERBOSE} -DT -m 644 stdio-shepherd/printer.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/printer.py" +install ${VERBOSE} -DT -m 755 stdio-shepherd/stdio-shepherd.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/stdio-shepherd/stdio-shepherd.py" if [ "${RELEASE_TRAIN}" = "base" ] then - install ${VERBOSE} -DT -m 644 system-stuff/dlpc350-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" - install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlpc350.rules" - install ${VERBOSE} -DT -m 755 usb-driver/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" - install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlpc350.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" + install ${VERBOSE} -DT -m 644 system-stuff/dlpc350-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" + install ${VERBOSE} -DT -m 644 usb-driver/90-dlpc350.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlpc350.rules" + install ${VERBOSE} -DT -m 755 usb-driver/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" + install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlpc350.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then - install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" - install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlp4710.rules" - install ${VERBOSE} -DT -m 755 dlp4710/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" - install ${VERBOSE} -DT -m 755 system-stuff/dlp4710-reset-lumen-projector-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-projector-port" - install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" + install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" + install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlp4710.rules" + install ${VERBOSE} -DT -m 4111 dlp4710/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" + install ${VERBOSE} -DT -m 755 system-stuff/dlp4710-reset-lumen-projector-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-projector-port" + install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" fi chmod ${CHXXXVERBOSE} -R go= "${LIGHTFIELD_FILES}/home/lumen/.gnupg" @@ -242,13 +243,13 @@ blue-bar "• Copying printrun files into packaging directory" cd "${PRINTRUN_SRC}" -install ${VERBOSE} -DT -m 644 printrun/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/__init__.py" -install ${VERBOSE} -DT -m 644 printrun/eventhandler.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/eventhandler.py" -install ${VERBOSE} -DT -m 644 printrun/gcoder.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/gcoder.py" -install ${VERBOSE} -DT -m 644 printrun/printcore.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/printcore.py" -install ${VERBOSE} -DT -m 644 printrun/utils.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/utils.py" -install ${VERBOSE} -DT -m 644 printrun/plugins/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/plugins/__init__.py" -install ${VERBOSE} -DT -m 644 Util/constants.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/Util/constants.py" +install ${VERBOSE} -DT -m 644 printrun/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/__init__.py" +install ${VERBOSE} -DT -m 644 printrun/eventhandler.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/eventhandler.py" +install ${VERBOSE} -DT -m 644 printrun/gcoder.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/gcoder.py" +install ${VERBOSE} -DT -m 644 printrun/printcore.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/printcore.py" +install ${VERBOSE} -DT -m 644 printrun/utils.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/utils.py" +install ${VERBOSE} -DT -m 644 printrun/plugins/__init__.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/printrun/plugins/__init__.py" +install ${VERBOSE} -DT -m 644 Util/constants.py "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/printrun/Util/constants.py" ################################################## From f78b95a2f2e72e1711f49fda9b40246056a98a90 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 21:26:04 -0800 Subject: [PATCH 43/89] DLP4710: Support correct power levels. --- qt/lf.pro | 4 ++++ rebuild | 28 +++++++++++++++++++--------- shared-stuff.sh | 2 +- src/advancedtab.cpp | 4 ++-- src/constants.h | 40 ++++++++++++++++++++++++++++------------ src/printjob.h | 4 ++-- src/printtab.cpp | 4 ++-- src/utils.h | 4 ++-- 8 files changed, 60 insertions(+), 30 deletions(-) diff --git a/qt/lf.pro b/qt/lf.pro index 87cbdc3c..91179fb6 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -111,3 +111,7 @@ static { debug { QMAKE_CXXFLAGS_WARN_ON += -Wno-class-memaccess } + +dlp4710 { + DEFINES += DLP4710 +} diff --git a/rebuild b/rebuild index 4189503b..c276d980 100755 --- a/rebuild +++ b/rebuild @@ -1,6 +1,11 @@ -#!/usr/bin/env bash +#!/bin/bash -[ -z "${BUILDDIR}" ] && export BUILDDIR="/home/lumen/Volumetric/LightField/build" +LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField + +# shellcheck disable=SC1090 +source "${LIGHTFIELD_ROOT}/shared-stuff.sh" + +[ -z "${BUILDDIR}" ] && export BUILDDIR="${LIGHTFIELD_ROOT}/build" function usage () { cat < "${BUILDDIR}/.lastbuildmode" + echo "${BUILD}-${RELEASE_TRAIN}" > "${BUILDDIR}/.lastbuildmode" NUKE=yes fi LASTBUILDMODE="$(<${BUILDDIR}/.lastbuildmode)" - if [ -n "${LASTBUILDMODE}" ] && [ "${BUILD}" != "${LASTBUILDMODE}" ] + if [ -n "${LASTBUILDMODE}" ] && [ "${LASTBUILDMODE}" != "${BUILD}-${RELEASE_TRAIN}" ] then - echo Switching from "${LASTBUILDMODE}" to "${BUILD}". + echo Switching from "${LASTBUILDMODE}" to "${BUILD}-${RELEASE_TRAIN}". NUKE=yes fi @@ -79,6 +87,7 @@ function rebuild () { mkdir -p "${BUILDDIR}" fi + # shellcheck disable=SC2164 cd "${BUILDDIR}" if [ -n "${CLEAN}" ] @@ -89,8 +98,8 @@ function rebuild () { if [ -n "${QMAKE}" ] || [ ! -f Makefile ] || [ ../qt/lf.pro -nt Makefile ] then - qmake CONFIG+="${BUILD}" ../qt/lf.pro || return - echo "${BUILD}" > "${BUILDDIR}/.lastbuildmode" + qmake CONFIG+="${BUILD}" ${ADDTOCONFIG} ../qt/lf.pro || return + echo "${BUILD}-${RELEASE_TRAIN}" > "${BUILDDIR}/.lastbuildmode" fi if make -q 1> /dev/null 2>&1 @@ -99,8 +108,9 @@ function rebuild () { return fi - rm buildinfo.o 1> /dev/null 2>&1 + # shellcheck disable=SC2015 + [ -f buildinfo.o ] && rm buildinfo.o 1> /dev/null 2>&1 || true make -j"${QT_BUILD_JOBS-12}" } -rebuild $* +rebuild "$@" diff --git a/shared-stuff.sh b/shared-stuff.sh index 18b8b903..13607214 100644 --- a/shared-stuff.sh +++ b/shared-stuff.sh @@ -42,7 +42,7 @@ then fi ARCHITECTURE=amd64 -RELEASE_TRAIN=base +RELEASE_TRAIN=dlp4710 VERSION=1.0.11.0 trap error-trap ERR diff --git a/src/advancedtab.cpp b/src/advancedtab.cpp index edad9822..0cccec16 100644 --- a/src/advancedtab.cpp +++ b/src/advancedtab.cpp @@ -155,8 +155,8 @@ AdvancedTab::AdvancedTab( QWidget* parent ): TabBase( parent ) { _powerLevelValueLayout = WrapWidgetsInHBox( _powerLevelLabel, nullptr, _powerLevelValue ); _powerLevelSlider->setEnabled( false ); - _powerLevelSlider->setMinimum( 20 ); - _powerLevelSlider->setMaximum( 100 ); + _powerLevelSlider->setMinimum( ProjectorMinPercent ); + _powerLevelSlider->setMaximum( ProjectorMaxPercent ); _powerLevelSlider->setOrientation( Qt::Horizontal ); _powerLevelSlider->setPageStep( 5 ); _powerLevelSlider->setSingleStep( 1 ); diff --git a/src/constants.h b/src/constants.h index 54455476..fea6a83f 100644 --- a/src/constants.h +++ b/src/constants.h @@ -41,22 +41,38 @@ QChar extern const FA_Backward; QChar extern const FA_Forward; QChar extern const FA_FastForward; -int const DebugLogPathCount = 6; +int const DebugLogPathCount = 6; char extern const* DebugLogPaths[DebugLogPathCount]; -double const PrinterMaximumX = 64.00; // mm -double const PrinterMaximumY = 40.00; // mm -double const PrinterMaximumZ = 50.00; // mm -double const PrinterRaiseToMaximumZ = 60.00; // mm -double const PrinterHighSpeedThresholdZ = 10.00; // mm +double constexpr const PrinterMaximumX = 64.00; // mm +double constexpr const PrinterMaximumY = 40.00; // mm +double constexpr const PrinterMaximumZ = 50.00; // mm +double constexpr const PrinterRaiseToMaximumZ = 60.00; // mm +double constexpr const PrinterHighSpeedThresholdZ = 10.00; // mm -double const PrinterDefaultHighSpeed = 200.00; // mm/min -double const PrinterDefaultLowSpeed = 50.00; // mm/min +double constexpr const PrinterDefaultHighSpeed = 200.00; // mm/min +double constexpr const PrinterDefaultLowSpeed = 50.00; // mm/min -double const AspectRatio5to3 = 5.0 / 3.0; -double const AspectRatio16to10 = 16.0 / 10.0; +double constexpr const AspectRatio5to3 = 5.0 / 3.0; +double constexpr const AspectRatio16to10 = 16.0 / 10.0; -double const LargeFontSize = 22.0; // pt -double const NormalFontSize = 12.0; // pt +double constexpr const LargeFontSize = 22.0; // pt +double constexpr const NormalFontSize = 12.0; // pt + +# if defined DLP4710 + +double constexpr const ProjectorMaxPowerLevel = 1023.0; + +int constexpr const ProjectorMinPercent = 5; +int constexpr const ProjectorMaxPercent = 80; + +# else + +double constexpr const ProjectorMaxPowerLevel = 255.0; + +int constexpr const ProjectorMinPercent = 20; +int constexpr const ProjectorMaxPercent = 80; + +# endif #endif // __CONSTANTS_H__ diff --git a/src/printjob.h b/src/printjob.h index 18a58acf..e37ee5cd 100644 --- a/src/printjob.h +++ b/src/printjob.h @@ -48,8 +48,8 @@ class PrintJob { int layerThickness { 100 }; // unit: µm double exposureTime { 1.0 }; // unit: s double exposureTimeScaleFactor { 1.0 }; // for first two layers - int powerLevel { 128 }; // range: 0..255 - double printSpeed { PrinterDefaultLowSpeed }; // unit: mm/min; range: 50-200 + int powerLevel { static_cast( ProjectorMaxPowerLevel / 2.0 + 0.5 ) }; // range: 0..ProjectorMaxPowerLevel + double printSpeed { PrinterDefaultLowSpeed }; // unit: mm/min; range: 50-200 }; diff --git a/src/printtab.cpp b/src/printtab.cpp index 5f1147d0..7aaf0fc1 100644 --- a/src/printtab.cpp +++ b/src/printtab.cpp @@ -70,8 +70,8 @@ PrintTab::PrintTab( QWidget* parent ): InitialShowEventMixin( _powerLevelValue->setAlignment( Qt::AlignRight ); _powerLevelValue->setFont( boldFont ); - _powerLevelSlider->setMinimum( 20 ); - _powerLevelSlider->setMaximum( 100 ); + _powerLevelSlider->setMinimum( ProjectorMinPercent ); + _powerLevelSlider->setMaximum( ProjectorMaxPercent ); _powerLevelSlider->setOrientation( Qt::Horizontal ); _powerLevelSlider->setPageStep( 5 ); _powerLevelSlider->setSingleStep( 1 ); diff --git a/src/utils.h b/src/utils.h index b68fc614..805c1cb4 100644 --- a/src/utils.h +++ b/src/utils.h @@ -30,11 +30,11 @@ inline QString RemoveFileExtension( QString const& fileName ) { } inline constexpr int PercentagePowerLevelToRawLevel( int percentage ) { - return static_cast( static_cast( percentage ) / 100.0 * 255.0 + 0.5 ); + return static_cast( static_cast( percentage ) / 100.0 * ProjectorMaxPowerLevel + 0.5 ); } inline constexpr int RawPowerLevelToPercentage( int raw ) { - return static_cast( static_cast( raw ) / 255.0 * 100.0 + 0.5 ); + return static_cast( static_cast( raw ) / ProjectorMaxPowerLevel * 100.0 + 0.5 ); } template From ec4c484cb146286c8604b6fa8179f854876e4aca Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 21:54:51 -0800 Subject: [PATCH 44/89] Add new images for DLP4710, and build-time selection. --- images/dlp4710-focus-image.png | Bin 0 -> 256396 bytes images/dlp4710-white-field.png | Bin 0 -> 452 bytes ...focus-image.png => dlpc350-focus-image.png} | Bin ...white-field.png => dlpc350-white-field.png} | Bin rebuild | 10 ++++++++++ 5 files changed, 10 insertions(+) create mode 100644 images/dlp4710-focus-image.png create mode 100644 images/dlp4710-white-field.png rename images/{focus-image.png => dlpc350-focus-image.png} (100%) rename images/{white-field.png => dlpc350-white-field.png} (100%) diff --git a/images/dlp4710-focus-image.png b/images/dlp4710-focus-image.png new file mode 100644 index 0000000000000000000000000000000000000000..c371c01ff9c7329501782b46db12b68902bb97c7 GIT binary patch literal 256396 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 z

S-novmg3$!AL8-iz5()0%$oS%zZ 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 z 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 literal 0 HcmV?d00001 diff --git a/images/dlp4710-white-field.png b/images/dlp4710-white-field.png new file mode 100644 index 0000000000000000000000000000000000000000..917f61113c46bff9fbbf31d6d6a1aaf2f9a6329d GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0y~yU^~FTz+%D33>4|r>`nkuEa{HEjtmSN`?>!lvI6;x z#X;^)4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#N zP=d3-BeIx*f$uN~Gak=hkpdKC4)6(a{r~^}@*~nGfZSi6E{-7*l5YQ~oN|d-plmzFem6RtIr7{#GX6BXX<)xM=nCKbmnWnf$ R0ON^)!PC{xWt~$(69D~|J^=s# literal 0 HcmV?d00001 diff --git a/images/focus-image.png b/images/dlpc350-focus-image.png similarity index 100% rename from images/focus-image.png rename to images/dlpc350-focus-image.png diff --git a/images/white-field.png b/images/dlpc350-white-field.png similarity index 100% rename from images/white-field.png rename to images/dlpc350-white-field.png diff --git a/rebuild b/rebuild index c276d980..9b720397 100755 --- a/rebuild +++ b/rebuild @@ -102,6 +102,16 @@ function rebuild () { echo "${BUILD}-${RELEASE_TRAIN}" > "${BUILDDIR}/.lastbuildmode" fi + if [ "${RELEASE_TRAIN}" = "base" ] + then + cp ../images/dlpc350-focus-image.png ../images/focus-image.png + cp ../images/dlpc350-white-field.png ../images/white-field.png + elif [ "${RELEASE_TRAIN}" = "dlp4710" ] + then + cp ../images/dlp4710-focus-image.png ../images/focus-image.png + cp ../images/dlp4710-white-field.png ../images/white-field.png + fi + if make -q 1> /dev/null 2>&1 then echo "make: Nothing to be done for 'first'." From 22a529bdba2cc08448842ecfb7716ab9baa19ad6 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 21 Jan 2020 22:14:08 -0800 Subject: [PATCH 45/89] Fix size of DLP4710 white-field.png. --- images/dlp4710-white-field.png | Bin 452 -> 4400 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/dlp4710-white-field.png b/images/dlp4710-white-field.png index 917f61113c46bff9fbbf31d6d6a1aaf2f9a6329d..c3b1c11b4fed353dbc8d932b678dc369aa899883 100644 GIT binary patch literal 4400 zcmeAS@N?(olHy`uVBq!ia0y~yU~gbxV6or;3NW1AF>gMQVo7)Ob!1@J*w6hZkrl{i zEDmyaVpw-h<|UA$kn9oU%fL{j#=y|f!octgDAe$RfuYoZf#FpG1B2BJ1_tqhIlBUF zfD)Vq9+AZi419+{nDKc2iWCM0fl^Nw$B>F!Z?76MGAQsIFgURP`NJkT6|P^U=b0Gd ztV)KM%#*`ZRF9u!DyFhw5vGUMaCF8Xyl&wi8J4BVZJ7CR6?S}HKHUq rKdq!Zu_%?HATcwqL@zJ3M8QPQP|q~QJpwo;!oc9^>gTe~DWM4f!SLOn literal 452 zcmeAS@N?(olHy`uVBq!ia0y~yU^~FTz+%D33>4|r>`nkuEa{HEjtmSN`?>!lvI6;x z#X;^)4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#N zP=d3-BeIx*f$uN~Gak=hkpdKC4)6(a{r~^}@*~nGfZSi6E{-7*l5YQ~oN|d-plmzFem6RtIr7{#GX6BXX<)xM=nCKbmnWnf$ R0ON^)!PC{xWt~$(69D~|J^=s# From 211304744fcf79fdc42e1518a76cf9fb8e62edd9 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Wed, 22 Jan 2020 13:29:35 -0800 Subject: [PATCH 46/89] dlp4710-white-field.png: Optimize compression. (cherry picked from commit 703ece9911200852e38e5c5b67a426f1dc9c40b6) --- images/dlp4710-white-field.png | Bin 4400 -> 476 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/dlp4710-white-field.png b/images/dlp4710-white-field.png index c3b1c11b4fed353dbc8d932b678dc369aa899883..62fda7d218958842e303d32ef8f64615f2252769 100644 GIT binary patch literal 476 zcmeAS@N?(olHy`uVBq!ia0y~yU~gbxV6k9i28vvg4F-}7Ea{HEjtmSN`?>!lvI6;x z#X;^)4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#N zP=d3-BeIx*f$uN~Gak=hkpdKC4)6(a{r~^}@*~nGfMyGMx;TbNNWMMD$P1J_r0{!r z9X~q{kU3JpgODFj85jjb&wK=ifJBLFL`iUdT1k0gQ7S`0VrE{6US4X6f{C7?o@t7E S1Tg*>7(8A5T-G@yGywoVWIR3q literal 4400 zcmeAS@N?(olHy`uVBq!ia0y~yU~gbxV6or;3NW1AF>gMQVo7)Ob!1@J*w6hZkrl{i zEDmyaVpw-h<|UA$kn9oU%fL{j#=y|f!octgDAe$RfuYoZf#FpG1B2BJ1_tqhIlBUF zfD)Vq9+AZi419+{nDKc2iWCM0fl^Nw$B>F!Z?76MGAQsIFgURP`NJkT6|P^U=b0Gd ztV)KM%#*`ZRF9u!DyFhw5vGUMaCF8Xyl&wi8J4BVZJ7CR6?S}HKHUq rKdq!Zu_%?HATcwqL@zJ3M8QPQP|q~QJpwo;!oc9^>gTe~DWM4f!SLOn From 9ae9cb67f5cf3e914e6c10173f827278b566ef3a Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Wed, 22 Jan 2020 13:29:35 -0800 Subject: [PATCH 47/89] dlp4710-white-field.png: Optimize compression. (cherry picked from commit 703ece9911200852e38e5c5b67a426f1dc9c40b6) From 6c2e4772b21055370530dfef438e4beca4fa5b70 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Wed, 22 Jan 2020 13:30:58 -0800 Subject: [PATCH 48/89] Add original vector source for {dlpc350,dlp4710}-focus-image.png. Image sourced from Wikimedia Commons at https://commons.wikimedia.org/wiki/File:EIA_Resolution_Chart_1956.svg Created by Commons user "BPK" (https://commons.wikimedia.org/wiki/User:BPK). Uploaded on 2009-05-20. Declared to be in the public domain. (cherry picked from commit 9a11e431d9cba0d7e720d597f1143ed43d5f760b) --- images/focus-image.svg | 526 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 526 insertions(+) create mode 100644 images/focus-image.svg diff --git a/images/focus-image.svg b/images/focus-image.svg new file mode 100644 index 00000000..ab903ab5 --- /dev/null +++ b/images/focus-image.svg @@ -0,0 +1,526 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 300 + + + 350 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 200 + + + 200 + + + 200 + + + 200 + + + 300 + + + 300 + + + 300 + + + 300 + + + 400 + + + 400 + + + 400 + + + 400 + + + 500 + + + 500 + + + 500 + + + 500 + + + 600 + + + 600 + + + 600 + + + 600 + + + + + + + + + + + + + + + + + + + + + + + 150 + + + 150 + + + 150 + + + 150 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 200 + + + 200 + + + 200 + + + 200 + + + 200 + + + 200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 200 + + + 200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + 9 + + 8 + 0 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 4 + + 3 + + 2 + + + + + + + + + + + + + EIA + RESOLUTION + + CHART + + 1956 + + + + + + + + 700 + + + 800 + + + 600 + + + 500 + + + 400 + + + 400 + + + 300 + + + 200 + + + + + + + + + + + 700 + + + 800 + + + 600 + + + 500 + + + 400 + + + 400 + + + 300 + + + 200 + + + + + + + + + + + 700 + + + 800 + + + 600 + + + 500 + + + 400 + + + 400 + + + 300 + + + 200 + + + + + + + + + + + + 700 + + + 800 + + + 600 + + + 500 + + + 400 + + + 400 + + + 300 + + + 200 + + + + + + + + + + + From dfb8017ff2d500e5f9740de4bf2efa8200b54ae1 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 24 Jan 2020 14:12:43 -0800 Subject: [PATCH 49/89] Print profiles: Add new classes. src/printprofile.{cpp,h}: new class PrintProfile. src/printprofilemanager.{cpp,h}: new class PrintProfileManager. --- CMakeLists.txt | 4 +++ qt/lf.pro | 4 +++ src/printprofile.cpp | 3 ++ src/printprofile.h | 49 +++++++++++++++++++++++++++++ src/printprofilemanager.cpp | 11 +++++++ src/printprofilemanager.h | 61 +++++++++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+) create mode 100644 src/printprofile.cpp create mode 100644 src/printprofile.h create mode 100644 src/printprofilemanager.cpp create mode 100644 src/printprofilemanager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d6b0cc9..8821f5a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,8 @@ src/mesh.cpp src/pngdisplayer.cpp src/preparetab.cpp src/printmanager.cpp +src/printprofile.cpp +src/printprofilemanager.cpp src/printtab.cpp src/processrunner.cpp src/shepherd.cpp @@ -93,6 +95,8 @@ src/pngdisplayer.h src/preparetab.h src/printjob.h src/printmanager.h +src/printprofile.h +src/printprofilemanager.h src/printtab.h src/processrunner.h src/shepherd.h diff --git a/qt/lf.pro b/qt/lf.pro index 91179fb6..8be7d70e 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -29,6 +29,8 @@ SOURCES += \ ../src/pngdisplayer.cpp \ ../src/preparetab.cpp \ ../src/printmanager.cpp \ + ../src/printprofile.cpp \ + ../src/printprofilemanager.cpp \ ../src/printtab.cpp \ ../src/processrunner.cpp \ ../src/shepherd.cpp \ @@ -71,6 +73,8 @@ HEADERS += \ ../src/preparetab.h \ ../src/printjob.h \ ../src/printmanager.h \ + ../src/printprofile.h \ + ../src/printprofilemanager.h \ ../src/printtab.h \ ../src/processrunner.h \ ../src/shepherd.h \ diff --git a/src/printprofile.cpp b/src/printprofile.cpp new file mode 100644 index 00000000..82547d4e --- /dev/null +++ b/src/printprofile.cpp @@ -0,0 +1,3 @@ +#include "pch.h" + +#include "printprofile.h" diff --git a/src/printprofile.h b/src/printprofile.h new file mode 100644 index 00000000..4fb43aa0 --- /dev/null +++ b/src/printprofile.h @@ -0,0 +1,49 @@ +#ifndef __PRINTPROFILE_H__ +#define __PRINTPROFILE_H__ + +class PrintProfile: public QObject { + + Q_OBJECT; + +public: + + PrintProfile( QObject* parent = nullptr ): QObject( parent ) { + /*empty*/ + } + + virtual ~PrintProfile( ) override { + /*empty*/ + } + + QString const& profileName( ) const { + return _name; + } + + void setProfileName( QString const& newName ) { + emit profileNameChanged( newName ); + _name = newName; + } + +protected: + +private: + + QString _name; + +signals: + ; + + void profileNameChanged( QString const& newName ); + +public slots: + ; + +protected slots: + ; + +private slots: + ; + +}; + +#endif // !__PRINTPROFILE_H__ diff --git a/src/printprofilemanager.cpp b/src/printprofilemanager.cpp new file mode 100644 index 00000000..1c5fe99b --- /dev/null +++ b/src/printprofilemanager.cpp @@ -0,0 +1,11 @@ +#include "pch.h" + +#include "printprofilemanager.h" + +bool PrintProfileManager::importProfiles( QString const& /*fileName*/ ) { + return false; +} + +bool PrintProfileManager::exportProfiles( QString const& /*fileName*/ ) { + return false; +} diff --git a/src/printprofilemanager.h b/src/printprofilemanager.h new file mode 100644 index 00000000..9f0dd809 --- /dev/null +++ b/src/printprofilemanager.h @@ -0,0 +1,61 @@ +#ifndef __PRINTPROFILEMANAGER_H__ +#define __PRINTPROFILEMANAGER_H__ + +#include "printprofile.h" + +using PrintProfileCollection = QVector; + +class PrintProfileManager: public QObject { + + Q_OBJECT; + +public: + + PrintProfileManager( QObject* parent = nullptr ): QObject( parent ) { + /*empty*/ + } + + virtual ~PrintProfileManager( ) override { + /*empty*/ + } + + bool importProfiles( QString const& fileName ); + bool exportProfiles( QString const& fileName ); + + PrintProfile const* activeProfile( ) { + return _activeProfile; + } + + void setActiveProfile( PrintProfile* newProfile ) { + emit activeProfileChanged( newProfile ); + _activeProfile = newProfile; + } + + PrintProfileCollection const* profiles( ) { + return _profiles; + } + +protected: + +private: + + PrintProfile* _activeProfile { }; + PrintProfileCollection* _profiles { new PrintProfileCollection }; + +signals: + ; + + void activeProfileChanged( PrintProfile const* newProfile ); + +public slots: + ; + +protected slots: + ; + +private slots: + ; + +}; + +#endif //!__PRINTPROFILEMANAGER_H__ From 9b38c3a1574f9f4071d04f1639b93a5ebd8200ca Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 24 Jan 2020 14:47:11 -0800 Subject: [PATCH 50/89] DLP4710: only run `set-projector-power --first-time` in the 'dlp4710' release train. (cherry picked from commit b575737dab58d53a0d9b6cb54f627bac69262ff5) --- debian/lightfield.postinst.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/debian/lightfield.postinst.in b/debian/lightfield.postinst.in index fe284696..f5dda33d 100644 --- a/debian/lightfield.postinst.in +++ b/debian/lightfield.postinst.in @@ -42,7 +42,10 @@ case "$1" in systemctl enable getty@tty1.service systemctl daemon-reload - set-projector-power --first-time + if [ "${RELEASE_TRAIN}" = "dlp4710" ] + then + set-projector-power --first-time + fi ;; abort-upgrade|abort-remove|abort-deconfigure) From cb8e3e6b835148219d5e45c2138fc13cd98c75b8 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 25 Jan 2020 12:38:51 -0800 Subject: [PATCH 51/89] make-deb-package.sh: Fix permissions on DLP4710 set-projector-power executable. (cherry picked from commit fb944485faeaade490471ede2d19831e8145cd63) --- install-lightfield.sh | 2 +- make-deb-package.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install-lightfield.sh b/install-lightfield.sh index f39245e7..315bd28b 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -144,7 +144,7 @@ elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service /lib/systemd/system/set-projector-power.service install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules /lib/udev/rules.d/90-dlp4710.rules - install ${VERBOSE} -DT -m 4111 dlp4710/set-projector-power /usr/bin/set-projector-power + install ${VERBOSE} -DT -m 4555 dlp4710/set-projector-power /usr/bin/set-projector-power install ${VERBOSE} -DT -m 755 system-stuff/dlp4710-reset-lumen-projector-port /usr/share/lightfield/libexec/reset-lumen-projector-port install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf /usr/share/X11/xorg.conf.d/99-waveshare.conf fi diff --git a/make-deb-package.sh b/make-deb-package.sh index 23b87fc9..d90d69ea 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -230,7 +230,7 @@ elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then install ${VERBOSE} -DT -m 644 system-stuff/dlp4710-set-projector-power.service "${LIGHTFIELD_FILES}/lib/systemd/system/set-projector-power.service" install ${VERBOSE} -DT -m 644 dlp4710/90-dlp4710.rules "${LIGHTFIELD_FILES}/lib/udev/rules.d/90-dlp4710.rules" - install ${VERBOSE} -DT -m 4111 dlp4710/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" + install ${VERBOSE} -DT -m 4555 dlp4710/set-projector-power "${LIGHTFIELD_FILES}/usr/bin/set-projector-power" install ${VERBOSE} -DT -m 755 system-stuff/dlp4710-reset-lumen-projector-port "${LIGHTFIELD_FILES}/usr/share/lightfield/libexec/reset-lumen-projector-port" install ${VERBOSE} -DT -m 644 system-stuff/99-waveshare-dlp4710.conf "${LIGHTFIELD_FILES}/usr/share/X11/xorg.conf.d/99-waveshare.conf" fi From 3c916befea95cb7e3bc260479102063207043256 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sat, 25 Jan 2020 12:39:23 -0800 Subject: [PATCH 52/89] make-upgrade-kit.sh: Fixes to multiple release train support. (cherry picked from commit d325229e36fcd6e049ccb13921d1c3ba8ea60463) --- make-upgrade-kit.sh | 36 ++++++++++++++---------------------- shared-stuff.sh | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index f57f901a..9336330e 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -23,7 +23,7 @@ Where: -q build quietly both create both kits If the build is successful, the requested upgrade kit(s) will be found in - ${KIT_DIR}/lightfield$([ "${DEFAULT_RELEASE_TRAIN}" = "base" ] || echo "-${DEFAULT_RELEASE_TRAIN}" )-BUILDTYPE_${VERSION}_${DEFAULT_ARCHITECTURE}.kit + ${PACKAGE_BUILD_ROOT}/VERSION[-RELEASE_TRAIN]-BUILDTYPE/kit/lightfield[-RELEASE_TRAIN]-BUILDTYPE_VERSION_ARCHITECTURE.kit EOF exit 1 } @@ -88,20 +88,7 @@ if [ -z "${RELEASE_TRAIN}" ] then RELEASE_TRAIN=base fi - -if [ "${RELEASE_TRAIN}" = "base" ] -then - SUFFIX=${BUILDTYPE} -else - SUFFIX=${RELEASE_TRAIN}-${BUILDTYPE} -fi - -if [ "${BUILDTYPE}" = "both" ] -then - "${0}" "${ARGS}" release || exit $? - "${0}" "${ARGS}" debug || exit $? - exit 0 -fi +SUFFIX=$(generate-suffix "${RELEASE_TRAIN}" "${BUILDTYPE}") if [ "${USE_KEY_SET}" = "current" ] then @@ -116,8 +103,8 @@ else fi PACKAGE_BUILD_DIR="${PACKAGE_BUILD_ROOT}/${VERSION}-${SUFFIX}" -DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" KIT_DIR="${PACKAGE_BUILD_DIR}/kit" +DEB_BUILD_DIR="${PACKAGE_BUILD_DIR}/deb" REPO_DIR="${PACKAGE_BUILD_DIR}/repo" RELEASEDATE=$(date "+%Y-%m-%d") @@ -131,7 +118,12 @@ mkdir ${VERBOSE} -p "${REPO_DIR}" mkdir ${VERBOSE} -p "${KIT_DIR}" install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${LIGHTFIELD_ROOT}/fonts-montserrat_7.200_all.deb" -install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-common_${VERSION}_all.deb" +if [ "${RELEASE_TRAIN}" = "base" ] +then + install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-common_${VERSION}_all.deb" +else + install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-${RELEASE_TRAIN}-common_${VERSION}_all.deb" +fi if [ "${BUILDTYPE}" = "release" ] then @@ -146,11 +138,11 @@ then then install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.ddeb" else - red-bar "!!! Unable to find either" - red-bar "!!! ${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.deb" - red-bar "!!! or" - red-bar "!!! ${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.ddeb" - red-bar "!!! Build failed!" + red-bar "!!! Unable to find either" + red-bar "!!! ${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.deb" + red-bar "!!! or" + red-bar "!!! ${DEB_BUILD_DIR}/lightfield-${SUFFIX}-dbgsym_${VERSION}_${ARCHITECTURE}.ddeb" + red-bar "!!! Build failed!" fi fi diff --git a/shared-stuff.sh b/shared-stuff.sh index 13607214..174d9aaf 100644 --- a/shared-stuff.sh +++ b/shared-stuff.sh @@ -34,6 +34,23 @@ function apply-assignment-substitution () { sed -i 's/^'"${1}"'=.*$/'"${1}"'='"${2}"'/g' "${@:3}" } +function generate-suffix () { + local RELEASE_TRAIN="${1}" + local BUILDTYPE="${2}" + + if [ -z "${RELEASE_TRAIN}" ] + then + RELEASE_TRAIN=base + fi + + if [ "${RELEASE_TRAIN}" = "base" ] + then + echo "${BUILDTYPE}" + else + echo "${RELEASE_TRAIN}-${BUILDTYPE}" + fi +} + DEFAULT_RELEASE_TRAIN=base DEFAULT_ARCHITECTURE=$(uname -m) if [ "${DEFAULT_ARCHITECTURE}" = "x86_64" ] From 875301f8061e5dddc25a33995ab41bcd87205fa8 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sun, 26 Jan 2020 11:17:11 -0800 Subject: [PATCH 53/89] Print profiles: Add new class PrintPumpingParameters. --- CMakeLists.txt | 1 + qt/lf.pro | 169 ++++++++++++++++++----------------- src/printprofile.h | 39 +++++++- src/printprofilemanager.cpp | 39 ++++++++ src/printprofilemanager.h | 22 ++--- src/printpumpingparameters.h | 131 +++++++++++++++++++++++++++ 6 files changed, 306 insertions(+), 95 deletions(-) create mode 100644 src/printpumpingparameters.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8821f5a3..b16a0f28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ src/printjob.h src/printmanager.h src/printprofile.h src/printprofilemanager.h +src/printpumpingparameters.h src/printtab.h src/processrunner.h src/shepherd.h diff --git a/qt/lf.pro b/qt/lf.pro index 8be7d70e..f289d5aa 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -7,92 +7,93 @@ QMAKE_CXXFLAGS_RELEASE -= -O2 QMAKE_CXXFLAGS_RELEASE += -O3 -DNDEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result QMAKE_CXXFLAGS_DEBUG += -Og -D_DEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result -SOURCES += \ - ../src/advancedtab.cpp \ - ../src/app.cpp \ - ../src/backdrop.cpp \ - ../src/buildinfo.cpp \ - ../src/canvas.cpp \ - ../src/constants.cpp \ - ../src/debug.cpp \ - ../src/debuglogcopier.cpp \ - ../src/filecopier.cpp \ - ../src/filetab.cpp \ - ../src/gesturelistview.cpp \ - ../src/glmesh.cpp \ - ../src/gpgsignaturechecker.cpp \ - ../src/hasher.cpp \ - ../src/lightfieldstyle.cpp \ - ../src/loader.cpp \ - ../src/main.cpp \ - ../src/mesh.cpp \ - ../src/pngdisplayer.cpp \ - ../src/preparetab.cpp \ - ../src/printmanager.cpp \ - ../src/printprofile.cpp \ - ../src/printprofilemanager.cpp \ - ../src/printtab.cpp \ - ../src/processrunner.cpp \ - ../src/shepherd.cpp \ - ../src/signalhandler.cpp \ - ../src/statustab.cpp \ - ../src/stdiologger.cpp \ - ../src/strings.cpp \ - ../src/svgrenderer.cpp \ - ../src/systemtab.cpp \ - ../src/tabbase.cpp \ - ../src/timinglogger.cpp \ - ../src/upgradekitunpacker.cpp \ - ../src/upgrademanager.cpp \ - ../src/upgradeselector.cpp \ - ../src/usbmountmanager.cpp \ - ../src/utils.cpp \ +SOURCES += \ + ../src/advancedtab.cpp \ + ../src/app.cpp \ + ../src/backdrop.cpp \ + ../src/buildinfo.cpp \ + ../src/canvas.cpp \ + ../src/constants.cpp \ + ../src/debug.cpp \ + ../src/debuglogcopier.cpp \ + ../src/filecopier.cpp \ + ../src/filetab.cpp \ + ../src/gesturelistview.cpp \ + ../src/glmesh.cpp \ + ../src/gpgsignaturechecker.cpp \ + ../src/hasher.cpp \ + ../src/lightfieldstyle.cpp \ + ../src/loader.cpp \ + ../src/main.cpp \ + ../src/mesh.cpp \ + ../src/pngdisplayer.cpp \ + ../src/preparetab.cpp \ + ../src/printmanager.cpp \ + ../src/printprofile.cpp \ + ../src/printprofilemanager.cpp \ + ../src/printtab.cpp \ + ../src/processrunner.cpp \ + ../src/shepherd.cpp \ + ../src/signalhandler.cpp \ + ../src/statustab.cpp \ + ../src/stdiologger.cpp \ + ../src/strings.cpp \ + ../src/svgrenderer.cpp \ + ../src/systemtab.cpp \ + ../src/tabbase.cpp \ + ../src/timinglogger.cpp \ + ../src/upgradekitunpacker.cpp \ + ../src/upgrademanager.cpp \ + ../src/upgradeselector.cpp \ + ../src/usbmountmanager.cpp \ + ../src/utils.cpp \ ../src/window.cpp -HEADERS += \ - ../src/advancedtab.h \ - ../src/app.h \ - ../src/backdrop.h \ - ../src/buildinfo.h \ - ../src/canvas.h \ - ../src/constants.h \ - ../src/coordinate.h \ - ../src/debug.h \ - ../src/debuglogcopier.h \ - ../src/filecopier.h \ - ../src/filetab.h \ - ../src/gesturelistview.h \ - ../src/glmesh.h \ - ../src/gpgsignaturechecker.h \ - ../src/hasher.h \ - ../src/initialshoweventmixin.h \ - ../src/lightfieldstyle.h \ - ../src/loader.h \ - ../src/mesh.h \ - ../src/pngdisplayer.h \ - ../src/preparetab.h \ - ../src/printjob.h \ - ../src/printmanager.h \ - ../src/printprofile.h \ - ../src/printprofilemanager.h \ - ../src/printtab.h \ - ../src/processrunner.h \ - ../src/shepherd.h \ - ../src/signalhandler.h \ - ../src/statustab.h \ - ../src/stdiologger.h \ - ../src/strings.h \ - ../src/svgrenderer.h \ - ../src/systemtab.h \ - ../src/tabbase.h \ - ../src/timinglogger.h \ - ../src/upgradekitunpacker.h \ - ../src/upgrademanager.h \ - ../src/upgradeselector.h \ - ../src/usbmountmanager.h \ - ../src/utils.h \ - ../src/version.h \ - ../src/vertex.h \ +HEADERS += \ + ../src/advancedtab.h \ + ../src/app.h \ + ../src/backdrop.h \ + ../src/buildinfo.h \ + ../src/canvas.h \ + ../src/constants.h \ + ../src/coordinate.h \ + ../src/debug.h \ + ../src/debuglogcopier.h \ + ../src/filecopier.h \ + ../src/filetab.h \ + ../src/gesturelistview.h \ + ../src/glmesh.h \ + ../src/gpgsignaturechecker.h \ + ../src/hasher.h \ + ../src/initialshoweventmixin.h \ + ../src/lightfieldstyle.h \ + ../src/loader.h \ + ../src/mesh.h \ + ../src/pngdisplayer.h \ + ../src/preparetab.h \ + ../src/printjob.h \ + ../src/printmanager.h \ + ../src/printprofile.h \ + ../src/printprofilemanager.h \ + ../src/printpumpingparameters.h \ + ../src/printtab.h \ + ../src/processrunner.h \ + ../src/shepherd.h \ + ../src/signalhandler.h \ + ../src/statustab.h \ + ../src/stdiologger.h \ + ../src/strings.h \ + ../src/svgrenderer.h \ + ../src/systemtab.h \ + ../src/tabbase.h \ + ../src/timinglogger.h \ + ../src/upgradekitunpacker.h \ + ../src/upgrademanager.h \ + ../src/upgradeselector.h \ + ../src/usbmountmanager.h \ + ../src/utils.h \ + ../src/version.h \ + ../src/vertex.h \ ../src/window.h CONFIG += c++1z precompile_header diff --git a/src/printprofile.h b/src/printprofile.h index 4fb43aa0..4861fdd9 100644 --- a/src/printprofile.h +++ b/src/printprofile.h @@ -1,6 +1,8 @@ #ifndef __PRINTPROFILE_H__ #define __PRINTPROFILE_H__ +#include "printpumpingparameters.h" + class PrintProfile: public QObject { Q_OBJECT; @@ -15,20 +17,55 @@ class PrintProfile: public QObject { /*empty*/ } + // + // Accessors + // + QString const& profileName( ) const { return _name; } + int baseLayerCount( ) const { + return _baseLayerCount; + } + + PrintPumpingParameters const& baseLayersPumpingParameters( ) const { + return _baseLayersPumpingParameters; + } + + PrintPumpingParameters const& bodyLayersPumpingParameters( ) const { + return _bodyLayersPumpingParameters; + } + + // + // Mutators + // + void setProfileName( QString const& newName ) { emit profileNameChanged( newName ); _name = newName; } + void setBaseLayerCount( int const newCount ) { + _baseLayerCount = newCount; + } + + void setBaseLayersPumpingParameters( PrintPumpingParameters const& newParameters ) { + _baseLayersPumpingParameters = newParameters; + } + + void setBodyLayersPumpingParameters( PrintPumpingParameters const& newParameters ) { + _bodyLayersPumpingParameters = newParameters; + } + protected: private: - QString _name; + QString _name; + int _baseLayerCount; + PrintPumpingParameters _baseLayersPumpingParameters; + PrintPumpingParameters _bodyLayersPumpingParameters; signals: ; diff --git a/src/printprofilemanager.cpp b/src/printprofilemanager.cpp index 1c5fe99b..ff281d89 100644 --- a/src/printprofilemanager.cpp +++ b/src/printprofilemanager.cpp @@ -2,6 +2,45 @@ #include "printprofilemanager.h" +PrintProfile* PrintProfileManager::_findProfile( QString const& profileName ) { + auto iter = std::find_if( _profiles->begin( ), _profiles->end( ), [&profileName] ( PrintProfile* p ) { return profileName == p->profileName( ); } ); + return ( iter != _profiles->end( ) ) ? *iter : nullptr; +} + +bool PrintProfileManager::addProfile( PrintProfile* newProfile ) { + auto profile = _findProfile( newProfile->profileName( ) ); + if ( !profile ) { + _profiles->append( newProfile ); + return true; + } else { + debug( "PrintProfileManager::addProfile: can't add profile %p: already have a profile named '%s'\n", newProfile, newProfile->profileName( ).toUtf8( ).data( ) ); + return false; + } +} + +bool PrintProfileManager::removeProfile( QString const& name ) { + auto profile = _findProfile( name ); + if ( profile ) { + _profiles->removeOne( profile ); + return true; + } else { + debug( "PrintProfileManager::removeProfile: can't remove profile named '%s': not in collection\n", name.toUtf8( ).data( ) ); + return false; + } +} + +bool PrintProfileManager::setActiveProfile( QString const& profileName ) { + auto newProfile = _findProfile( profileName ); + if ( newProfile ) { + emit activeProfileChanged( newProfile ); + _activeProfile = newProfile; + return true; + } else { + debug( "PrintProfileManager::setActiveProfile: couldn't find profile named '%s' in collection\n", profileName.toUtf8( ).data( ) ); + return false; + } +} + bool PrintProfileManager::importProfiles( QString const& /*fileName*/ ) { return false; } diff --git a/src/printprofilemanager.h b/src/printprofilemanager.h index 9f0dd809..1e46792f 100644 --- a/src/printprofilemanager.h +++ b/src/printprofilemanager.h @@ -19,28 +19,30 @@ class PrintProfileManager: public QObject { /*empty*/ } - bool importProfiles( QString const& fileName ); - bool exportProfiles( QString const& fileName ); + PrintProfileCollection const* profiles( ) { + return _profiles; + } PrintProfile const* activeProfile( ) { return _activeProfile; } - void setActiveProfile( PrintProfile* newProfile ) { - emit activeProfileChanged( newProfile ); - _activeProfile = newProfile; - } + bool addProfile( PrintProfile* newProfile ); + bool removeProfile( QString const& name ); - PrintProfileCollection const* profiles( ) { - return _profiles; - } + bool setActiveProfile( QString const& profileName ); + + bool importProfiles( QString const& fileName ); + bool exportProfiles( QString const& fileName ); protected: private: - PrintProfile* _activeProfile { }; PrintProfileCollection* _profiles { new PrintProfileCollection }; + PrintProfile* _activeProfile { }; + + PrintProfile* _findProfile( QString const& name ); signals: ; diff --git a/src/printpumpingparameters.h b/src/printpumpingparameters.h new file mode 100644 index 00000000..76da3daa --- /dev/null +++ b/src/printpumpingparameters.h @@ -0,0 +1,131 @@ +#ifndef __PRINTPUMPINGPARAMETERS_H__ +#define __PRINTPUMPINGPARAMETERS_H__ + +class PrintPumpingParameters { + +public: + + PrintPumpingParameters( ) = default; + PrintPumpingParameters( PrintPumpingParameters const& ) = default; + PrintPumpingParameters( PrintPumpingParameters&& ) = default; + PrintPumpingParameters& operator=( PrintPumpingParameters const& ) = default; + PrintPumpingParameters& operator=( PrintPumpingParameters&& ) = default; + + // + // Accessors + // + + // unit: mm, multiples of 0.01 + double pumpUpDistance( ) const { + return _pumpUpDistance; + } + + // unit: ms + int pumpUpTime( ) const { + return _pumpUpTime; + } + + // unit: mm/min + double pumpUpVelocity_Effective( ) const { + return _pumpUpDistance / ( _pumpUpTime / 1000.0 / 60.0 ); + } + + // unit: ms + int pumpUpPause( ) const { + return _pumpUpPause; + } + + // unit: ms + int pumpDownTime( ) const { + return _pumpDownTime; + } + + // unit: ms + int pumpDownPause( ) const { + return _pumpDownPause; + } + + // unit: mm/min + double noPumpUpVelocity( ) const { + return _noPumpUpVelocity; + } + + // unit: µm + int layerThickness( ) const { + return _layerThickness; + } + + // unit: ms + int layerExposureTime( ) const { + return _layerExposureTime; + } + + // unit: none + int pumpEveryNthLayer( ) const { + return _pumpEveryNthLayer; + } + + // + // Mutators + // + + // unit: mm, multiples of 0.01 + void setPumpUpDistance( double const value ) { + _pumpUpDistance = value; + } + + // unit: ms + void setPumpUpTime( int const value ) { + _pumpUpTime = value; + } + + // unit: ms + void setPumpUpPause( int const value ) { + _pumpUpPause = value; + } + + // unit: ms + void setPumpDownTime( int const value ) { + _pumpDownTime = value; + } + + // unit: ms + void setPumpDownPause( int const value ) { + _pumpDownPause = value; + } + + // unit: mm/min + void setNoPumpUpVelocity( double const value ) { + _noPumpUpVelocity = value; + } + + // unit: µm + void setLayerThickness( int const value ) { + _layerThickness = value; + } + + // unit: ms + void setLayerExposureTime( int const value ) { + _layerExposureTime = value; + } + + // unit: none + void setPumpEveryNthLayer( int const value ) { + _pumpEveryNthLayer = value; + } + +private: + + double _pumpUpDistance { }; + int _pumpUpTime { }; + int _pumpUpPause { }; + int _pumpDownTime { }; + int _pumpDownPause { }; + int _noPumpUpVelocity { }; + int _layerThickness { }; + int _layerExposureTime { }; + int _pumpEveryNthLayer { }; + +}; + +#endif //!__PRINTPUMPINGPARAMETERS_H__ From 0a13eccc137e89282cc8e3a7fb94e853d5c50a20 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sun, 26 Jan 2020 12:29:31 -0800 Subject: [PATCH 54/89] rebuild: Try to generate src/version.h if it's missing. Also fix rebuilding qrc_images.{cpp,o} every time. --- rebuild | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/rebuild b/rebuild index 9b720397..045df877 100755 --- a/rebuild +++ b/rebuild @@ -2,6 +2,12 @@ LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField +######################################################### +## ## +## No user-serviceable parts below this point. ## +## ## +######################################################### + # shellcheck disable=SC1090 source "${LIGHTFIELD_ROOT}/shared-stuff.sh" @@ -66,14 +72,14 @@ function rebuild () { then if [ ! -f "${BUILDDIR}/.lastbuildmode" ] then - echo "${BUILD}-${RELEASE_TRAIN}" > "${BUILDDIR}/.lastbuildmode" + echo "${BUILD}:${RELEASE_TRAIN}" > "${BUILDDIR}/.lastbuildmode" NUKE=yes fi LASTBUILDMODE="$(<${BUILDDIR}/.lastbuildmode)" - if [ -n "${LASTBUILDMODE}" ] && [ "${LASTBUILDMODE}" != "${BUILD}-${RELEASE_TRAIN}" ] + if [ -n "${LASTBUILDMODE}" ] && [ "${LASTBUILDMODE}" != "${BUILD}:${RELEASE_TRAIN}" ] then - echo Switching from "${LASTBUILDMODE}" to "${BUILD}-${RELEASE_TRAIN}". + echo Switching from "${LASTBUILDMODE}" to "${BUILD}:${RELEASE_TRAIN}". NUKE=yes fi @@ -87,6 +93,23 @@ function rebuild () { mkdir -p "${BUILDDIR}" fi + if [ ! -f "${LIGHTFIELD_ROOT}/version.h" ] + then + if [ -n "${RELEASE_TRAIN}" ] && [ -n "${VERSION}" ] + then + if ! "${LIGHTFIELD_ROOT}/change-version-number.sh" -t "${RELEASE_TRAIN}" "${VERSION}" + then + red-bar "Unable to generate missing src/version.h." + red-bar "Run change-version-number.sh manually." + exit 1 + fi + fi + else + red-bar "src/version.h does not exist, and the version number is unconfigured." + red-bar "Run change-version-number.sh manually." + exit 1 + fi + # shellcheck disable=SC2164 cd "${BUILDDIR}" @@ -99,27 +122,27 @@ function rebuild () { if [ -n "${QMAKE}" ] || [ ! -f Makefile ] || [ ../qt/lf.pro -nt Makefile ] then qmake CONFIG+="${BUILD}" ${ADDTOCONFIG} ../qt/lf.pro || return - echo "${BUILD}-${RELEASE_TRAIN}" > "${BUILDDIR}/.lastbuildmode" + echo "${BUILD}:${RELEASE_TRAIN}" > "${BUILDDIR}/.lastbuildmode" fi if [ "${RELEASE_TRAIN}" = "base" ] then - cp ../images/dlpc350-focus-image.png ../images/focus-image.png - cp ../images/dlpc350-white-field.png ../images/white-field.png + cp -a ../images/dlpc350-focus-image.png ../images/focus-image.png + cp -a ../images/dlpc350-white-field.png ../images/white-field.png elif [ "${RELEASE_TRAIN}" = "dlp4710" ] then - cp ../images/dlp4710-focus-image.png ../images/focus-image.png - cp ../images/dlp4710-white-field.png ../images/white-field.png + cp -a ../images/dlp4710-focus-image.png ../images/focus-image.png + cp -a ../images/dlp4710-white-field.png ../images/white-field.png fi - if make -q 1> /dev/null 2>&1 + if make -q 1>/dev/null 2>&1 then echo "make: Nothing to be done for 'first'." return fi # shellcheck disable=SC2015 - [ -f buildinfo.o ] && rm buildinfo.o 1> /dev/null 2>&1 || true + [ -f buildinfo.o ] && rm buildinfo.o 1>/dev/null 2>&1 || true make -j"${QT_BUILD_JOBS-12}" } From 20f4378303b491a59c10c0d1d594b9550947941e Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 27 Jan 2020 17:48:19 -0800 Subject: [PATCH 55/89] Add new class ProfilesTab. --- CMakeLists.txt | 2 ++ qt/lf.pro | 2 ++ src/profilestab.cpp | 26 ++++++++++++++++++++++++++ src/profilestab.h | 39 +++++++++++++++++++++++++++++++++++++++ src/strings.cpp | 1 + src/tabbase.h | 1 + src/window.cpp | 9 +++++++++ src/window.h | 2 ++ 8 files changed, 82 insertions(+) create mode 100644 src/profilestab.cpp create mode 100644 src/profilestab.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b16a0f28..a5457b9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ src/printprofile.cpp src/printprofilemanager.cpp src/printtab.cpp src/processrunner.cpp +src/profilestab.cpp src/shepherd.cpp src/signalhandler.cpp src/statustab.cpp @@ -100,6 +101,7 @@ src/printprofilemanager.h src/printpumpingparameters.h src/printtab.h src/processrunner.h +src/profilestab.h src/shepherd.h src/signalhandler.h src/statustab.h diff --git a/qt/lf.pro b/qt/lf.pro index f289d5aa..6b08f955 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -33,6 +33,7 @@ SOURCES += \ ../src/printprofilemanager.cpp \ ../src/printtab.cpp \ ../src/processrunner.cpp \ + ../src/profilestab.cpp \ ../src/shepherd.cpp \ ../src/signalhandler.cpp \ ../src/statustab.cpp \ @@ -78,6 +79,7 @@ HEADERS += \ ../src/printpumpingparameters.h \ ../src/printtab.h \ ../src/processrunner.h \ + ../src/profilestab.h \ ../src/shepherd.h \ ../src/signalhandler.h \ ../src/statustab.h \ diff --git a/src/profilestab.cpp b/src/profilestab.cpp new file mode 100644 index 00000000..c7533393 --- /dev/null +++ b/src/profilestab.cpp @@ -0,0 +1,26 @@ +#include "pch.h" + +#include "profilestab.h" + +ProfilesTab::ProfilesTab( QWidget* parent ): TabBase( parent ) { + +} + +ProfilesTab::~ProfilesTab( ) { + /*empty*/ +} + +void ProfilesTab::tab_uiStateChanged( TabIndex const sender, UiState const state ) { + debug( "+ ProfilesTab::tab_uiStateChanged: from %sTab: %s => %s\n", ToString( sender ), ToString( _uiState ), ToString( state ) ); + _uiState = state; + + switch ( _uiState ) { + case UiState::SelectStarted: + case UiState::SelectCompleted: + case UiState::SliceStarted: + case UiState::SliceCompleted: + case UiState::PrintStarted: + case UiState::PrintCompleted: + break; + } +} diff --git a/src/profilestab.h b/src/profilestab.h new file mode 100644 index 00000000..f7ba7261 --- /dev/null +++ b/src/profilestab.h @@ -0,0 +1,39 @@ +#ifndef __PROFILESTAB_H__ +#define __PROFILESTAB_H__ + +#include "tabbase.h" + +#include "printprofilemanager.h" + +class ProfilesTab: public TabBase { + + Q_OBJECT + +public: + + ProfilesTab( QWidget* parent = nullptr ); + virtual ~ProfilesTab( ) override; + + virtual TabIndex tabIndex( ) const override { return TabIndex::Profiles; } + +protected: + +private: + +signals: + ; + +public slots: + ; + + virtual void tab_uiStateChanged( TabIndex const sender, UiState const state ) override; + +protected slots: + ; + +private slots: + ; + +}; + +#endif //!__PROFILESTAB_H__ diff --git a/src/strings.cpp b/src/strings.cpp index fb49db34..31761190 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -47,6 +47,7 @@ namespace { "Print", "Status", "Advanced", + "Profiles", "System", }; diff --git a/src/tabbase.h b/src/tabbase.h index 1ba8ce68..655a37af 100644 --- a/src/tabbase.h +++ b/src/tabbase.h @@ -26,6 +26,7 @@ class TabBase: public QWidget { Print, Status, Advanced, + Profiles, System, }; Q_ENUM( TabIndex ); diff --git a/src/window.cpp b/src/window.cpp index af100c2b..f995fc42 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -15,6 +15,7 @@ #include "printtab.h" #include "statustab.h" #include "advancedtab.h" +#include "profilestab.h" #include "systemtab.h" namespace { @@ -70,6 +71,7 @@ Window::Window( QWidget* parent ): QMainWindow( parent ) { _printTab = new PrintTab, _statusTab = new StatusTab, _advancedTab = new AdvancedTab, + _profilesTab = new ProfilesTab, _systemTab = new SystemTab, }; @@ -155,6 +157,13 @@ Window::Window( QWidget* parent ): QMainWindow( parent ) { QObject::connect( _advancedTab, &AdvancedTab::printerAvailabilityChanged, _statusTab, &StatusTab::setPrinterAvailable ); QObject::connect( _advancedTab, &AdvancedTab::printerAvailabilityChanged, _systemTab, &SystemTab::setPrinterAvailable ); + // + // "Profiles" tab + // + + _profilesTab->setContentsMargins( { } ); + _profilesTab->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + // // "System" tab // diff --git a/src/window.h b/src/window.h index 22616348..525c3216 100644 --- a/src/window.h +++ b/src/window.h @@ -18,6 +18,7 @@ class PrepareTab; class PrintTab; class StatusTab; class AdvancedTab; +class ProfilesTab; class SystemTab; class Window: public QMainWindow { @@ -53,6 +54,7 @@ class Window: public QMainWindow { PrintTab* _printTab; StatusTab* _statusTab; AdvancedTab* _advancedTab; + ProfilesTab* _profilesTab; SystemTab* _systemTab; QPushButton* _helpButton { new QPushButton }; From e1eb3a66e729a177507e2404ce6854fb4c51ff16 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 27 Jan 2020 17:48:53 -0800 Subject: [PATCH 56/89] rebuild: Fix excessive regeneration of src/version.h. --- rebuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebuild b/rebuild index 045df877..6a1da631 100755 --- a/rebuild +++ b/rebuild @@ -93,7 +93,7 @@ function rebuild () { mkdir -p "${BUILDDIR}" fi - if [ ! -f "${LIGHTFIELD_ROOT}/version.h" ] + if [ ! -f "${LIGHTFIELD_ROOT}/src/version.h" ] then if [ -n "${RELEASE_TRAIN}" ] && [ -n "${VERSION}" ] then From 884ffdbf400a0135d3a832b9c2c801944b5eb860 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sun, 26 Jan 2020 15:37:46 -0800 Subject: [PATCH 57/89] Packaging: Various fixes. (cherry picked from commit 76020adbf93c491e1f1ff312ca7ae87151e33fc5) --- debian/control.in | 4 ++-- debian/lightfield-common-base.install | 25 ++++++++++++------------ debian/lightfield-common-dlp4710.install | 25 ++++++++++++------------ make-deb-package.sh | 7 ++++++- make-upgrade-kit.sh | 2 +- 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/debian/control.in b/debian/control.in index 532dcb0e..33158484 100644 --- a/debian/control.in +++ b/debian/control.in @@ -19,7 +19,7 @@ Breaks: lightfield-@@ANTIBUILDTYPE@@ Conflicts: lightfield-@@ANTIBUILDTYPE@@ Replaces: lightfield-@@ANTIBUILDTYPE@@ Depends: - lightfield@@RELEASE_TRAIN@@-common (= ${binary:Version}), + lightfield-common@@RELEASE_TRAIN@@ (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, libhidapi-libusb0 (>> 0.8), @@ -35,7 +35,7 @@ Recommends: fonts-font-awesome (>= 5.0.10) Description: Printer software for Volumetric's Lumen X 3D printer - debug version -Package: lightfield@@RELEASE_TRAIN@@-common +Package: lightfield-common@@RELEASE_TRAIN@@ Architecture: all Multi-Arch: foreign Provides: lightfield-common diff --git a/debian/lightfield-common-base.install b/debian/lightfield-common-base.install index 2b417a51..bf6cc8e9 100644 --- a/debian/lightfield-common-base.install +++ b/debian/lightfield-common-base.install @@ -1,13 +1,12 @@ -files/etc/apt/trusted.gpg.d/volumetric-keyring.gpg etc/apt/trusted.gpg.d -files/etc/sudoers.d/lumen-lightfield etc/sudoers.d -files/home/lumen/.bash_profile home/lumen -files/home/lumen/.real_bash_profile home/lumen -files/home/lumen/.gnupg/pubring.gpg home/lumen/.gnupg -files/home/lumen/.gnupg/pubring.kbx home/lumen/.gnupg -files/home/lumen/.gnupg/trustdb.gpg home/lumen/.gnupg -files/lib/udev/rules.d/90-dlp4710.rules lib/udev/rules.d -files/usr/share/lightfield/libexec/reset-lumen-arduino-port usr/share/lightfield/libexec -files/usr/share/lightfield/libexec/reset-lumen-projector-port usr/share/lightfield/libexec -files/usr/share/lightfield/libexec/printrun/* usr/share/lightfield/libexec/printrun -files/usr/share/lightfield/libexec/stdio-shepherd/* usr/share/lightfield/libexec/stdio-shepherd -files/usr/share/X11/xorg.conf.d/99-waveshare.conf usr/share/X11/xorg.conf.d +files/etc/apt/trusted.gpg.d/volumetric-keyring.gpg etc/apt/trusted.gpg.d +files/etc/sudoers.d/lumen-lightfield etc/sudoers.d +files/home/lumen/.bash_profile home/lumen +files/home/lumen/.real_bash_profile home/lumen +files/home/lumen/.gnupg/pubring.gpg home/lumen/.gnupg +files/home/lumen/.gnupg/pubring.kbx home/lumen/.gnupg +files/home/lumen/.gnupg/trustdb.gpg home/lumen/.gnupg +files/lib/udev/rules.d/90-dlp4710.rules lib/udev/rules.d +files/usr/share/lightfield/libexec/reset-lumen-arduino-port usr/share/lightfield/libexec +files/usr/share/lightfield/libexec/printrun/* usr/share/lightfield/libexec/printrun +files/usr/share/lightfield/libexec/stdio-shepherd/* usr/share/lightfield/libexec/stdio-shepherd +files/usr/share/X11/xorg.conf.d/99-waveshare.conf usr/share/X11/xorg.conf.d diff --git a/debian/lightfield-common-dlp4710.install b/debian/lightfield-common-dlp4710.install index bf6cc8e9..2b417a51 100644 --- a/debian/lightfield-common-dlp4710.install +++ b/debian/lightfield-common-dlp4710.install @@ -1,12 +1,13 @@ -files/etc/apt/trusted.gpg.d/volumetric-keyring.gpg etc/apt/trusted.gpg.d -files/etc/sudoers.d/lumen-lightfield etc/sudoers.d -files/home/lumen/.bash_profile home/lumen -files/home/lumen/.real_bash_profile home/lumen -files/home/lumen/.gnupg/pubring.gpg home/lumen/.gnupg -files/home/lumen/.gnupg/pubring.kbx home/lumen/.gnupg -files/home/lumen/.gnupg/trustdb.gpg home/lumen/.gnupg -files/lib/udev/rules.d/90-dlp4710.rules lib/udev/rules.d -files/usr/share/lightfield/libexec/reset-lumen-arduino-port usr/share/lightfield/libexec -files/usr/share/lightfield/libexec/printrun/* usr/share/lightfield/libexec/printrun -files/usr/share/lightfield/libexec/stdio-shepherd/* usr/share/lightfield/libexec/stdio-shepherd -files/usr/share/X11/xorg.conf.d/99-waveshare.conf usr/share/X11/xorg.conf.d +files/etc/apt/trusted.gpg.d/volumetric-keyring.gpg etc/apt/trusted.gpg.d +files/etc/sudoers.d/lumen-lightfield etc/sudoers.d +files/home/lumen/.bash_profile home/lumen +files/home/lumen/.real_bash_profile home/lumen +files/home/lumen/.gnupg/pubring.gpg home/lumen/.gnupg +files/home/lumen/.gnupg/pubring.kbx home/lumen/.gnupg +files/home/lumen/.gnupg/trustdb.gpg home/lumen/.gnupg +files/lib/udev/rules.d/90-dlp4710.rules lib/udev/rules.d +files/usr/share/lightfield/libexec/reset-lumen-arduino-port usr/share/lightfield/libexec +files/usr/share/lightfield/libexec/reset-lumen-projector-port usr/share/lightfield/libexec +files/usr/share/lightfield/libexec/printrun/* usr/share/lightfield/libexec/printrun +files/usr/share/lightfield/libexec/stdio-shepherd/* usr/share/lightfield/libexec/stdio-shepherd +files/usr/share/X11/xorg.conf.d/99-waveshare.conf usr/share/X11/xorg.conf.d diff --git a/make-deb-package.sh b/make-deb-package.sh index d90d69ea..74ee902d 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -259,11 +259,16 @@ cd "${LIGHTFIELD_PACKAGE}" mv debian/control.in debian/control mv debian/lightfield.install debian/lightfield-"${SUFFIX}".install -mv debian/lightfield-common-"${RELEASE_TRAIN}".install debian/lightfield-common-"${SUFFIX}".install mv debian/lightfield.preinst debian/lightfield-"${SUFFIX}".preinst mv debian/lightfield.prerm debian/lightfield-"${SUFFIX}".prerm mv debian/lightfield.postinst.in debian/lightfield-"${SUFFIX}".postinst mv debian/lightfield.postrm debian/lightfield-"${SUFFIX}".postrm +if [ "${RELEASE_TRAIN}" != "base" ] +then + mv debian/lightfield-common.preinst debian/lightfield-common-"${RELEASE_TRAIN}".preinst + mv debian/lightfield-common.postinst debian/lightfield-common-"${RELEASE_TRAIN}".postinst + mv debian/lightfield-common.postrm debian/lightfield-common-"${RELEASE_TRAIN}".postrm +fi apply-atsign-substitution BUILDTYPE "${BUILDTYPE}" debian/control apply-atsign-substitution ANTIBUILDTYPE "${ANTIBUILDTYPE}" debian/control diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index 9336330e..de44d11c 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -122,7 +122,7 @@ if [ "${RELEASE_TRAIN}" = "base" ] then install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-common_${VERSION}_all.deb" else - install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-${RELEASE_TRAIN}-common_${VERSION}_all.deb" + install ${VERBOSE} -Dt "${REPO_DIR}/" -m 644 "${DEB_BUILD_DIR}/lightfield-common-${RELEASE_TRAIN}_${VERSION}_all.deb" fi if [ "${BUILDTYPE}" = "release" ] From ee1135663bce961976facd2da37bb07e0d449fa1 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Mon, 27 Jan 2020 17:58:27 -0800 Subject: [PATCH 58/89] Packaging: Another small fix. (cherry picked from commit 7962856316e3e0cb0d353b74bdeb1fc614d182d8) --- debian/lightfield-common-base.install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/lightfield-common-base.install b/debian/lightfield-common-base.install index bf6cc8e9..f45cf155 100644 --- a/debian/lightfield-common-base.install +++ b/debian/lightfield-common-base.install @@ -5,7 +5,7 @@ files/home/lumen/.real_bash_profile home/lumen files/home/lumen/.gnupg/pubring.gpg home/lumen/.gnupg files/home/lumen/.gnupg/pubring.kbx home/lumen/.gnupg files/home/lumen/.gnupg/trustdb.gpg home/lumen/.gnupg -files/lib/udev/rules.d/90-dlp4710.rules lib/udev/rules.d +files/lib/udev/rules.d/90-dlpc350.rules lib/udev/rules.d files/usr/share/lightfield/libexec/reset-lumen-arduino-port usr/share/lightfield/libexec files/usr/share/lightfield/libexec/printrun/* usr/share/lightfield/libexec/printrun files/usr/share/lightfield/libexec/stdio-shepherd/* usr/share/lightfield/libexec/stdio-shepherd From 494ad338bb27b238d8f4deab07b0a3112336d6af Mon Sep 17 00:00:00 2001 From: sagittarius89 Date: Tue, 28 Jan 2020 20:33:07 +0100 Subject: [PATCH 59/89] - added left menu (advanced tab) - added new forms with print parameters - added virtual keyboard support (partially) - working on import/export print parameters --- qt/lf.pro | 15 +- src/advancedtab.cpp | 583 ++++++++++++++++++++++-------- src/advancedtab.h | 116 ++++-- src/advancedtabselectionmodel.cpp | 28 ++ src/advancedtabselectionmodel.h | 21 ++ src/app.cpp | 2 +- src/constants.cpp | 1 + src/constants.h | 1 + src/main.cpp | 2 + src/paramslider.cpp | 51 +++ src/paramslider.h | 27 ++ src/window.cpp | 7 +- src/window.h | 3 +- 13 files changed, 674 insertions(+), 183 deletions(-) create mode 100644 src/advancedtabselectionmodel.cpp create mode 100644 src/advancedtabselectionmodel.h create mode 100644 src/paramslider.cpp create mode 100644 src/paramslider.h diff --git a/qt/lf.pro b/qt/lf.pro index 6b08f955..1f18bc1f 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -6,9 +6,11 @@ TEMPLATE = app QMAKE_CXXFLAGS_RELEASE -= -O2 QMAKE_CXXFLAGS_RELEASE += -O3 -DNDEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result QMAKE_CXXFLAGS_DEBUG += -Og -D_DEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result +QT += virtualkeyboard SOURCES += \ ../src/advancedtab.cpp \ + ../src/advancedtabselectionmodel.cpp \ ../src/app.cpp \ ../src/backdrop.cpp \ ../src/buildinfo.cpp \ @@ -26,6 +28,7 @@ SOURCES += \ ../src/loader.cpp \ ../src/main.cpp \ ../src/mesh.cpp \ + ../src/paramslider.cpp \ ../src/pngdisplayer.cpp \ ../src/preparetab.cpp \ ../src/printmanager.cpp \ @@ -52,6 +55,7 @@ SOURCES += \ HEADERS += \ ../src/advancedtab.h \ + ../src/advancedtabselectionmodel.h \ ../src/app.h \ ../src/backdrop.h \ ../src/buildinfo.h \ @@ -70,6 +74,7 @@ HEADERS += \ ../src/lightfieldstyle.h \ ../src/loader.h \ ../src/mesh.h \ + ../src/paramslider.h \ ../src/pngdisplayer.h \ ../src/preparetab.h \ ../src/printjob.h \ @@ -121,4 +126,12 @@ debug { dlp4710 { DEFINES += DLP4710 -} +}QT += core gui widgets xml + +TARGET = lf +TEMPLATE = app + +QMAKE_CXXFLAGS_RELEASE -= -O2 +QMAKE_CXXFLAGS_RELEASE += -O3 -DNDEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result +QMAKE_CXXFLAGS_DEBUG += -Og -D_DEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result +QT += virtualkeyboard diff --git a/src/advancedtab.cpp b/src/advancedtab.cpp index 0cccec16..2c7ecb4d 100644 --- a/src/advancedtab.cpp +++ b/src/advancedtab.cpp @@ -6,6 +6,10 @@ #include "printjob.h" #include "printmanager.h" #include "shepherd.h" +#include "advancedtabselectionmodel.h" +#include "paramslider.h" +#include +#include namespace { @@ -18,186 +22,87 @@ AdvancedTab::AdvancedTab( QWidget* parent ): TabBase( parent ) { auto boldFont = ModifyFont( origFont, QFont::Bold ); auto fontAwesome = ModifyFont( origFont, "FontAwesome", LargeFontSize ); + // import/export operations - _currentTemperatureLabel->setText( "Current temperature:" ); - _targetTemperatureLabel ->setText( "Target temperature:" ); - _heatingElementLabel ->setText( "Heating element:" ); - _zPositionLabel ->setText( "Z position:" ); + _importParams->setFixedSize( MainButtonSize ); + _importParams->setFont( ModifyFont( _importParams->font( ), LargeFontSize ) ); + _importParams->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); - _currentTemperature->setAlignment( Qt::AlignRight ); - _currentTemperature->setFont( boldFont ); - _currentTemperature->setText( EmDash ); + _exportParams->setFixedSize( MainButtonSize ); + _exportParams->setFont( ModifyFont( _exportParams->font( ), LargeFontSize ) ); + _exportParams->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); - _targetTemperature ->setAlignment( Qt::AlignRight ); - _targetTemperature ->setFont( boldFont ); - _targetTemperature ->setText( EmDash ); + _setupExportSettingsForm(); - _heatingElement ->setAlignment( Qt::AlignRight ); - _heatingElement ->setFont( boldFont ); - _heatingElement ->setText( EmDash ); + _forms[0] = _generalForm; + _forms[1] = _temperatureForm; + _forms[2] = _basePumpForm; + _forms[3] = _baseLayerForm; + _forms[4] = _bodyLayersForm; + _forms[5] = _bodyPumpForm; + _forms[6] = _exportSettingsForm; - _zPosition ->setAlignment( Qt::AlignRight ); - _zPosition ->setFont( boldFont ); - _zPosition ->setText( EmDash ); + //menu + this->_setupLeftMenu(fontAwesome); + // General form + this->_setupGeneralForm(boldFont, fontAwesome); - _leftColumn->setContentsMargins( { } ); - _leftColumn->setFixedWidth( MainButtonSize.width( ) ); - _leftColumn->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ); - _leftColumn->setLayout( WrapWidgetsInVBox( - WrapWidgetsInHBox( _currentTemperatureLabel, nullptr, _currentTemperature ), - WrapWidgetsInHBox( _targetTemperatureLabel, nullptr, _targetTemperature ), - WrapWidgetsInHBox( _heatingElementLabel, nullptr, _heatingElement ), - WrapWidgetsInHBox( _zPositionLabel, nullptr, _zPosition ), - nullptr - ) ); + // Temperature form + this->_setupTemperaturelForm(boldFont); + //Base Pump Form + this->_setupBasePumpForm(boldFont); - _offsetLabel->setText( "Build platform offset:" ); + //Base Layer Form + this->_setupBaseLayerForm(); - _offsetValue->setAlignment( Qt::AlignRight ); - _offsetValue->setFont( boldFont ); - _offsetValue->setText( QString { "%1 µm" }.arg( g_settings.buildPlatformOffset ) ); + //Body Layers Form + this->_setupBodyLayersForm(); - _offsetSlider->setMinimum( 0 ); - _offsetSlider->setMaximum( 40 ); - _offsetSlider->setOrientation( Qt::Horizontal ); - _offsetSlider->setPageStep( 4 ); - _offsetSlider->setSingleStep( 1 ); - _offsetSlider->setTickInterval( 4 ); - _offsetSlider->setTickPosition( QSlider::TicksBothSides ); - _offsetSlider->setValue( g_settings.buildPlatformOffset / 25 ); - QObject::connect( _offsetSlider, &QSlider::sliderReleased, this, &AdvancedTab::offsetSlider_sliderReleased ); - QObject::connect( _offsetSlider, &QSlider::valueChanged, this, &AdvancedTab::offsetSlider_valueChanged ); + //Body Pump Form + this->_setupBodyPumpForm(boldFont); + for(int i=1; isetVisible(false); - _buildPlatformOffsetGroup->setContentsMargins( { } ); - _buildPlatformOffsetGroup->setLayout( WrapWidgetsInVBoxDM( - WrapWidgetsInHBox( _offsetLabel, nullptr, _offsetValue ), - _offsetSlider - ) ); + _rightColumn->setLayout(WrapWidgetsInVBox(_generalForm, _temperatureForm, _basePumpForm, _baseLayerForm, + _bodyLayersForm, _bodyPumpForm, _exportSettingsForm, nullptr)); - _bedHeatingButton->setCheckable( true ); - _bedHeatingButton->setChecked( false ); - _bedHeatingButton->setFont( fontAwesome ); - _bedHeatingButton->setFixedSize( 37, 38 ); - _bedHeatingButton->setText( FA_Times ); - QObject::connect( _bedHeatingButton, &QPushButton::clicked, this, &AdvancedTab::printBedHeatingButton_clicked ); - - _bedHeatingButtonLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); - _bedHeatingButtonLabel->setText( "Print bed heating" ); - -#if defined ENABLE_TEMPERATURE_SETTING - _bedTemperatureLabel->setEnabled( false ); - _bedTemperatureLabel->setText( "Print bed temperature:" ); - - _bedTemperatureValue->setAlignment( Qt::AlignRight ); - _bedTemperatureValue->setEnabled( false ); - _bedTemperatureValue->setFont( boldFont ); - _bedTemperatureValue->setText( QString { "%1 °C" }.arg( DefaultPrintBedTemperature ) ); - - _bedTemperatureValueLayout = WrapWidgetsInHBox( _bedTemperatureLabel, nullptr, _bedTemperatureValue ); - _bedTemperatureValueLayout->setEnabled( false ); - - _bedTemperatureSlider->setEnabled( false ); - _bedTemperatureSlider->setMinimum( 30 ); - _bedTemperatureSlider->setMaximum( 50 ); - _bedTemperatureSlider->setOrientation( Qt::Horizontal ); - _bedTemperatureSlider->setPageStep( 1 ); - _bedTemperatureSlider->setSingleStep( 1 ); - _bedTemperatureSlider->setTickInterval( 5 ); - _bedTemperatureSlider->setTickPosition( QSlider::TicksBothSides ); - _bedTemperatureSlider->setValue( DefaultPrintBedTemperature ); - QObject::connect( _bedTemperatureSlider, &QSlider::sliderReleased, this, &AdvancedTab::printBedTemperatureSlider_sliderReleased ); - QObject::connect( _bedTemperatureSlider, &QSlider::valueChanged, this, &AdvancedTab::printBedTemperatureSlider_valueChanged ); -#endif - - auto bedTemperatureLayout = WrapWidgetsInVBoxDM( - WrapWidgetsInHBox( _bedHeatingButton, _bedHeatingButtonLabel, nullptr ) + setLayout( WrapWidgetsInHBox( + WrapWidgetsInVBox(_leftMenu, _importParams, _exportParams), + _rightColumn, nullptr + ) ); -#if defined ENABLE_TEMPERATURE_SETTING - bedTemperatureLayout->addLayout( _bedTemperatureValueLayout ); - bedTemperatureLayout->addWidget( _bedTemperatureSlider ); -#endif - - _bedHeatingGroup->setContentsMargins( { } ); - _bedHeatingGroup->setLayout( bedTemperatureLayout ); - - - _projectBlankImageButton->setCheckable( true ); - _projectBlankImageButton->setChecked( false ); - _projectBlankImageButton->setFont( fontAwesome ); - _projectBlankImageButton->setFixedSize( 37, 38 ); - _projectBlankImageButton->setText( FA_Times ); - QObject::connect( _projectBlankImageButton, &QPushButton::clicked, this, &AdvancedTab::projectBlankImageButton_clicked ); - - _projectBlankImageButtonLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); - _projectBlankImageButtonLabel->setText( "Project blank image" ); - - _projectFocusImageButton->setCheckable( true ); - _projectFocusImageButton->setChecked( false ); - _projectFocusImageButton->setFont( fontAwesome ); - _projectFocusImageButton->setFixedSize( 37, 38 ); - _projectFocusImageButton->setText( FA_Times ); - QObject::connect( _projectFocusImageButton, &QPushButton::clicked, this, &AdvancedTab::projectFocusImageButton_clicked ); - - _projectFocusImageButtonLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); - _projectFocusImageButtonLabel->setText( "Project focus image" ); - - _powerLevelLabel->setText( "Projector power level:" ); - - _powerLevelValue->setAlignment( Qt::AlignRight ); - _powerLevelValue->setFont( boldFont ); - _powerLevelValue->setText( "50%" ); - - _powerLevelValueLayout = WrapWidgetsInHBox( _powerLevelLabel, nullptr, _powerLevelValue ); - - _powerLevelSlider->setEnabled( false ); - _powerLevelSlider->setMinimum( ProjectorMinPercent ); - _powerLevelSlider->setMaximum( ProjectorMaxPercent ); - _powerLevelSlider->setOrientation( Qt::Horizontal ); - _powerLevelSlider->setPageStep( 5 ); - _powerLevelSlider->setSingleStep( 1 ); - _powerLevelSlider->setTickInterval( 5 ); - _powerLevelSlider->setTickPosition( QSlider::TicksBothSides ); - _powerLevelSlider->setValue( 50 ); - QObject::connect( _powerLevelSlider, &QSlider::sliderReleased, this, &AdvancedTab::powerLevelSlider_sliderReleased ); - QObject::connect( _powerLevelSlider, &QSlider::valueChanged, this, &AdvancedTab::powerLevelSlider_valueChanged ); - - _powerLevelLabel->setEnabled( false ); - _powerLevelSlider->setEnabled( false ); - _powerLevelValue->setEnabled( false ); - _powerLevelValueLayout->setEnabled( false ); - - - _projectImageButtonsGroup->setContentsMargins( { } ); - _projectImageButtonsGroup->setLayout( WrapWidgetsInVBoxDM( - WrapWidgetsInHBox( _projectBlankImageButton, _projectBlankImageButtonLabel, nullptr, _projectFocusImageButton, _projectFocusImageButtonLabel, nullptr ), - _powerLevelValueLayout, - _powerLevelSlider - ) ); - - - _rightColumn->setContentsMargins( { } ); - _rightColumn->setMinimumSize( MaximalRightHandPaneSize ); - _rightColumn->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - _rightColumn->setLayout( WrapWidgetsInVBoxDM( - _buildPlatformOffsetGroup, - _bedHeatingGroup, - _projectImageButtonsGroup, - nullptr - ) ); - - - setLayout( WrapWidgetsInHBox( _leftColumn, _rightColumn ) ); } AdvancedTab::~AdvancedTab( ) { /*empty*/ } +void AdvancedTab::chbox_addBodyPumpChanged(int state) +{ + _bodyPumpEveryNthLayer->setEnabled(state); + _bodyDistanceSlider->setEnabled(state); + _bodyUpTimeSlider->setEnabled(state); + _bodyUpPauseSlider->setEnabled(state); + _bodyDownTimeSlider->setEnabled(state); + _bodyDownPauseSlider->setEnabled(state); + _bodyUpVelocitySlider->setEnabled(state); +} + +void AdvancedTab::chbox_addBasePumpCheckChanged(int state) +{ + _distanceSlider->setEnabled(state); + _upTimeSlider->setEnabled(state); + _upPauseSlider->setEnabled(state); + _downTimeSlider->setEnabled(state); + _downPauseSlider->setEnabled(state); + _upVelocitySlider->setEnabled(state); +} + void AdvancedTab::_connectShepherd( ) { if ( _shepherd ) { QObject::connect( _shepherd, &Shepherd::printer_online, this, &AdvancedTab::printer_online ); @@ -390,3 +295,367 @@ void AdvancedTab::setPrinterAvailable( bool const value ) { _updateControlGroups( ); } + +void AdvancedTab::_setupLeftMenu(QFont fontAwesome) { + AdvancedTabSelectionModel* model = new AdvancedTabSelectionModel(5, 1, _forms, FORMS_COUNT); + + QStandardItem* item = new QStandardItem(QString("General")); + QStandardItem* generalItem = item; + model->setItem(0, 0, item); + + item = new QStandardItem(QString("Temperature")); + model->setItem(1, 0, item); + + item = new QStandardItem(QString("Base Pump")); + model->setItem(2, 0, item); + + item = new QStandardItem(QString("Base Layer")); + model->setItem(3, 0, item); + + item = new QStandardItem(QString("Body Layers")); + model->setItem(4, 0, item); + + item = new QStandardItem(QString("Body Pump")); + model->setItem(5, 0, item); + + QItemSelectionModel* selectionModel = new QItemSelectionModel(model); + QObject::connect( + _leftMenu, &QTreeView::pressed, model, &AdvancedTabSelectionModel::onclick + ); + + _leftMenu->setModel( model ); + _leftMenu->setSelectionModel( selectionModel ); + _leftMenu->setFont( fontAwesome ); + _leftMenu->setVisible( true ); + _leftMenu->setSelectionBehavior(QAbstractItemView::SelectRows); + _leftMenu->setCurrentIndex(model->indexFromItem(generalItem)); +} + +void AdvancedTab::_setupGeneralForm(QFont boldFont, QFont fontAwesome) { + _offsetLabel->setText( "Build platform offset:" ); + + _offsetValue->setAlignment( Qt::AlignRight ); + _offsetValue->setFont( boldFont ); + _offsetValue->setText( QString { "%1 µm" }.arg( g_settings.buildPlatformOffset ) ); + + _offsetSlider->setMinimum( 0 ); + _offsetSlider->setMaximum( 40 ); + _offsetSlider->setOrientation( Qt::Horizontal ); + _offsetSlider->setPageStep( 4 ); + _offsetSlider->setSingleStep( 1 ); + _offsetSlider->setTickInterval( 4 ); + _offsetSlider->setTickPosition( QSlider::TicksBothSides ); + _offsetSlider->setValue( g_settings.buildPlatformOffset / 25 ); + QObject::connect( _offsetSlider, &QSlider::sliderReleased, this, &AdvancedTab::offsetSlider_sliderReleased ); + QObject::connect( _offsetSlider, &QSlider::valueChanged, this, &AdvancedTab::offsetSlider_valueChanged ); + + + _buildPlatformOffsetGroup->setContentsMargins( { } ); + _buildPlatformOffsetGroup->setLayout( WrapWidgetsInVBoxDM( + WrapWidgetsInHBox( _offsetLabel, nullptr, _offsetValue ), + _offsetSlider + ) ); + + + _bedHeatingButton->setCheckable( true ); + _bedHeatingButton->setChecked( false ); + _bedHeatingButton->setFont( fontAwesome ); + _bedHeatingButton->setFixedSize( 37, 38 ); + _bedHeatingButton->setText( FA_Times ); + QObject::connect( _bedHeatingButton, &QPushButton::clicked, this, &AdvancedTab::printBedHeatingButton_clicked ); + + _bedHeatingButtonLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + _bedHeatingButtonLabel->setText( "Print bed heating" ); + +#if defined ENABLE_TEMPERATURE_SETTING + _bedTemperatureLabel->setEnabled( false ); + _bedTemperatureLabel->setText( "Print bed temperature:" ); + + _bedTemperatureValue->setAlignment( Qt::AlignRight ); + _bedTemperatureValue->setEnabled( false ); + _bedTemperatureValue->setFont( boldFont ); + _bedTemperatureValue->setText( QString { "%1 °C" }.arg( DefaultPrintBedTemperature ) ); + + _bedTemperatureValueLayout = WrapWidgetsInHBox( _bedTemperatureLabel, nullptr, _bedTemperatureValue ); + _bedTemperatureValueLayout->setEnabled( false ); + + _bedTemperatureSlider->setEnabled( false ); + _bedTemperatureSlider->setMinimum( 30 ); + _bedTemperatureSlider->setMaximum( 50 ); + _bedTemperatureSlider->setOrientation( Qt::Horizontal ); + _bedTemperatureSlider->setPageStep( 1 ); + _bedTemperatureSlider->setSingleStep( 1 ); + _bedTemperatureSlider->setTickInterval( 5 ); + _bedTemperatureSlider->setTickPosition( QSlider::TicksBothSides ); + _bedTemperatureSlider->setValue( DefaultPrintBedTemperature ); + QObject::connect( _bedTemperatureSlider, &QSlider::sliderReleased, this, &AdvancedTab::printBedTemperatureSlider_sliderReleased ); + QObject::connect( _bedTemperatureSlider, &QSlider::valueChanged, this, &AdvancedTab::printBedTemperatureSlider_valueChanged ); +#endif + + auto bedTemperatureLayout = WrapWidgetsInVBoxDM( + WrapWidgetsInHBox( _bedHeatingButton, _bedHeatingButtonLabel, nullptr ) + ); +#if defined ENABLE_TEMPERATURE_SETTING + bedTemperatureLayout->addLayout( _bedTemperatureValueLayout ); + bedTemperatureLayout->addWidget( _bedTemperatureSlider ); +#endif + + _bedHeatingGroup->setContentsMargins( { } ); + _bedHeatingGroup->setLayout( bedTemperatureLayout ); + + + _projectBlankImageButton->setCheckable( true ); + _projectBlankImageButton->setChecked( false ); + _projectBlankImageButton->setFont( fontAwesome ); + _projectBlankImageButton->setFixedSize( 37, 38 ); + _projectBlankImageButton->setText( FA_Times ); + QObject::connect( _projectBlankImageButton, &QPushButton::clicked, this, &AdvancedTab::projectBlankImageButton_clicked ); + + _projectBlankImageButtonLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + _projectBlankImageButtonLabel->setText( "Project blank image" ); + + _projectFocusImageButton->setCheckable( true ); + _projectFocusImageButton->setChecked( false ); + _projectFocusImageButton->setFont( fontAwesome ); + _projectFocusImageButton->setFixedSize( 37, 38 ); + _projectFocusImageButton->setText( FA_Times ); + QObject::connect( _projectFocusImageButton, &QPushButton::clicked, this, &AdvancedTab::projectFocusImageButton_clicked ); + + _projectFocusImageButtonLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + _projectFocusImageButtonLabel->setText( "Project focus image" ); + + _powerLevelLabel->setText( "Projector power level:" ); + + _powerLevelValue->setAlignment( Qt::AlignRight ); + _powerLevelValue->setFont( boldFont ); + _powerLevelValue->setText( "50%" ); + + _powerLevelValueLayout = WrapWidgetsInHBox( _powerLevelLabel, nullptr, _powerLevelValue ); + + _powerLevelSlider->setEnabled( false ); + _powerLevelSlider->setMinimum( ProjectorMinPercent ); + _powerLevelSlider->setMaximum( ProjectorMaxPercent ); + _powerLevelSlider->setOrientation( Qt::Horizontal ); + _powerLevelSlider->setPageStep( 5 ); + _powerLevelSlider->setSingleStep( 1 ); + _powerLevelSlider->setTickInterval( 5 ); + _powerLevelSlider->setTickPosition( QSlider::TicksBothSides ); + _powerLevelSlider->setValue( 50 ); + QObject::connect( _powerLevelSlider, &QSlider::sliderReleased, this, &AdvancedTab::powerLevelSlider_sliderReleased ); + QObject::connect( _powerLevelSlider, &QSlider::valueChanged, this, &AdvancedTab::powerLevelSlider_valueChanged ); + + _powerLevelLabel->setEnabled( false ); + _powerLevelSlider->setEnabled( false ); + _powerLevelValue->setEnabled( false ); + _powerLevelValueLayout->setEnabled( false ); + + + _projectImageButtonsGroup->setContentsMargins( { } ); + _projectImageButtonsGroup->setLayout( WrapWidgetsInVBoxDM( + WrapWidgetsInHBox( _projectBlankImageButton, _projectBlankImageButtonLabel, nullptr, _projectFocusImageButton, _projectFocusImageButtonLabel, nullptr ), + _powerLevelValueLayout, + _powerLevelSlider + ) ); + + + _generalForm->setContentsMargins( { } ); + _generalForm->setMinimumSize( MaximalRightHandPaneSize ); + _generalForm->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + _generalForm->setLayout( WrapWidgetsInVBoxDM( + _buildPlatformOffsetGroup, + _bedHeatingGroup, + _projectImageButtonsGroup, + nullptr + ) ); +} + +void AdvancedTab::_setupTemperaturelForm(QFont boldFont) { + _currentTemperatureLabel->setText( "Current temperature:" ); + _targetTemperatureLabel ->setText( "Target temperature:" ); + _heatingElementLabel ->setText( "Heating element:" ); + _zPositionLabel ->setText( "Z position:" ); + + + _currentTemperature->setAlignment( Qt::AlignRight ); + _currentTemperature->setFont( boldFont ); + _currentTemperature->setText( EmDash ); + + _targetTemperature ->setAlignment( Qt::AlignRight ); + _targetTemperature ->setFont( boldFont ); + _targetTemperature ->setText( EmDash ); + + _heatingElement ->setAlignment( Qt::AlignRight ); + _heatingElement ->setFont( boldFont ); + _heatingElement ->setText( EmDash ); + + _zPosition ->setAlignment( Qt::AlignRight ); + _zPosition ->setFont( boldFont ); + _zPosition ->setText( EmDash ); + + _temperatureForm->setContentsMargins( { } ); + _temperatureForm->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding ); + _temperatureForm->setMinimumSize(MaximalRightHandPaneSize); + _temperatureForm->setLayout( WrapWidgetsInVBoxDM( + WrapWidgetsInHBox( _currentTemperatureLabel, nullptr, _currentTemperature ), + WrapWidgetsInHBox( _targetTemperatureLabel, nullptr, _targetTemperature ), + WrapWidgetsInHBox( _heatingElementLabel, nullptr, _heatingElement ), + WrapWidgetsInHBox( _zPositionLabel, nullptr, _zPosition ), + WrapWidgetsInHBox( _leftMenu ), + nullptr + ) ); +} + +void AdvancedTab::_setupBasePumpForm(QFont boldFont) +{ + _basePumpForm->setMinimumSize(QSize(MaximalRightHandPaneSize.width() + 40, MaximalRightHandPaneSize.height() + 20)); + _basePumpForm->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + + QWidget* container = new QWidget(); + container->setMinimumSize(MaximalRightHandPaneSize); + container->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + + QObject::connect( _addBasePumpCheckbox, &QCheckBox::stateChanged, this, &AdvancedTab::chbox_addBasePumpCheckChanged ); + + _addBasePumpCheckbox->setFont(boldFont); + _addBasePumpCheckbox->setChecked(true); + + QObject::connect( _addBasePumpCheckbox, &QCheckBox::stateChanged, this, &AdvancedTab::chbox_addBodyPumpChanged ); + + QGroupBox* addBasePumpGroup = new QGroupBox(); + addBasePumpGroup->setLayout(WrapWidgetsInVBox(_addBasePumpCheckbox, nullptr)); + addBasePumpGroup->setContentsMargins( { } ); + + + container->setLayout( + WrapWidgetsInVBox( + addBasePumpGroup, + _distanceSlider, + _upTimeSlider, + _upPauseSlider, + _downTimeSlider, + _downPauseSlider, + _upVelocitySlider, + nullptr + )); + + _basePumpForm->setWidget(container); +} + +void AdvancedTab::_setupBaseLayerForm() +{ + _baseLayerForm->setContentsMargins( { } ); + _baseLayerForm->setMinimumSize( MaximalRightHandPaneSize ); + _baseLayerForm->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + _baseLayerForm->setLayout( + WrapWidgetsInVBoxDM( + _numberOfBaseLayersSlider, + _baseThicknessSlider, + _baseExposureTimeSlider, + nullptr + ) + ); +} +void AdvancedTab::_setupBodyLayersForm() +{ + _bodyLayersForm->setContentsMargins( { } ); + _bodyLayersForm->setMinimumSize( MaximalRightHandPaneSize ); + _bodyLayersForm->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + _bodyLayersForm->setLayout( + WrapWidgetsInVBoxDM( + _bodyThicknessSlider, + _bodyExposureTimeSlider, + nullptr + ) + ); +} +void AdvancedTab::_setupBodyPumpForm(QFont boldFont) +{ + QWidget* container = new QWidget(); + + _bodyPumpForm->setMinimumSize(QSize(MaximalRightHandPaneSize.width() + 40, MaximalRightHandPaneSize.height() + 20)); + _bodyPumpForm->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + + container = new QWidget(); + container->setMinimumSize(MaximalRightHandPaneSize); + container->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + + _addBodyPumpCheckbox->setFont(boldFont); + _addBodyPumpCheckbox->setChecked(true); + + QObject::connect( _addBodyPumpCheckbox, &QCheckBox::stateChanged, this, &AdvancedTab::chbox_addBodyPumpChanged ); + + QGroupBox* addBodyPumpGroup = new QGroupBox(); + addBodyPumpGroup->setLayout(WrapWidgetsInVBox(_addBodyPumpCheckbox, nullptr)); + addBodyPumpGroup->setContentsMargins( { } ); + + container->setLayout( + WrapWidgetsInVBox( + addBodyPumpGroup, + _bodyPumpEveryNthLayer, + _bodyDistanceSlider, + _bodyUpTimeSlider, + _bodyUpPauseSlider, + _bodyDownTimeSlider, + _bodyDownPauseSlider, + _bodyUpVelocitySlider, + nullptr + ) + ); + + _bodyPumpForm->setWidget(container); +} + +void AdvancedTab::_setupExportSettingsForm() +{ + QObject::connect(_exportParams, &QPushButton::clicked, this, &AdvancedTab::showExportForm); + + _saveButton->setFixedSize( MainButtonSize ); + _saveButton->setFont( ModifyFont( _importParams->font( ), LargeFontSize ) ); + _saveButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + _fileNameValue->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ); + + _exportSettingsForm->setLayout( + WrapWidgetsInVBox( + WrapWidgetsInHBox(nullptr, _fileNameLabel, _fileNameValue, nullptr), + _saveButton, + nullptr + ) + ); +} + +void AdvancedTab::showExportForm(bool) +{ + auto origFont = font( ); + auto fontAwesome = ModifyFont( origFont, "FontAwesome", LargeFontSize ); + + for(int i=0; isetVisible(false); + } + + _exportSettingsForm->setVisible(true); + + /*QQuickWindow* window = new QQuickWindow(); + + //window->setColor() + + QSurfaceFormat surfaceFormat = window->requestedFormat(); + surfaceFormat.setAlphaBufferSize(8); + surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL); + + window->setFormat(surfaceFormat); + window->setColor(QColor(Qt::transparent)); + window->setClearBeforeRendering(true); + + window-> + /*window->showFullScreen();*/ + + + QInputDialog* inputDialog = new QInputDialog(); + inputDialog->setModal(true); + inputDialog->open(); + inputDialog->move(0,0); + inputDialog->setFont(fontAwesome); + inputDialog->setFocus(Qt::FocusReason::ActiveWindowFocusReason); +} diff --git a/src/advancedtab.h b/src/advancedtab.h index fe9a6c55..d60d3876 100644 --- a/src/advancedtab.h +++ b/src/advancedtab.h @@ -4,6 +4,7 @@ #define ENABLE_TEMPERATURE_SETTING #include "tabbase.h" +#include "paramslider.h" class PngDisplayer; @@ -24,16 +25,6 @@ class AdvancedTab: public TabBase { private: - QLabel* _currentTemperatureLabel { new QLabel }; - QLabel* _targetTemperatureLabel { new QLabel }; - QLabel* _heatingElementLabel { new QLabel }; - QLabel* _zPositionLabel { new QLabel }; - - QLabel* _currentTemperature { new QLabel }; - QLabel* _targetTemperature { new QLabel }; - QLabel* _heatingElement { new QLabel }; - QLabel* _zPosition { new QLabel }; - QLabel* _offsetLabel { new QLabel }; QLabel* _offsetValue { new QLabel }; QSlider* _offsetSlider { new QSlider }; @@ -43,15 +34,6 @@ class AdvancedTab: public TabBase { QPushButton* _bedHeatingButton { new QPushButton }; QLabel* _bedHeatingButtonLabel { new QLabel }; -#if defined ENABLE_TEMPERATURE_SETTING - QLabel* _bedTemperatureLabel { new QLabel }; - QLabel* _bedTemperatureValue { new QLabel }; - QHBoxLayout* _bedTemperatureValueLayout { }; - QSlider* _bedTemperatureSlider { new QSlider }; -#endif - - QGroupBox* _bedHeatingGroup { new QGroupBox }; - QPushButton* _projectBlankImageButton { new QPushButton }; QLabel* _projectBlankImageButtonLabel { new QLabel }; @@ -66,7 +48,87 @@ class AdvancedTab: public TabBase { QGroupBox* _projectImageButtonsGroup { new QGroupBox }; QWidget* _leftColumn { new QWidget }; - QGroupBox* _rightColumn { new QGroupBox }; + QWidget* _rightColumn { new QWidget }; + + //Left panel + QListView* _leftMenu { new QListView }; + + //General form + QGroupBox* _generalForm { new QGroupBox }; + + QGroupBox* _bedHeatingGroup { new QGroupBox }; + + //Temperature form + QWidget* _temperatureForm { new QWidget }; + + QLabel* _currentTemperatureLabel { new QLabel }; + QLabel* _targetTemperatureLabel { new QLabel }; + QLabel* _heatingElementLabel { new QLabel }; + QLabel* _zPositionLabel { new QLabel }; + + QLabel* _currentTemperature { new QLabel }; + QLabel* _targetTemperature { new QLabel }; + QLabel* _heatingElement { new QLabel }; + QLabel* _zPosition { new QLabel }; + +#if defined ENABLE_TEMPERATURE_SETTING + QLabel* _bedTemperatureLabel { new QLabel }; + QLabel* _bedTemperatureValue { new QLabel }; + QHBoxLayout* _bedTemperatureValueLayout { }; + QSlider* _bedTemperatureSlider { new QSlider }; +#endif + + //Base Pump Form + QScrollArea* _basePumpForm { new QScrollArea }; + QCheckBox* _addBasePumpCheckbox { new QCheckBox("add base pump") }; + + ParamSlider* _distanceSlider { new ParamSlider("Base Pump Distance", "µm", 1000, 2000, 1) }; + ParamSlider* _upTimeSlider { new ParamSlider("Base Pump Up Time", "ms", 1000, 2000, 1) }; + ParamSlider* _upPauseSlider { new ParamSlider("Base Pump Up Pause", "ms", 1000, 2000, 1) }; + ParamSlider* _downTimeSlider { new ParamSlider("Base Pump Down Time", "ms", 1000, 2000, 1) }; + ParamSlider* _downPauseSlider { new ParamSlider("Base Pump Down Pause", "ms", 1000, 2000, 1) }; + ParamSlider* _upVelocitySlider { new ParamSlider("Base Pump Up Velocity", "µm/ms", 1000, 2000, 1) }; + + + //Base Layer Form + QWidget* _baseLayerForm { new QWidget }; + + ParamSlider* _numberOfBaseLayersSlider { new ParamSlider("Number of Base Layer", "", 1, 20, 1) }; + ParamSlider* _baseThicknessSlider { new ParamSlider("Base Layer Thickness", "µm", 100, 1000, 1) }; + ParamSlider* _baseExposureTimeSlider { new ParamSlider("Base Pump Up Pause", "ms", 2000, 10000, 1) }; + + //Body Layers Form + QWidget* _bodyLayersForm { new QWidget }; + + ParamSlider* _bodyThicknessSlider { new ParamSlider("Body Layer Thickness", "µm", 20, 1000, 1) }; + ParamSlider* _bodyExposureTimeSlider { new ParamSlider("Base Pump Up Pause", "ms", 2000, 10000, 1) }; + + //Body Pump Form + QScrollArea* _bodyPumpForm { new QScrollArea }; + QCheckBox* _addBodyPumpCheckbox { new QCheckBox("add body pump") }; + + ParamSlider* _bodyPumpEveryNthLayer { new ParamSlider("Body Pump Distance", "µm", 5, 40, 1) }; + ParamSlider* _bodyDistanceSlider { new ParamSlider("Body Pump Distance", "µm", 1000, 2000, 1) }; + ParamSlider* _bodyUpTimeSlider { new ParamSlider("Body Pump Up Time", "ms", 1000, 2000, 1) }; + ParamSlider* _bodyUpPauseSlider { new ParamSlider("Body Pump Up Pause", "ms", 1000, 2000, 1) }; + ParamSlider* _bodyDownTimeSlider { new ParamSlider("Body Pump Down Time", "ms", 1000, 2000, 1) }; + ParamSlider* _bodyDownPauseSlider { new ParamSlider("Body Pump Down Pause", "ms", 1000, 2000, 1) }; + ParamSlider* _bodyUpVelocitySlider { new ParamSlider("Body Pump Up Velocity", "µm/ms", 1000, 2000, 1) }; + + + //Export Settings Form + QWidget* _exportSettingsForm { new QWidget }; + QLabel* _fileNameLabel { new QLabel("file name: ") }; + QLineEdit* _fileNameValue { new QLineEdit }; + QPushButton* _saveButton { new QPushButton("Save Profile") }; + QPushButton* _importParams { new QPushButton("Import settings") }; + QPushButton* _exportParams { new QPushButton("Export settings") }; + + //Import Settings Form + + + static const int FORMS_COUNT { 7 }; + QWidget* _forms[FORMS_COUNT]; PngDisplayer* _pngDisplayer { }; @@ -76,7 +138,14 @@ class AdvancedTab: public TabBase { void _updateControlGroups( ); void _projectImage( char const* fileName ); - + void _setupLeftMenu(QFont fontAwesome); + void _setupGeneralForm(QFont fontBold, QFont fontAwesome); + void _setupTemperaturelForm(QFont fontBold); + void _setupBasePumpForm(QFont fontBold); + void _setupBaseLayerForm(); + void _setupBodyLayersForm(); + void _setupBodyPumpForm(QFont fontBold); + void _setupExportSettingsForm(); signals: ; @@ -92,6 +161,7 @@ public slots: void setPrinterAvailable( bool const value ); void projectorPowerLevel_changed( int const percentage ); + void showExportForm(bool checked=false); protected slots: ; @@ -122,6 +192,10 @@ private slots: void shepherd_sendComplete( bool const success ); + void chbox_addBodyPumpChanged(int); + + void chbox_addBasePumpCheckChanged(int state); + }; #endif // __ADVANCEDTAB_H__ diff --git a/src/advancedtabselectionmodel.cpp b/src/advancedtabselectionmodel.cpp new file mode 100644 index 00000000..95696fca --- /dev/null +++ b/src/advancedtabselectionmodel.cpp @@ -0,0 +1,28 @@ +#include "advancedtabselectionmodel.h" +#include + + +void AdvancedTabSelectionModel::onclick(const QModelIndex &index) +{ + QStandardItem *item = this->itemFromIndex(index); + + int row = index.row(); + int column = index.column(); + + std::cout << row << std::endl; + std::cout << column << std::endl; + + for(int i=0; i<_panelsCount; i++) + { + this->_relatedPanel[i]->setVisible(i==row); + } +} + +AdvancedTabSelectionModel::AdvancedTabSelectionModel(int r,int c, QWidget** relatedPanels, int panelsCount): QStandardItemModel::QStandardItemModel(r,c) +{ + this->_relatedPanel = relatedPanels; + this->_panelsCount = panelsCount; +} + +AdvancedTabSelectionModel::~AdvancedTabSelectionModel() +{ } diff --git a/src/advancedtabselectionmodel.h b/src/advancedtabselectionmodel.h new file mode 100644 index 00000000..e2e0b27e --- /dev/null +++ b/src/advancedtabselectionmodel.h @@ -0,0 +1,21 @@ +#ifndef __ADVANCEDTABSELECTIONMODEL_H__ +#define __ADVANCEDTABSELECTIONMODEL_H__ + +#include "tabbase.h" + +class AdvancedTabSelectionModel: public QStandardItemModel +{ + Q_OBJECT + private: + QWidget** _relatedPanel; + int _panelsCount; + + public: + AdvancedTabSelectionModel(int r,int c, QWidget** relatedPanel, int panelsCount); + ~AdvancedTabSelectionModel(); + + public slots: + void onclick(const QModelIndex &index); +}; + +#endif // __ADVANCEDTABSELECTIONMODEL_H__ diff --git a/src/app.cpp b/src/app.cpp index 0da1ba67..e67633c7 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -251,7 +251,7 @@ App::App( int& argc, char* argv[] ): QApplication( argc, argv ) { QCoreApplication::setApplicationName( "LightField" ); QCoreApplication::setApplicationVersion( LIGHTFIELD_VERSION_STRING ); QGuiApplication::setFont( ModifyFont( QGuiApplication::font( ), "Montserrat", NormalFontSize ) ); - +QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); _parseCommandLine( ); if ( !_isAlreadyRunning( ) ) { diff --git a/src/constants.cpp b/src/constants.cpp index df841fc4..a3d1a949 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -24,6 +24,7 @@ QString const ShepherdPath { "/usr/share/lig QString const SlicedSvgFileName { "sliced.svg" }; QString const StlModelLibraryPath { "/var/lib/lightfield/model-library" }; QString const UpdatesRootPath { "/var/lib/lightfield/software-updates" }; +QString const PrintProfilesPath { "print-profiles" }; QChar const LineFeed { L'\u000A' }; QChar const CarriageReturn { L'\u000D' }; diff --git a/src/constants.h b/src/constants.h index fea6a83f..04839ab7 100644 --- a/src/constants.h +++ b/src/constants.h @@ -24,6 +24,7 @@ QString extern const ShepherdPath; QString extern const SlicedSvgFileName; QString extern const StlModelLibraryPath; QString extern const UpdatesRootPath; +QString extern const PrintProfilesPath; QChar extern const LineFeed; QChar extern const CarriageReturn; diff --git a/src/main.cpp b/src/main.cpp index 682b6dc8..67ac6ff7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,8 @@ void _initializeOpenGL( ) { } int main( int argc, char* argv[] ) { + qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); + qputenv("QT_VIRTUALKEYBOARD_STYLE_PATH","./styles"); _initializeOpenGL( ); return App( argc, argv ).exec( ); } diff --git a/src/paramslider.cpp b/src/paramslider.cpp new file mode 100644 index 00000000..11d9bc91 --- /dev/null +++ b/src/paramslider.cpp @@ -0,0 +1,51 @@ +#include "paramslider.h" + +void ParamSlider::init(QString name, QString unit, int startValue, int maxValue, int step) +{ + QString valueLabel = QString::number(startValue) + QString(" ") + unit; + auto origFont = font( ); + auto boldFont = ModifyFont( origFont, QFont::Bold ); + + this->_nameLabel->setText(name); + this->_valueLabel->setText(valueLabel); + this->_valueLabel->setFont(boldFont); + this->_slider->setValue(startValue); + this->_slider->setMaximum(maxValue); + this->_slider->setSingleStep(step); + this->_slider->setOrientation(Qt::Orientation::Horizontal); + + QWidget::connect(this->_slider, &QSlider::valueChanged, this, &ParamSlider::onvaluechanged); + + this->setLayout( + WrapWidgetsInVBoxDM( + WrapWidgetsInHBox(_nameLabel, nullptr, _valueLabel), + _slider + ) + ); + + _unit = unit; +} + +int ParamSlider::getValue() +{ + return _slider->value(); +} + +void ParamSlider::onvaluechanged(int value) +{ + this->_valueLabel->setText(QString::number(_slider->value()) + " " + QString(_unit)); +} + +ParamSlider::ParamSlider(QString name, QString unit, int startValue, int maxValue, int step) +{ + this->init(name, unit, startValue, maxValue, step); +} + +ParamSlider::ParamSlider(QString name, int maxValue) +{ + this->init(name, "", 0, maxValue, 1); +} + +ParamSlider::~ParamSlider() +{ } + diff --git a/src/paramslider.h b/src/paramslider.h new file mode 100644 index 00000000..2d778283 --- /dev/null +++ b/src/paramslider.h @@ -0,0 +1,27 @@ +#ifndef __PARAMSLIDER_H__ +#define __PARAMSLIDER_H__ + +#include "tabbase.h" + +class ParamSlider: public QGroupBox { + Q_OBJECT + private: + QSlider* _slider { new QSlider }; + QLabel* _nameLabel { new QLabel }; + QLabel* _valueLabel { new QLabel }; + QString _unit; + + void init(QString name, QString unit, int startValue, int maxValue, int step); + + public: + ParamSlider(QString name, QString unit, int startValue, int maxValue, int step); + ParamSlider(QString name, int maxValue); + ~ParamSlider(); + + int getValue(); + + public slots: + void onvaluechanged(int value); +}; + +#endif // __PARAMSLIDER_H__ diff --git a/src/window.cpp b/src/window.cpp index f995fc42..56eb9fdd 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -37,7 +37,6 @@ Window::Window( QWidget* parent ): QMainWindow( parent ) { #if defined _DEBUG _isPrinterPrepared = g_settings.pretendPrinterIsPrepared; #endif // _DEBUG - setWindowFlags( windowFlags( ) | ( g_settings.frameless ? Qt::FramelessWindowHint : Qt::BypassWindowManagerHint ) ); setFixedSize( MainWindowSize ); move( g_settings.mainWindowPosition ); @@ -485,3 +484,9 @@ void Window::signalHandler_signalReceived( siginfo_t const& info ) { close( ); } + +void Window::showEvent( QShowEvent* aShowEvent ) +{ + QMainWindow::showEvent(aShowEvent); + activateWindow(); +} diff --git a/src/window.h b/src/window.h index 525c3216..5b1be2db 100644 --- a/src/window.h +++ b/src/window.h @@ -35,7 +35,7 @@ class Window: public QMainWindow { protected: virtual void closeEvent( QCloseEvent* event ) override; - + void showEvent( QShowEvent* aShowEvent ) override; private: SignalHandler* _signalHandler { }; @@ -74,7 +74,6 @@ class Window: public QMainWindow { void printerPrepared( bool const value ); void terminationRequested( ); - public slots: protected slots: From e1110dbb201d3ea7ac799b90c0bb45eacd74bb45 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 28 Jan 2020 12:27:17 -0800 Subject: [PATCH 60/89] Class PrintPumpingParameters: Set default values, and add new methods pumpDown{Distance,Time,Velocity}_Effective. --- src/printpumpingparameters.h | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/printpumpingparameters.h b/src/printpumpingparameters.h index 76da3daa..aa860044 100644 --- a/src/printpumpingparameters.h +++ b/src/printpumpingparameters.h @@ -35,9 +35,19 @@ class PrintPumpingParameters { return _pumpUpPause; } + // unit: mm, multiples of 0.01 + double pumpDownDistance_Effective( ) const { + return _pumpUpDistance - ( _layerThickness / 1000.0 ); + } + // unit: ms - int pumpDownTime( ) const { - return _pumpDownTime; + int pumpDownTime_Effective( ) const { + return ( pumpDownDistance_Effective( ) / pumpDownVelocity_Effective( ) ) / ( 60.0 / 1000.0 ) + 0.5; + } + + // unit: mm/min + double pumpDownVelocity_Effective( ) const { + return pumpDownDistance_Effective( ) / ( pumpDownTime_Effective( ) / 1000.0 / 60.0 ); } // unit: ms @@ -84,11 +94,6 @@ class PrintPumpingParameters { _pumpUpPause = value; } - // unit: ms - void setPumpDownTime( int const value ) { - _pumpDownTime = value; - } - // unit: ms void setPumpDownPause( int const value ) { _pumpDownPause = value; @@ -116,15 +121,14 @@ class PrintPumpingParameters { private: - double _pumpUpDistance { }; - int _pumpUpTime { }; - int _pumpUpPause { }; - int _pumpDownTime { }; - int _pumpDownPause { }; - int _noPumpUpVelocity { }; - int _layerThickness { }; - int _layerExposureTime { }; - int _pumpEveryNthLayer { }; + double _pumpUpDistance { 2.00 }; // mm + int _pumpUpTime { 600 }; // ms + int _pumpUpPause { 2000 }; // ms + int _pumpDownPause { 4000 }; // ms + int _noPumpUpVelocity { 200 }; // mm/min + int _layerThickness { 100 }; // µm + int _layerExposureTime { 1000 }; // ms + int _pumpEveryNthLayer { 1 }; }; From 846ce6a253b733bf5969891090542cbebb739f9b Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 28 Jan 2020 12:33:32 -0800 Subject: [PATCH 61/89] Script "rebuild": Fix misplaced else clause. --- rebuild | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rebuild b/rebuild index 6a1da631..c7ec5349 100755 --- a/rebuild +++ b/rebuild @@ -103,11 +103,11 @@ function rebuild () { red-bar "Run change-version-number.sh manually." exit 1 fi + else + red-bar "src/version.h does not exist, and the version number is unconfigured." + red-bar "Run change-version-number.sh manually." + exit 1 fi - else - red-bar "src/version.h does not exist, and the version number is unconfigured." - red-bar "Run change-version-number.sh manually." - exit 1 fi # shellcheck disable=SC2164 From a04e5c074fd3ad67b0a12b0d68e25d5d8bb89b5a Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 28 Jan 2020 14:06:19 -0800 Subject: [PATCH 62/89] Class PrintPumpingParameters: Add power level; rearrange a bit. --- src/printpumpingparameters.h | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/printpumpingparameters.h b/src/printpumpingparameters.h index aa860044..18695281 100644 --- a/src/printpumpingparameters.h +++ b/src/printpumpingparameters.h @@ -60,6 +60,11 @@ class PrintPumpingParameters { return _noPumpUpVelocity; } + // unit: none + int pumpEveryNthLayer( ) const { + return _pumpEveryNthLayer; + } + // unit: µm int layerThickness( ) const { return _layerThickness; @@ -70,9 +75,9 @@ class PrintPumpingParameters { return _layerExposureTime; } - // unit: none - int pumpEveryNthLayer( ) const { - return _pumpEveryNthLayer; + // unit: percent + double powerLevel( ) const { + return _powerLevel; } // @@ -104,6 +109,11 @@ class PrintPumpingParameters { _noPumpUpVelocity = value; } + // unit: none + void setPumpEveryNthLayer( int const value ) { + _pumpEveryNthLayer = value; + } + // unit: µm void setLayerThickness( int const value ) { _layerThickness = value; @@ -114,9 +124,9 @@ class PrintPumpingParameters { _layerExposureTime = value; } - // unit: none - void setPumpEveryNthLayer( int const value ) { - _pumpEveryNthLayer = value; + // unit: percent + void setPowerLevel( double const value ) { + _powerLevel = value; } private: @@ -126,9 +136,10 @@ class PrintPumpingParameters { int _pumpUpPause { 2000 }; // ms int _pumpDownPause { 4000 }; // ms int _noPumpUpVelocity { 200 }; // mm/min + int _pumpEveryNthLayer { 1 }; int _layerThickness { 100 }; // µm int _layerExposureTime { 1000 }; // ms - int _pumpEveryNthLayer { 1 }; + double _powerLevel { 50.0 }; // percent }; From 787b9a08e0753665504d375ca5d3a480ef0fdffa Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 28 Jan 2020 12:33:32 -0800 Subject: [PATCH 63/89] Script "rebuild": Fix misplaced else clause. --- rebuild | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rebuild b/rebuild index 6a1da631..c7ec5349 100755 --- a/rebuild +++ b/rebuild @@ -103,11 +103,11 @@ function rebuild () { red-bar "Run change-version-number.sh manually." exit 1 fi + else + red-bar "src/version.h does not exist, and the version number is unconfigured." + red-bar "Run change-version-number.sh manually." + exit 1 fi - else - red-bar "src/version.h does not exist, and the version number is unconfigured." - red-bar "Run change-version-number.sh manually." - exit 1 fi # shellcheck disable=SC2164 From 77f59a21ce353aef8b90db97236e3258cf371bdd Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 28 Jan 2020 14:43:53 -0800 Subject: [PATCH 64/89] Scripts: Change LIGHTFIELD_ROOT to the current directory. --- change-version-number.sh | 2 +- install-lightfield.sh | 14 +++++++------- make-deb-package.sh | 2 +- make-upgrade-kit.sh | 8 ++++---- rebuild | 4 ++-- unpack-kit-manually.sh | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/change-version-number.sh b/change-version-number.sh index c482c392..5f3b87a5 100755 --- a/change-version-number.sh +++ b/change-version-number.sh @@ -1,7 +1,7 @@ #!/bin/bash # shellcheck disable=SC2164 -LIGHTFIELD_ROOT="/home/lumen/Volumetric/LightField" +LIGHTFIELD_ROOT="${PWD}" ######################################################### ## ## diff --git a/install-lightfield.sh b/install-lightfield.sh index 315bd28b..b7dbf6ec 100755 --- a/install-lightfield.sh +++ b/install-lightfield.sh @@ -1,6 +1,6 @@ #!/bin/bash -LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField +LIGHTFIELD_ROOT="${PWD}" ######################################################### ## ## @@ -89,24 +89,24 @@ fi blue-bar • Building debugging version of set-projector-power # shellcheck disable=SC2164 -cd ${PROJECTOR_SRC} +cd "${PROJECTOR_SRC}" # shellcheck disable=SC2015 [ "${FORCEREBUILD}" = "-x" ] && make BUILD=debug clean || true make BUILD=debug blue-bar • Building debugging version of Mountmon # shellcheck disable=SC2164 -cd ${MOUNTMON_SRC} +cd "${MOUNTMON_SRC}" ./rebuild ${FORCEREBUILD} ${BUILDQUIETLY} blue-bar • Building debugging version of LightField # shellcheck disable=SC2164 -cd ${LIGHTFIELD_ROOT} +cd "${LIGHTFIELD_ROOT}" ./rebuild ${FORCEREBUILD} ${BUILDQUIETLY} -chown ${CHXXXVERBOSE} -R lumen:lumen ${PROJECTOR_SRC} -chown ${CHXXXVERBOSE} -R lumen:lumen ${MOUNTMON_SRC}/build -chown ${CHXXXVERBOSE} -R lumen:lumen ${LIGHTFIELD_ROOT}/build +chown ${CHXXXVERBOSE} -R lumen:lumen "${PROJECTOR_SRC}" +chown ${CHXXXVERBOSE} -R lumen:lumen "${MOUNTMON_SRC}/build" +chown ${CHXXXVERBOSE} -R lumen:lumen "${LIGHTFIELD_ROOT}/build" blue-bar • Creating any missing directories [ ! -d /var/cache/lightfield/print-jobs ] && mkdir ${VERBOSE} -p /var/cache/lightfield/print-jobs diff --git a/make-deb-package.sh b/make-deb-package.sh index 74ee902d..fd3291b1 100755 --- a/make-deb-package.sh +++ b/make-deb-package.sh @@ -3,7 +3,7 @@ # shellcheck disable=SC2164 BUILDTYPE= -LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField +LIGHTFIELD_ROOT="${PWD}" PACKAGE_BUILD_ROOT=${LIGHTFIELD_ROOT}/packaging ######################################################### diff --git a/make-upgrade-kit.sh b/make-upgrade-kit.sh index de44d11c..14fa4f83 100755 --- a/make-upgrade-kit.sh +++ b/make-upgrade-kit.sh @@ -2,7 +2,7 @@ # shellcheck disable=SC2103 # shellcheck disable=SC2164 -LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField +LIGHTFIELD_ROOT="${PWD}" PACKAGE_BUILD_ROOT=${LIGHTFIELD_ROOT}/packaging USE_KEY_SET=current BUILDTYPE= @@ -150,7 +150,7 @@ cd "${REPO_DIR}" dpkg-scanpackages . | tee Packages | xz -ceT0 > Packages.xz -apt-ftparchive --config-file ${LIGHTFIELD_ROOT}/apt-files/release.conf release . | tee Release | xz -ceT0 > Release.xz +apt-ftparchive --config-file "${LIGHTFIELD_ROOT}/apt-files/release.conf" release . | tee Release | xz -ceT0 > Release.xz gpg \ ${VERBOSE} \ @@ -185,13 +185,13 @@ sha256sum -- -b * | sed -r -e 's/^/ /' -e 's/ +\*/ /' > .hashes "${LIGHTFIELD_ROOT}/apt-files/version.inf.in" # extract description from ${LIGHTFIELD_ROOT}/debian/changelog - linecount=$(grep -n '^ -- LightField packager' ${LIGHTFIELD_ROOT}/debian/changelog | head -1 | cut -d: -f1 || echo 0) + linecount=$(grep -n '^ -- LightField packager' "${LIGHTFIELD_ROOT}/debian/changelog" | head -1 | cut -d: -f1 || echo 0) if [ -z "${linecount}" ] || [ "${linecount}" -lt 1 ] then red-bar "!!! Can't find end of first change in ${LIGHTFIELD_ROOT}/debian/changelog, aborting" exit 1 fi - head -$((linecount - 2)) ${LIGHTFIELD_ROOT}/debian/changelog | tail +3 | perl -lpe 's/\s+$//; s/^$/./; s/^/ /' + head -$((linecount - 2)) "${LIGHTFIELD_ROOT}/debian/changelog" | tail +3 | perl -lpe 's/\s+$//; s/^$/./; s/^/ /' echo 'Checksums-SHA256:' cat .hashes diff --git a/rebuild b/rebuild index c7ec5349..ea764e95 100755 --- a/rebuild +++ b/rebuild @@ -1,6 +1,6 @@ #!/bin/bash -LIGHTFIELD_ROOT=/home/lumen/Volumetric/LightField +LIGHTFIELD_ROOT="${PWD}" ######################################################### ## ## @@ -76,7 +76,7 @@ function rebuild () { NUKE=yes fi - LASTBUILDMODE="$(<${BUILDDIR}/.lastbuildmode)" + LASTBUILDMODE=$(<"${BUILDDIR}/.lastbuildmode") if [ -n "${LASTBUILDMODE}" ] && [ "${LASTBUILDMODE}" != "${BUILD}:${RELEASE_TRAIN}" ] then echo Switching from "${LASTBUILDMODE}" to "${BUILD}:${RELEASE_TRAIN}". diff --git a/unpack-kit-manually.sh b/unpack-kit-manually.sh index 43d13eb5..99c9da12 100755 --- a/unpack-kit-manually.sh +++ b/unpack-kit-manually.sh @@ -1,7 +1,7 @@ #!/bin/bash VERSION=1.0.11.0 -PACKAGE_BUILD_ROOT=/home/lumen/Volumetric/LightField/packaging +PACKAGE_BUILD_ROOT="${PWD}/packaging" ######################################################### ## ## From a9a0e2118438f5159abb2e59c80029f99e533539 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 28 Jan 2020 14:44:46 -0800 Subject: [PATCH 65/89] Move some scripts into new folder old-scripts/ . --- LightFieldSymlinksFix.sh => old-scripts/LightFieldSymlinksFix.sh | 0 .../MakeLightFieldDirectories | 0 PrepareSystem.sh => old-scripts/PrepareSystem.sh | 0 SetUp => old-scripts/SetUp | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename LightFieldSymlinksFix.sh => old-scripts/LightFieldSymlinksFix.sh (100%) rename MakeLightFieldDirectories => old-scripts/MakeLightFieldDirectories (100%) rename PrepareSystem.sh => old-scripts/PrepareSystem.sh (100%) rename SetUp => old-scripts/SetUp (100%) diff --git a/LightFieldSymlinksFix.sh b/old-scripts/LightFieldSymlinksFix.sh similarity index 100% rename from LightFieldSymlinksFix.sh rename to old-scripts/LightFieldSymlinksFix.sh diff --git a/MakeLightFieldDirectories b/old-scripts/MakeLightFieldDirectories similarity index 100% rename from MakeLightFieldDirectories rename to old-scripts/MakeLightFieldDirectories diff --git a/PrepareSystem.sh b/old-scripts/PrepareSystem.sh similarity index 100% rename from PrepareSystem.sh rename to old-scripts/PrepareSystem.sh diff --git a/SetUp b/old-scripts/SetUp similarity index 100% rename from SetUp rename to old-scripts/SetUp From 7e3e1356341a513476968f22a7ea3a39e5a636bf Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 28 Jan 2020 14:47:26 -0800 Subject: [PATCH 66/89] Update .gitignore. --- .gitignore | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 35af18a8..98739513 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,43 @@ -.gdbinit -.vs -.vscode build +calculate-ctm.txt class-template.txt +cleaned.txt copy-to-usb-stick.sh deps-junk-1.txt deps-junk-2.txt deps-junk-3.txt +display-args.func dlp4710/set-projector-power dlp4710/set-projector-power.o do-it.sh +fresh-pkg-list.txt +.gdbinit gpg/gpg-agent.conf gpg/openpgp-revocs.d gpg/private-keys-v1.d gpg/random_seed +images/focus-image.png +images/white-field.png installer +install-overlap.txt +listcomp.pl +listfilter.pl model-library +mountmon/build mountmon/.gdbinit -mountmon/.qmake.stash mountmon/Makefile -mountmon/build +mountmon/.qmake.stash newConfigureUbuntuAndInstallLightField.sh out packaging +rpi4_packages-overlap.txt +rpi4_packages-to-install.txt src/version.h superclean.sh -usb-driver/set-projector-power +ubuntu-cosmic-18.10-packages.txt usb-driver/dlpc350_api.o usb-driver/dlpc350_usb.o usb-driver/main.o +usb-driver/set-projector-power +.vs +.vscode From bedd7fdb34f3e2c0c5cde6a37e9ef85125b5f4c7 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 28 Jan 2020 19:11:44 -0800 Subject: [PATCH 67/89] Class PrintProfile, methods {base,body}LayersPumpingParameters: de-const-ify. --- src/printprofile.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/printprofile.h b/src/printprofile.h index 4861fdd9..3e341824 100644 --- a/src/printprofile.h +++ b/src/printprofile.h @@ -29,11 +29,11 @@ class PrintProfile: public QObject { return _baseLayerCount; } - PrintPumpingParameters const& baseLayersPumpingParameters( ) const { + PrintPumpingParameters& baseLayersPumpingParameters( ) { return _baseLayersPumpingParameters; } - PrintPumpingParameters const& bodyLayersPumpingParameters( ) const { + PrintPumpingParameters& bodyLayersPumpingParameters( ) { return _bodyLayersPumpingParameters; } From 7552b74d76aa80cf1f7f99f5e550cfd71a2e4b60 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Wed, 29 Jan 2020 12:39:16 -0800 Subject: [PATCH 68/89] Class PrintJob: Add new member printProfile. --- src/printjob.h | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/printjob.h b/src/printjob.h index e37ee5cd..14295389 100644 --- a/src/printjob.h +++ b/src/printjob.h @@ -2,6 +2,7 @@ #define __PRINTJOB_H__ #include "coordinate.h" +#include "printprofile.h" class PrintJob { @@ -28,28 +29,30 @@ class PrintJob { exposureTimeScaleFactor = value->exposureTimeScaleFactor; powerLevel = value->powerLevel; printSpeed = value->printSpeed; + printProfile = value->printProfile; } ~PrintJob( ) { /*empty*/ } - size_t vertexCount { }; - Coordinate x { }; - Coordinate y { }; - Coordinate z { }; - double estimatedVolume { }; // unit: µL - - QString modelFileName { }; - QString modelHash { }; - QString jobWorkingDirectory { }; - - int layerCount { }; - int layerThickness { 100 }; // unit: µm - double exposureTime { 1.0 }; // unit: s - double exposureTimeScaleFactor { 1.0 }; // for first two layers - int powerLevel { static_cast( ProjectorMaxPowerLevel / 2.0 + 0.5 ) }; // range: 0..ProjectorMaxPowerLevel - double printSpeed { PrinterDefaultLowSpeed }; // unit: mm/min; range: 50-200 + size_t vertexCount { }; + Coordinate x { }; + Coordinate y { }; + Coordinate z { }; + double estimatedVolume { }; // unit: µL + + QString modelFileName { }; + QString modelHash { }; + QString jobWorkingDirectory { }; + + int layerCount { }; + int layerThickness { 100 }; // unit: µm + double exposureTime { 1.0 }; // unit: s + double exposureTimeScaleFactor { 1.0 }; // for first two layers + int powerLevel { static_cast( ProjectorMaxPowerLevel / 2.0 + 0.5 ) }; // range: 0..ProjectorMaxPowerLevel + double printSpeed { PrinterDefaultLowSpeed }; // unit: mm/min; range: 50-200 + PrintProfile* printProfile { }; }; From d4819936f93938c3247216c476fb1353f4bcce82 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Wed, 29 Jan 2020 12:44:14 -0800 Subject: [PATCH 69/89] Class ProfilesTab: Add member variable _printProfileManager. --- src/profilestab.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/profilestab.h b/src/profilestab.h index f7ba7261..4ec0b402 100644 --- a/src/profilestab.h +++ b/src/profilestab.h @@ -2,7 +2,6 @@ #define __PROFILESTAB_H__ #include "tabbase.h" - #include "printprofilemanager.h" class ProfilesTab: public TabBase { @@ -16,10 +15,16 @@ class ProfilesTab: public TabBase { virtual TabIndex tabIndex( ) const override { return TabIndex::Profiles; } + void setPrintProfileManager( PrintProfileManager* printProfileManager ) { + _printProfileManager = printProfileManager; + } + protected: private: + PrintProfileManager* _printProfileManager; + signals: ; From 6970268a21a9b38c20aa9bd2285a23ef5a27bdb7 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Wed, 29 Jan 2020 12:44:55 -0800 Subject: [PATCH 70/89] Class Window: Construct a PrintProfilesManager and hand it off to ProfilesTab. --- src/window.cpp | 15 +++++++++------ src/window.h | 52 ++++++++++++++++++++++++++++---------------------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/window.cpp b/src/window.cpp index f995fc42..c828e065 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -5,6 +5,7 @@ #include "pngdisplayer.h" #include "printjob.h" #include "printmanager.h" +#include "printprofilemanager.h" #include "shepherd.h" #include "signalhandler.h" #include "upgrademanager.h" @@ -57,8 +58,9 @@ Window::Window( QWidget* parent ): QMainWindow( parent ) { _printJob = new PrintJob; - _upgradeManager = new UpgradeManager; - _usbMountManager = new UsbMountManager; + _printProfileManager = new PrintProfileManager; + _upgradeManager = new UpgradeManager; + _usbMountManager = new UsbMountManager; QObject::connect( _usbMountManager, &UsbMountManager::ready, _upgradeManager, [this] ( ) { QObject::connect( _usbMountManager, &UsbMountManager::filesystemMounted, _upgradeManager, &UpgradeManager::checkForUpgrades ); @@ -91,10 +93,11 @@ Window::Window( QWidget* parent ): QMainWindow( parent ) { emit shepherdChanged( _shepherd ); emit printJobChanged( _printJob ); - _fileTab ->setUsbMountManager( _usbMountManager ); - _advancedTab->setPngDisplayer ( _pngDisplayer ); - _systemTab ->setUpgradeManager ( _upgradeManager ); - _systemTab ->setUsbMountManager( _usbMountManager ); + _fileTab ->setUsbMountManager ( _usbMountManager ); + _advancedTab->setPngDisplayer ( _pngDisplayer ); + _profilesTab->setPrintProfileManager( _printProfileManager ); + _systemTab ->setUpgradeManager ( _upgradeManager ); + _systemTab ->setUsbMountManager ( _usbMountManager ); _shepherd->start( ); diff --git a/src/window.h b/src/window.h index 525c3216..1a0dff72 100644 --- a/src/window.h +++ b/src/window.h @@ -6,8 +6,9 @@ class ModelSelectionInfo; class PngDisplayer; -class PrintManager; class PrintJob; +class PrintManager; +class PrintProfileManager; class Shepherd; class SignalHandler; class UpgradeManager; @@ -38,33 +39,35 @@ class Window: public QMainWindow { private: - SignalHandler* _signalHandler { }; - ModelSelectionInfo* _modelSelection { }; - PngDisplayer* _pngDisplayer { }; - PrintJob* _printJob { }; - PrintManager* _printManager { }; - Shepherd* _shepherd { }; - UiState _uiState { }; - UpgradeManager* _upgradeManager { }; - UsbMountManager* _usbMountManager { }; - - QTabWidget* _tabWidget { new QTabWidget }; - FileTab* _fileTab; - PrepareTab* _prepareTab; - PrintTab* _printTab; - StatusTab* _statusTab; - AdvancedTab* _advancedTab; - ProfilesTab* _profilesTab; - SystemTab* _systemTab; - QPushButton* _helpButton { new QPushButton }; - - bool _isPrinterPrepared { }; - bool _isModelRendered { }; + SignalHandler* _signalHandler { }; + ModelSelectionInfo* _modelSelection { }; + PngDisplayer* _pngDisplayer { }; + PrintJob* _printJob { }; + PrintManager* _printManager { }; + PrintProfileManager* _printProfileManager { }; + Shepherd* _shepherd { }; + UiState _uiState { }; + UpgradeManager* _upgradeManager { }; + UsbMountManager* _usbMountManager { }; + + QTabWidget* _tabWidget { new QTabWidget }; + FileTab* _fileTab; + PrepareTab* _prepareTab; + PrintTab* _printTab; + StatusTab* _statusTab; + AdvancedTab* _advancedTab; + ProfilesTab* _profilesTab; + SystemTab* _systemTab; + QPushButton* _helpButton { new QPushButton }; + + bool _isPrinterPrepared { }; + bool _isModelRendered { }; void _setPrinterPrepared( bool const value ); void _setModelRendered( bool const value ); signals: + ; void printJobChanged( PrintJob* printJob ); void printManagerChanged( PrintManager* printManager ); @@ -76,10 +79,13 @@ class Window: public QMainWindow { void terminationRequested( ); public slots: + ; protected slots: + ; private slots: + ; void startPrinting( ); From e0f21c3c5b92974b1e45166d5c6871b2353fa5fe Mon Sep 17 00:00:00 2001 From: sagittarius89 Date: Thu, 30 Jan 2020 17:21:53 +0100 Subject: [PATCH 71/89] - added profiles tab layout - added json parser - added gui modal messages - some improvements in advanced tab --- qt/lf.pro | 1 + src/advancedtab.cpp | 77 +---------- src/advancedtab.h | 15 +-- src/constants.cpp | 5 +- src/printprofile.h | 2 +- src/profilesjsonparser.h | 206 +++++++++++++++++++++++++++++ src/profilestab.cpp | 277 +++++++++++++++++++++++++++++++++++++++ src/profilestab.h | 27 +++- 8 files changed, 518 insertions(+), 92 deletions(-) create mode 100644 src/profilesjsonparser.h diff --git a/qt/lf.pro b/qt/lf.pro index 1f18bc1f..86bd2e4b 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -84,6 +84,7 @@ HEADERS += \ ../src/printpumpingparameters.h \ ../src/printtab.h \ ../src/processrunner.h \ + ../src/profilesjsonparser.h \ ../src/profilestab.h \ ../src/shepherd.h \ ../src/signalhandler.h \ diff --git a/src/advancedtab.cpp b/src/advancedtab.cpp index 2c7ecb4d..fa6161a8 100644 --- a/src/advancedtab.cpp +++ b/src/advancedtab.cpp @@ -22,26 +22,12 @@ AdvancedTab::AdvancedTab( QWidget* parent ): TabBase( parent ) { auto boldFont = ModifyFont( origFont, QFont::Bold ); auto fontAwesome = ModifyFont( origFont, "FontAwesome", LargeFontSize ); - // import/export operations - - _importParams->setFixedSize( MainButtonSize ); - _importParams->setFont( ModifyFont( _importParams->font( ), LargeFontSize ) ); - _importParams->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); - - - _exportParams->setFixedSize( MainButtonSize ); - _exportParams->setFont( ModifyFont( _exportParams->font( ), LargeFontSize ) ); - _exportParams->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); - - _setupExportSettingsForm(); - _forms[0] = _generalForm; _forms[1] = _temperatureForm; _forms[2] = _basePumpForm; _forms[3] = _baseLayerForm; _forms[4] = _bodyLayersForm; _forms[5] = _bodyPumpForm; - _forms[6] = _exportSettingsForm; //menu this->_setupLeftMenu(fontAwesome); @@ -69,10 +55,10 @@ AdvancedTab::AdvancedTab( QWidget* parent ): TabBase( parent ) { _rightColumn->setLayout(WrapWidgetsInVBox(_generalForm, _temperatureForm, _basePumpForm, _baseLayerForm, - _bodyLayersForm, _bodyPumpForm, _exportSettingsForm, nullptr)); + _bodyLayersForm, _bodyPumpForm, nullptr)); setLayout( WrapWidgetsInHBox( - WrapWidgetsInVBox(_leftMenu, _importParams, _exportParams), + WrapWidgetsInVBox( _leftMenu ), _rightColumn, nullptr ) ); @@ -507,7 +493,7 @@ void AdvancedTab::_setupTemperaturelForm(QFont boldFont) { void AdvancedTab::_setupBasePumpForm(QFont boldFont) { - _basePumpForm->setMinimumSize(QSize(MaximalRightHandPaneSize.width() + 40, MaximalRightHandPaneSize.height() + 20)); + _basePumpForm->setMinimumSize(QSize(MaximalRightHandPaneSize.width() + 35, MaximalRightHandPaneSize.height() + 25)); _basePumpForm->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); QWidget* container = new QWidget(); @@ -572,7 +558,7 @@ void AdvancedTab::_setupBodyPumpForm(QFont boldFont) { QWidget* container = new QWidget(); - _bodyPumpForm->setMinimumSize(QSize(MaximalRightHandPaneSize.width() + 40, MaximalRightHandPaneSize.height() + 20)); + _bodyPumpForm->setMinimumSize(QSize(MaximalRightHandPaneSize.width() + 35, MaximalRightHandPaneSize.height() + 25)); _bodyPumpForm->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); container = new QWidget(); @@ -604,58 +590,3 @@ void AdvancedTab::_setupBodyPumpForm(QFont boldFont) _bodyPumpForm->setWidget(container); } - -void AdvancedTab::_setupExportSettingsForm() -{ - QObject::connect(_exportParams, &QPushButton::clicked, this, &AdvancedTab::showExportForm); - - _saveButton->setFixedSize( MainButtonSize ); - _saveButton->setFont( ModifyFont( _importParams->font( ), LargeFontSize ) ); - _saveButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); - - _fileNameValue->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ); - - _exportSettingsForm->setLayout( - WrapWidgetsInVBox( - WrapWidgetsInHBox(nullptr, _fileNameLabel, _fileNameValue, nullptr), - _saveButton, - nullptr - ) - ); -} - -void AdvancedTab::showExportForm(bool) -{ - auto origFont = font( ); - auto fontAwesome = ModifyFont( origFont, "FontAwesome", LargeFontSize ); - - for(int i=0; isetVisible(false); - } - - _exportSettingsForm->setVisible(true); - - /*QQuickWindow* window = new QQuickWindow(); - - //window->setColor() - - QSurfaceFormat surfaceFormat = window->requestedFormat(); - surfaceFormat.setAlphaBufferSize(8); - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL); - - window->setFormat(surfaceFormat); - window->setColor(QColor(Qt::transparent)); - window->setClearBeforeRendering(true); - - window-> - /*window->showFullScreen();*/ - - - QInputDialog* inputDialog = new QInputDialog(); - inputDialog->setModal(true); - inputDialog->open(); - inputDialog->move(0,0); - inputDialog->setFont(fontAwesome); - inputDialog->setFocus(Qt::FocusReason::ActiveWindowFocusReason); -} diff --git a/src/advancedtab.h b/src/advancedtab.h index d60d3876..85b8347a 100644 --- a/src/advancedtab.h +++ b/src/advancedtab.h @@ -116,18 +116,7 @@ class AdvancedTab: public TabBase { ParamSlider* _bodyUpVelocitySlider { new ParamSlider("Body Pump Up Velocity", "µm/ms", 1000, 2000, 1) }; - //Export Settings Form - QWidget* _exportSettingsForm { new QWidget }; - QLabel* _fileNameLabel { new QLabel("file name: ") }; - QLineEdit* _fileNameValue { new QLineEdit }; - QPushButton* _saveButton { new QPushButton("Save Profile") }; - QPushButton* _importParams { new QPushButton("Import settings") }; - QPushButton* _exportParams { new QPushButton("Export settings") }; - - //Import Settings Form - - - static const int FORMS_COUNT { 7 }; + static const int FORMS_COUNT { 6 }; QWidget* _forms[FORMS_COUNT]; PngDisplayer* _pngDisplayer { }; @@ -145,7 +134,6 @@ class AdvancedTab: public TabBase { void _setupBaseLayerForm(); void _setupBodyLayersForm(); void _setupBodyPumpForm(QFont fontBold); - void _setupExportSettingsForm(); signals: ; @@ -161,7 +149,6 @@ public slots: void setPrinterAvailable( bool const value ); void projectorPowerLevel_changed( int const percentage ); - void showExportForm(bool checked=false); protected slots: ; diff --git a/src/constants.cpp b/src/constants.cpp index a3d1a949..a0ebfd64 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -24,7 +24,10 @@ QString const ShepherdPath { "/usr/share/lig QString const SlicedSvgFileName { "sliced.svg" }; QString const StlModelLibraryPath { "/var/lib/lightfield/model-library" }; QString const UpdatesRootPath { "/var/lib/lightfield/software-updates" }; -QString const PrintProfilesPath { "print-profiles" }; +//QString const PrintProfilesPath { "/var/lib/lightfield/print-profiles.json" }; +QString const PrintProfilesPath { "/home/lumen/Volumetric/LightField/print-profiles/print-profiles.json" }; + +//QString const PrintProfilesSchemaPath { "/var/lib/lightfield/print-profiles-schema.json" }; QChar const LineFeed { L'\u000A' }; QChar const CarriageReturn { L'\u000D' }; diff --git a/src/printprofile.h b/src/printprofile.h index 3e341824..98bcbd2f 100644 --- a/src/printprofile.h +++ b/src/printprofile.h @@ -10,7 +10,7 @@ class PrintProfile: public QObject { public: PrintProfile( QObject* parent = nullptr ): QObject( parent ) { - /*empty*/ + } virtual ~PrintProfile( ) override { diff --git a/src/profilesjsonparser.h b/src/profilesjsonparser.h new file mode 100644 index 00000000..c367b747 --- /dev/null +++ b/src/profilesjsonparser.h @@ -0,0 +1,206 @@ +#ifndef PROFILESJSONPARSER_H +#define PROFILESJSONPARSER_H + +#include "printprofile.h" +#include + +#define DEBUG_LOGS + +class ProfilesJsonParser +{ +public: + static QVector* loadProfiles() { + QVector* profilesList = new QVector(); + + log( "opening file ..." ); + QFile jsonFile(PrintProfilesPath); + if(!jsonFile.exists()) + { + std::cerr << "File " << PrintProfilesPath.toStdString() << " does not exists" << std::endl; + return profilesList; + } + jsonFile.open(QIODevice::ReadOnly); + + log( "building document ..." ); + QJsonDocument jsonDocument=QJsonDocument().fromJson(jsonFile.readAll()); + + log( "get root array ..." ); + QJsonArray array = jsonDocument.array(); + + log( "iter over array ..." ); + for(int i=0; isetProfileName(obj["name"].toString()); + } + else + { + std::cerr<<"Error while parsing name field"; + throw new QException(); + } + + log( "get baseLayerCount ..." ); + auto blc = obj["baseLayerCount"]; + + if(blc.isDouble()) + printProfile->setBaseLayerCount(blc.toInt()); + else + { + std::cerr<<"Error while parsing baseLayerCount field"; + throw new QException(); + } + + log( "get baseLayersPumpingParameters ..." ); + auto baseLayersPumpingParameters = obj["baseLayersPumpingParameters"]; + + if(!baseLayersPumpingParameters.isNull()) + { + PrintPumpingParameters params = _parsePrintPumpingParameters(baseLayersPumpingParameters.toObject()); + printProfile->setBaseLayersPumpingParameters(params); + } + else + { + std::cerr<<"Error while parsing baseLayersPumpingParameters field"; + throw new QException(); + } + + log( "get bodyLayersPumpingParameters ..." ); + auto bodyLayersPumpingParameters = obj["bodyLayersPumpingParameters"]; + + if(!bodyLayersPumpingParameters.isNull()) + { + PrintPumpingParameters params = _parsePrintPumpingParameters(bodyLayersPumpingParameters.toObject()); + printProfile->setBodyLayersPumpingParameters(params); + } + else + { + std::cerr<<"Error while parsing bodyLayersPumpingParameters field"; + throw new QException(); + } + + log( "push back to list ..." ); + profilesList->push_back(printProfile); + } + catch (QException e) { } + catch (...) + { + std::cerr<<"Unknow error while parsing print profile"; + } + } + + log( "end parsing ..." ); + return profilesList; + } + + static void saveProfiles(const QVector* profiles) + { + QFile jsonFile(PrintProfilesPath); + QJsonDocument jsonDocument=QJsonDocument(); + + QJsonArray jsonArray; + + + for(int i=0; icount(); ++i) + { + auto profile = (*profiles)[i]; + QJsonObject json; + + json["name"] = profile->profileName(); + json["baseLayerCount"] = profile->baseLayerCount(); + + QJsonObject baseParameters = _serializePrintPumpingParameters(profile->baseLayersPumpingParameters()); + json["baseLayersPumpingParameters"]= baseParameters; + + QJsonObject bodyParameters = _serializePrintPumpingParameters(profile->bodyLayersPumpingParameters()); + json["bodyLayersPumpingParameters"]= bodyParameters; + + jsonArray.append(json); + } + + jsonDocument.setArray(jsonArray); + + jsonFile.open(QFile::WriteOnly); + jsonFile.write(jsonDocument.toJson()); + } + +private: + static PrintPumpingParameters _parsePrintPumpingParameters(QJsonObject obj) + { + PrintPumpingParameters params; + + params.setPumpUpDistance( _parseIntValue(obj, QString("pumpUpDistance")) ); + params.setPumpUpTime( _parseDoubleValue(obj, QString("pumpUpTime")) ); + params.setPumpUpPause( _parseDoubleValue(obj, QString("pumpUpPause")) ); + params.setPumpDownPause( _parseDoubleValue(obj, QString("pumpDownPause")) ); + params.setNoPumpUpVelocity( _parseDoubleValue(obj, QString("noPumpUpVelocity")) ); + params.setPumpEveryNthLayer( _parseDoubleValue(obj, QString("pumpEveryNthLayer")) ); + params.setLayerThickness( _parseDoubleValue(obj, QString("layerThickness")) ); + params.setLayerExposureTime( _parseDoubleValue(obj, QString("layerExposureTime")) ); + params.setPowerLevel( _parseIntValue(obj, QString("powerLevel")) ); + + return params; + } + + static int _parseIntValue(QJsonObject obj, QString propertyName) + { + QJsonValue value; + + value = obj[propertyName]; + + if(value.isDouble()) + return value.toInt(); + else + { + std::cerr<<"Error while parsing " << propertyName.toStdString() << " field"; + throw new QException(); + } + } + + static double _parseDoubleValue(QJsonObject obj, QString propertyName) + { + QJsonValue value; + + value = obj[propertyName]; + + if(value.isDouble()) + return value.toDouble(); + else + { + std::cerr<<"Error while parsing " << propertyName.toStdString() << " field"; + throw new QException(); + } + } + + static QJsonObject _serializePrintPumpingParameters(PrintPumpingParameters params) + { + QJsonObject result; + + result["pumpUpDistance"]=params.pumpUpDistance(); + result["pumpUpTime"]=params.pumpUpTime(); + result["pumpUpPause"]=params.pumpUpPause(); + result["pumpDownPause"]=params.pumpDownPause(); + result["noPumpUpVelocity"]=params.noPumpUpVelocity(); + result["pumpEveryNthLayer"]=params.pumpEveryNthLayer(); + result["layerThickness"]=params.layerThickness(); + result["layerExposureTime"]=params.layerExposureTime(); + result["powerLevel"]=params.powerLevel(); + + return result; + } + + static void log(std::string message) + { +#ifdef DEBUG_LOGS + std::cout << message << std::endl; +#endif + } +}; + +#endif // PROFILESJSONPARSER_H diff --git a/src/profilestab.cpp b/src/profilestab.cpp index c7533393..17a02b57 100644 --- a/src/profilestab.cpp +++ b/src/profilestab.cpp @@ -1,9 +1,73 @@ #include "pch.h" #include "profilestab.h" +#include "profilesjsonparser.h" +#include "window.h" + +#include +#include ProfilesTab::ProfilesTab( QWidget* parent ): TabBase( parent ) { + auto origFont = font( ); + auto boldFont = ModifyFont( origFont, QFont::Bold ); + auto fontAwesome = ModifyFont( origFont, "FontAwesome", LargeFontSize ); + _fontAwesome = new QFont(fontAwesome); + + + QGroupBox* cpyProfilesUsbBox = new QGroupBox(); + cpyProfilesUsbBox->setLayout(WrapWidgetsInVBox(_cpyProfilesUsb, nullptr)); + cpyProfilesUsbBox->setContentsMargins( { } ); + + QGroupBox* cpyStlFilesUsbBox = new QGroupBox(); + cpyStlFilesUsbBox->setLayout(WrapWidgetsInVBox(_cpyStlFilesUsb, nullptr)); + cpyStlFilesUsbBox->setContentsMargins( { } ); + + _cpyProfilesUsb->setFont(fontAwesome); + _cpyStlFilesUsb->setFont(fontAwesome); + + _importParams->setFont(fontAwesome); + _importParams->setFixedSize( MainButtonSize ); + _importParams->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + _exportParams->setFont(fontAwesome); + _exportParams->setFixedSize( MainButtonSize ); + _exportParams->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + _newProfile->setFont(fontAwesome); + _newProfile->setFixedSize( MainButtonSize ); + _newProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + _deleteProfile->setFont(fontAwesome); + _deleteProfile->setFixedSize( MainButtonSize ); + _deleteProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + _loadProfile->setFont(fontAwesome); + _loadProfile->setFixedSize( MainButtonSize ); + _loadProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + _setupProfilesList(fontAwesome); + + QObject::connect( _importParams, &QPushButton::clicked, this, &ProfilesTab::importParams_clicked ); + QObject::connect( _exportParams, &QPushButton::clicked, this, &ProfilesTab::exportParams_clicked ); + QObject::connect( _newProfile, &QPushButton::clicked, this, &ProfilesTab::newProfile_clicked ); + QObject::connect( _deleteProfile, &QPushButton::clicked, this, &ProfilesTab::deleteProfile_clicked ); + QObject::connect( _loadProfile, &QPushButton::clicked, this, &ProfilesTab::loadProfile_clicked ); + + setLayout( + WrapWidgetsInHBox( + WrapWidgetsInVBox( + _exportParams, + cpyProfilesUsbBox, + cpyStlFilesUsbBox, + _importParams, + _newProfile, + _loadProfile, + _deleteProfile, + nullptr + ), + _profilesList + ) + ); } ProfilesTab::~ProfilesTab( ) { @@ -24,3 +88,216 @@ void ProfilesTab::tab_uiStateChanged( TabIndex const sender, UiState const state break; } } + +void ProfilesTab::_setupProfilesList(QFont font) { + + QItemSelectionModel* selectionModel = new QItemSelectionModel( _model ); + _profilesList->setModel( _model ); + _profilesList->setSelectionModel( selectionModel ); + _profilesList->setFont( font ); + _profilesList->setVisible( true ); + _profilesList->setSelectionBehavior( QAbstractItemView::SelectRows ); + _profilesList->setMinimumSize(QSize(MaximalRightHandPaneSize.width(), MaximalRightHandPaneSize.height())); + _profilesList->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + +} + +void ProfilesTab::setPrintProfileManager( PrintProfileManager* printProfileManager ) { + _printProfileManager = printProfileManager; + + QStandardItem* item = nullptr; + QStandardItem* firstItem = nullptr; + QVector* profiles = ProfilesJsonParser::loadProfiles(); + + for(int i=0; icount(); ++i) + { + PrintProfile* profile = (*profiles)[i]; + _printProfileManager->addProfile(profile); + + item = new QStandardItem(profile->profileName()); + item->setEditable( false ); + if( !i ) + firstItem=item; + + _model->setItem( i, 0, item ); + } + + if( firstItem ) + _profilesList->setCurrentIndex( _model->indexFromItem(firstItem) ); + + delete profiles; +} + +void ProfilesTab::importParams_clicked(bool) +{ + Window* w = App::mainWindow(); + QRect r = w->geometry(); + + QMessageBox msgBox; + msgBox.setText("Are You sure to import all profiles from USB memory stick?"); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + msgBox.move(r.x()+100, r.y()+100); + msgBox.setFont(*_fontAwesome); + + + int ret = msgBox.exec(); + + switch (ret) { + case QMessageBox::Yes: + + // @todo how to pass checkboxes values? what filename means? + if(!_printProfileManager->importProfiles(nullptr)) + { + msgBox.setText("Something went wrong. Make sure memory stick is inserted into USB drive."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } + else + { + msgBox.setText("Import successed."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } + break; + default: + break; + } +} + +void ProfilesTab::exportParams_clicked(bool) +{ + Window* w = App::mainWindow(); + QRect r = w->geometry(); + + QMessageBox msgBox; + msgBox.setText("Are You sure to export all profiles to USB memory stick?"); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + msgBox.move(r.x()+100, r.y()+100); + msgBox.setFont(*_fontAwesome); + + int ret = msgBox.exec(); + + switch (ret) { + case QMessageBox::Yes: + + // @todo how to pass checkboxes values? what filename means? + if(!_printProfileManager->exportProfiles(nullptr)) + { + msgBox.setText("Something went wrong. Make sure memory stick is inserted into USB drive."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } + else + { + msgBox.setText("Import successed."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } + break; + default: + break; + } +} + +void ProfilesTab::newProfile_clicked(bool) +{ + Window* w = App::mainWindow(); + QRect r = w->geometry(); + + QMessageBox msgBox; + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.move(r.x()+100, r.y()+100); + msgBox.setFont(*_fontAwesome); + + QInputDialog inputDialog; + inputDialog.setModal(true); + inputDialog.move(r.x()+100, r.y()+100); + inputDialog.setFont(*_fontAwesome); + inputDialog.setFocus(Qt::FocusReason::ActiveWindowFocusReason); + inputDialog.setLabelText("Enter a profile name: "); + int ret = inputDialog.exec(); + + QString filename = inputDialog.textValue(); + if (ret && !filename.isEmpty()) + { + if(!_createNewProfile(filename)) + { + msgBox.setText("Something went wrong."); + msgBox.exec(); + } + else + { + msgBox.setText("Profile successfuly added."); + msgBox.exec(); + } + } +} + +void ProfilesTab::deleteProfile_clicked(bool) +{ + Window* w = App::mainWindow(); + QRect r = w->geometry(); + + QMessageBox msgBox; + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.move(r.x()+100, r.y()+100); + msgBox.setFont(*_fontAwesome); + msgBox.setText("Are You sure to delete selected profile?"); + int ret = msgBox.exec(); + + switch (ret) { + case QMessageBox::Yes: + if(!_deletePrintProfile()) + { + msgBox.setText("Something went wrong."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } + break; + default: + break; + } +} + +void ProfilesTab::loadProfile_clicked(bool) +{ + Window* w = App::mainWindow(); + QRect r = w->geometry(); + + QMessageBox msgBox; + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.move(r.x()+100, r.y()+100); + msgBox.setFont(*_fontAwesome); + msgBox.setText("Are You sure to load selected profile?"); + int ret = msgBox.exec(); + + switch (ret) { + case QMessageBox::Yes: + if(!_loadPrintProfile()) + { + msgBox.setText("Something went wrong."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } + break; + default: + break; + } +} + +bool ProfilesTab::_createNewProfile(QString profileName) +{ + return false; +} + +bool ProfilesTab::_deletePrintProfile() +{ + return false; +} + +bool ProfilesTab::_loadPrintProfile() +{ + return false; +} diff --git a/src/profilestab.h b/src/profilestab.h index 4ec0b402..c72118b1 100644 --- a/src/profilestab.h +++ b/src/profilestab.h @@ -15,9 +15,7 @@ class ProfilesTab: public TabBase { virtual TabIndex tabIndex( ) const override { return TabIndex::Profiles; } - void setPrintProfileManager( PrintProfileManager* printProfileManager ) { - _printProfileManager = printProfileManager; - } + void setPrintProfileManager( PrintProfileManager* printProfileManager ); protected: @@ -25,6 +23,23 @@ class ProfilesTab: public TabBase { PrintProfileManager* _printProfileManager; + + QPushButton* _importParams { new QPushButton("Import") }; + QPushButton* _exportParams { new QPushButton("Export") }; + QPushButton* _newProfile { new QPushButton("Create profile") }; + QPushButton* _deleteProfile { new QPushButton("Delete selected") }; + QPushButton* _loadProfile { new QPushButton("Load selected") }; + QCheckBox* _cpyProfilesUsb { new QCheckBox("Copy profiles to USB") }; + QCheckBox* _cpyStlFilesUsb { new QCheckBox("Copy STL files to USB") }; + QListView* _profilesList { new QListView }; + QStandardItemModel* _model { new QStandardItemModel }; + QFont* _fontAwesome; + + void _setupProfilesList(QFont font); + bool _createNewProfile(QString profileName); + bool _deletePrintProfile(); + bool _loadPrintProfile(); + signals: ; @@ -39,6 +54,12 @@ protected slots: private slots: ; + void importParams_clicked(bool); + void exportParams_clicked(bool); + void newProfile_clicked(bool); + void deleteProfile_clicked(bool); + void loadProfile_clicked(bool); + }; #endif //!__PROFILESTAB_H__ From 28561661b79d380cf4de70f7dc51eb346664a60d Mon Sep 17 00:00:00 2001 From: sagittarius89 Date: Thu, 30 Jan 2020 17:25:15 +0100 Subject: [PATCH 72/89] added json example file with configuration and schema. Need to add print-profiles.json to installation scripts to put in in proper place --- print-profiles/print-profiles-schema.json | 244 ++++++++++++++++++++++ print-profiles/print-profiles.json | 28 +++ 2 files changed, 272 insertions(+) create mode 100644 print-profiles/print-profiles-schema.json create mode 100644 print-profiles/print-profiles.json diff --git a/print-profiles/print-profiles-schema.json b/print-profiles/print-profiles-schema.json new file mode 100644 index 00000000..3bb84840 --- /dev/null +++ b/print-profiles/print-profiles-schema.json @@ -0,0 +1,244 @@ + 1 { + 2 "definitions": {}, + 3 "$schema": "", + 4 "$id": "http://example.com/root.json", + 5 "type": "array", + 6 "title": "Print profiles", + 7 "description": "List of print profiles", + 8 "default": null, + 9 "readOnly": false, + 10 "items": { + 11 "$id": "#/items", + 12 "type": "object", + 13 "title": "PrintProfile", + 14 "description": "Single print profile", + 15 "default": null, + 16 "readOnly": false, + 17 "additionalProperties": false, + 18 "required": [ + 19 "name", + 20 "baseLayerCount", + 21 "baseLayersPumpingParameters", + 22 "bodyLayersPumpingParameters" + 23 ], + 24 "properties": { + 25 "name": { + 26 "$id": "#/items/properties/name", + 27 "type": "string", + 28 "title": "The Name Schema", + 29 "default": "", + 30 "examples": [ + 31 "exampleProfile" + 32 ], + 33 "pattern": "^(.*)$" + 34 }, + 35 "baseLayerCount": { + 36 "$id": "#/items/properties/baseLayerCount", + 37 "type": "integer", + 38 "title": "The Baselayercount Schema", + 39 "default": 0, + 40 "examples": [ + 41 1 + 42 ] + 43 }, + 44 "baseLayersPumpingParameters": { + 45 "$id": "#/items/properties/baseLayersPumpingParameters", + 46 "type": "object", + 47 "title": "The Baselayerspumpingparameters Schema", + 48 "required": [ + 49 "pumpUpDistance", + 50 "pumpUpTime", + 51 "pumpUpPause", + 52 "pumpDownPause", + 53 "noPumpUpVelocity", + 54 "pumpEveryNthLayer", + 55 "layerThickness", + 56 "layerExposureTime", + 57 "powerLevel" + 58 ], + 59 "properties": { + 60 "pumpUpDistance": { + 61 "$id": "#/items/properties/baseLayersPumpingParameters/properties/pumpUpDistance", + 62 "type": "number", + 63 "title": "The Pumpupdistance Schema", + 64 "default": 0, + 65 "examples": [ + 66 2 + 67 ] + 68 }, + 69 "pumpUpTime": { + 70 "$id": "#/items/properties/baseLayersPumpingParameters/properties/pumpUpTime", + 71 "type": "integer", + 72 "title": "The Pumpuptime Schema", + 73 "default": 0, + 74 "examples": [ + 75 600 + 76 ] + 77 }, + 78 "pumpUpPause": { + 79 "$id": "#/items/properties/baseLayersPumpingParameters/properties/pumpUpPause", + 80 "type": "integer", + 81 "title": "The Pumpuppause Schema", + 82 "default": 0, + 83 "examples": [ + 84 2000 + 85 ] + 86 }, + 87 "pumpDownPause": { + 88 "$id": "#/items/properties/baseLayersPumpingParameters/properties/pumpDownPause", + 89 "type": "integer", + 90 "title": "The Pumpdownpause Schema", + 91 "default": 0, + 92 "examples": [ + 93 4000 + 94 ] + 95 }, + 96 "noPumpUpVelocity": { + 97 "$id": "#/items/properties/baseLayersPumpingParameters/properties/noPumpUpVelocity", + 98 "type": "integer", + 99 "title": "The Nopumpupvelocity Schema", +100 "default": 0, +101 "examples": [ +102 200 +103 ] +104 }, +105 "pumpEveryNthLayer": { +106 "$id": "#/items/properties/baseLayersPumpingParameters/properties/pumpEveryNthLayer", +107 "type": "integer", +108 "title": "The Pumpeverynthlayer Schema", +109 "default": 0, +110 "examples": [ +111 1 +112 ] +113 }, +114 "layerThickness": { +115 "$id": "#/items/properties/baseLayersPumpingParameters/properties/layerThickness", +116 "type": "integer", +117 "title": "The Layerthickness Schema", +118 "default": 0, +119 "examples": [ +120 100 +121 ] +122 }, +123 "layerExposureTime": { +124 "$id": "#/items/properties/baseLayersPumpingParameters/properties/layerExposureTime", +125 "type": "integer", +126 "title": "The Layerexposuretime Schema", +127 "default": 0, +128 "examples": [ +129 1000 +130 ] +131 }, +132 "powerLevel": { +133 "$id": "#/items/properties/baseLayersPumpingParameters/properties/powerLevel", +134 "type": "number", +135 "title": "The Powerlevel Schema", +136 "default": 0, +137 "examples": [ +138 50 +139 ] +140 } +141 } +142 }, +143 "bodyLayersPumpingParameters": { +144 "$id": "#/items/properties/bodyLayersPumpingParameters", +145 "type": "object", +146 "title": "The Bodylayerspumpingparameters Schema", +147 "required": [ +148 "pumpUpDistance", +149 "pumpUpTime", +150 "pumpUpPause", +151 "pumpDownPause", +152 "noPumpUpVelocity", +153 "pumpEveryNthLayer", +154 "layerThickness", +155 "layerExposureTime", +156 "powerLevel" +157 ], +158 "properties": { +159 "pumpUpDistance": { +160 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/pumpUpDistance", +161 "type": "number", +162 "title": "The Pumpupdistance Schema", +163 "default": 0, +164 "examples": [ +165 2 +166 ] +167 }, +168 "pumpUpTime": { +169 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/pumpUpTime", +170 "type": "integer", +171 "title": "The Pumpuptime Schema", +172 "default": 0, +173 "examples": [ +174 600 +175 ] +176 }, +177 "pumpUpPause": { +178 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/pumpUpPause", +179 "type": "integer", +180 "title": "The Pumpuppause Schema", +181 "default": 0, +182 "examples": [ +183 2000 +184 ] +185 }, +186 "pumpDownPause": { +187 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/pumpDownPause", +188 "type": "integer", +189 "title": "The Pumpdownpause Schema", +190 "default": 0, +191 "examples": [ +192 4000 +193 ] +194 }, +195 "noPumpUpVelocity": { +196 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/noPumpUpVelocity", +197 "type": "integer", +198 "title": "The Nopumpupvelocity Schema", +199 "default": 0, +200 "examples": [ +201 200 +202 ] +203 }, +204 "pumpEveryNthLayer": { +205 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/pumpEveryNthLayer", +206 "type": "integer", +207 "title": "The Pumpeverynthlayer Schema", +208 "default": 0, +209 "examples": [ +210 1 +211 ] +212 }, +213 "layerThickness": { +214 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/layerThickness", +215 "type": "integer", +216 "title": "The Layerthickness Schema", +217 "default": 0, +218 "examples": [ +219 100 +220 ] +221 }, +222 "layerExposureTime": { +223 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/layerExposureTime", +224 "type": "integer", +225 "title": "The Layerexposuretime Schema", +226 "default": 0, +227 "examples": [ +228 1000 +229 ] +230 }, +231 "powerLevel": { +232 "$id": "#/items/properties/bodyLayersPumpingParameters/properties/powerLevel", +233 "type": "number", +234 "title": "The Powerlevel Schema", +235 "default": 0, +236 "examples": [ +237 50 +238 ] +239 } +240 } +241 } +242 } +243 } +244 } diff --git a/print-profiles/print-profiles.json b/print-profiles/print-profiles.json new file mode 100644 index 00000000..814ab89d --- /dev/null +++ b/print-profiles/print-profiles.json @@ -0,0 +1,28 @@ +[ + { + "name": "exampleProfile", + "baseLayerCount": 1, + "baseLayersPumpingParameters": { + "pumpUpDistance": 2.00, + "pumpUpTime": 600, + "pumpUpPause": 2000, + "pumpDownPause": 4000, + "noPumpUpVelocity": 200, + "pumpEveryNthLayer": 1, + "layerThickness": 100, + "layerExposureTime": 1000, + "powerLevel": 50.0 + }, + "bodyLayersPumpingParameters": { + "pumpUpDistance": 2.00, + "pumpUpTime": 600, + "pumpUpPause": 2000, + "pumpDownPause": 4000, + "noPumpUpVelocity": 200, + "pumpEveryNthLayer": 1, + "layerThickness": 100, + "layerExposureTime": 1000, + "powerLevel": 50.0 + } + } +] From c04d4a39909b7be41b956c2f9caeda5109b29330 Mon Sep 17 00:00:00 2001 From: sagittarius89 Date: Fri, 31 Jan 2020 00:13:10 +0100 Subject: [PATCH 73/89] - implementation of list operations --- src/advancedtab.cpp | 132 ++++++++++++++++++++++++++++++++++++ src/advancedtab.h | 47 +++++++------ src/paramslider.cpp | 9 ++- src/paramslider.h | 3 + src/printprofile.h | 31 +++++++++ src/printprofilemanager.cpp | 1 - src/profilesjsonparser.h | 47 +++++++------ src/profilestab.cpp | 31 +++++++-- src/profilestab.h | 1 - src/window.cpp | 2 + 10 files changed, 255 insertions(+), 49 deletions(-) diff --git a/src/advancedtab.cpp b/src/advancedtab.cpp index fa6161a8..39be4888 100644 --- a/src/advancedtab.cpp +++ b/src/advancedtab.cpp @@ -29,6 +29,25 @@ AdvancedTab::AdvancedTab( QWidget* parent ): TabBase( parent ) { _forms[4] = _bodyLayersForm; _forms[5] = _bodyPumpForm; + QWidget::connect(_distanceSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_upTimeSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_upPauseSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_downTimeSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_downPauseSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_upVelocitySlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_numberOfBaseLayersSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_baseThicknessSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_baseExposureTimeSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyThicknessSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyExposureTimeSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyPumpEveryNthLayer, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyDistanceSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyUpTimeSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyUpPauseSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyDownTimeSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyDownPauseSlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + QWidget::connect(_bodyUpVelocitySlider, &ParamSlider::valuechanged, this, &AdvancedTab::updatePrintProfile); + //menu this->_setupLeftMenu(fontAwesome); @@ -590,3 +609,116 @@ void AdvancedTab::_setupBodyPumpForm(QFont boldFont) _bodyPumpForm->setWidget(container); } + +void AdvancedTab::updatePrintProfile() { + + if(_lockUpdate) + return; + + PrintProfile* profile = (PrintProfile*)_printProfileManager->activeProfile(); + QString tempProfileName = "temp"; + bool setActive = false; + if(profile->profileName() != tempProfileName) + { + QVector* c = (QVector*)_printProfileManager->profiles(); + auto iter = std::find_if( c->begin( ), c->end( ), [&tempProfileName] ( PrintProfile* p ) { return tempProfileName == p->profileName( ); } ); + profile = ( iter != c->end( ) ) ? *iter : nullptr; + + if(!profile) + { + profile = new PrintProfile(); + profile->setProfileName(tempProfileName); + _printProfileManager->addProfile(profile); + } + + setActive=true; + } + + + profile->setBaseLayerCount(_numberOfBaseLayersSlider->getValue()); + + profile->setAddBaseLayersPumpingParameters(_addBasePumpCheckbox->isChecked()); + if(_addBasePumpCheckbox->isChecked()) + { + PrintPumpingParameters baseParams; + + baseParams.setPumpUpDistance(_distanceSlider->getValue()); + baseParams.setPumpUpTime(_upTimeSlider->getValue()); + baseParams.setPumpUpPause(_upPauseSlider->getValue()); + baseParams.setPumpDownPause(_downPauseSlider->getValue()); + baseParams.setNoPumpUpVelocity(_upVelocitySlider->getValue()); + baseParams.setPumpEveryNthLayer(0); + baseParams.setLayerThickness(_baseThicknessSlider->getValue()); + baseParams.setLayerExposureTime(_baseExposureTimeSlider->getValue()); + baseParams.setPowerLevel(_powerLevelSlider->value()); + + profile->setBaseLayersPumpingParameters(baseParams); + } + + profile->setAddBaseLayersPumpingParameters(_addBodyPumpCheckbox->isChecked()); + if(_addBodyPumpCheckbox->isChecked()) + { + PrintPumpingParameters bodyParams; + + bodyParams.setPumpUpDistance(_bodyDistanceSlider->getValue()); + bodyParams.setPumpUpTime(_bodyUpTimeSlider->getValue()); + bodyParams.setPumpUpPause(_bodyUpPauseSlider->getValue()); + bodyParams.setPumpDownPause(_bodyDownPauseSlider->getValue()); + bodyParams.setNoPumpUpVelocity(_bodyUpVelocitySlider->getValue()); + bodyParams.setPumpEveryNthLayer(_bodyPumpEveryNthLayer->getValue()); + bodyParams.setLayerThickness(_bodyThicknessSlider->getValue()); + bodyParams.setLayerExposureTime(_bodyExposureTimeSlider->getValue()); + bodyParams.setPowerLevel(_powerLevelSlider->value()); + + profile->setBodyLayersPumpingParameters(bodyParams); + } + + if(setActive) + { + std::cout<<"!!!!!!!!!!!!!!!!!!!!!! setActiveProfile: " << std::endl; + _printProfileManager->setActiveProfile(tempProfileName); + } +} + +void AdvancedTab::loadPrintProfile(PrintProfile const* profilePtr) { + PrintProfile* profile = (PrintProfile*)profilePtr; + _lockUpdate=true; + + _numberOfBaseLayersSlider->setValue(profile->baseLayerCount()); + + if(profile->whetherAddBaseLayersPumpingParameters()) + { + PrintPumpingParameters baseParams = profile->baseLayersPumpingParameters(); + + _distanceSlider->setValue(baseParams.pumpUpDistance() * 1000 ); + _upTimeSlider->setValue(baseParams.pumpUpTime()); + _upPauseSlider->setValue(baseParams.pumpUpPause()); + _downPauseSlider->setValue(baseParams.pumpDownPause()); + _upVelocitySlider->setValue(baseParams.noPumpUpVelocity() * (1000/60) ); + _baseThicknessSlider->setValue(baseParams.layerThickness()); + _baseExposureTimeSlider->setValue(baseParams.layerExposureTime()); + _powerLevelSlider->setValue(baseParams.powerLevel()); + } + + if(profile->whetherAddBodyLayersPumpingParameters()) + { + PrintPumpingParameters bodyParams = profile->baseLayersPumpingParameters(); + + _bodyDistanceSlider->setValue(bodyParams.pumpUpDistance() * 1000 ); + _bodyUpTimeSlider->setValue(bodyParams.pumpUpTime()); + _bodyUpPauseSlider->setValue(bodyParams.pumpUpPause()); + _bodyDownPauseSlider->setValue(bodyParams.pumpDownPause()); + _bodyUpVelocitySlider->setValue(bodyParams.noPumpUpVelocity() * (1000/60) ); + _bodyThicknessSlider->setValue(bodyParams.layerThickness()); + _bodyExposureTimeSlider->setValue(bodyParams.layerExposureTime()); + _powerLevelSlider->setValue(bodyParams.powerLevel()); + _bodyPumpEveryNthLayer->setValue(bodyParams.pumpEveryNthLayer()); + } + + _lockUpdate=false; +} + +void AdvancedTab::setPrintProfileManager(PrintProfileManager* profileManager) +{ + _printProfileManager = profileManager; +} diff --git a/src/advancedtab.h b/src/advancedtab.h index 85b8347a..fb06a546 100644 --- a/src/advancedtab.h +++ b/src/advancedtab.h @@ -5,7 +5,8 @@ #include "tabbase.h" #include "paramslider.h" - +#include "printprofile.h" +#include "printprofilemanager.h" class PngDisplayer; class AdvancedTab: public TabBase { @@ -17,14 +18,14 @@ class AdvancedTab: public TabBase { AdvancedTab( QWidget* parent = nullptr ); virtual ~AdvancedTab( ) override; - virtual TabIndex tabIndex( ) const override { return TabIndex::Advanced; } - + virtual TabIndex tabIndex( ) const override { return TabIndex::Advanced; } + void setPrintProfileManager(PrintProfileManager* profileManager); protected: virtual void _connectShepherd( ) override; private: - + PrintProfileManager* _printProfileManager; QLabel* _offsetLabel { new QLabel }; QLabel* _offsetValue { new QLabel }; QSlider* _offsetSlider { new QSlider }; @@ -82,38 +83,38 @@ class AdvancedTab: public TabBase { QScrollArea* _basePumpForm { new QScrollArea }; QCheckBox* _addBasePumpCheckbox { new QCheckBox("add base pump") }; - ParamSlider* _distanceSlider { new ParamSlider("Base Pump Distance", "µm", 1000, 2000, 1) }; - ParamSlider* _upTimeSlider { new ParamSlider("Base Pump Up Time", "ms", 1000, 2000, 1) }; - ParamSlider* _upPauseSlider { new ParamSlider("Base Pump Up Pause", "ms", 1000, 2000, 1) }; - ParamSlider* _downTimeSlider { new ParamSlider("Base Pump Down Time", "ms", 1000, 2000, 1) }; - ParamSlider* _downPauseSlider { new ParamSlider("Base Pump Down Pause", "ms", 1000, 2000, 1) }; - ParamSlider* _upVelocitySlider { new ParamSlider("Base Pump Up Velocity", "µm/ms", 1000, 2000, 1) }; + ParamSlider* _distanceSlider { new ParamSlider("Base Pump Distance", "µm", 1000, 8000, 1) }; + ParamSlider* _upTimeSlider { new ParamSlider("Base Pump Up Time", "ms", 1000, 8000, 1) }; + ParamSlider* _upPauseSlider { new ParamSlider("Base Pump Up Pause", "ms", 1000, 8000, 1) }; + ParamSlider* _downTimeSlider { new ParamSlider("Base Pump Down Time", "ms", 1000, 8000, 1) }; + ParamSlider* _downPauseSlider { new ParamSlider("Base Pump Down Pause", "ms", 1000, 8000, 1) }; + ParamSlider* _upVelocitySlider { new ParamSlider("Base Pump Up Velocity", "µm/ms", 1000, 8000, 1) }; //Base Layer Form QWidget* _baseLayerForm { new QWidget }; ParamSlider* _numberOfBaseLayersSlider { new ParamSlider("Number of Base Layer", "", 1, 20, 1) }; - ParamSlider* _baseThicknessSlider { new ParamSlider("Base Layer Thickness", "µm", 100, 1000, 1) }; - ParamSlider* _baseExposureTimeSlider { new ParamSlider("Base Pump Up Pause", "ms", 2000, 10000, 1) }; + ParamSlider* _baseThicknessSlider { new ParamSlider("Base Layer Thickness", "µm", 100, 8000, 1) }; + ParamSlider* _baseExposureTimeSlider { new ParamSlider("Base Pump Up Pause", "ms", 2000, 8000, 1) }; //Body Layers Form QWidget* _bodyLayersForm { new QWidget }; - ParamSlider* _bodyThicknessSlider { new ParamSlider("Body Layer Thickness", "µm", 20, 1000, 1) }; - ParamSlider* _bodyExposureTimeSlider { new ParamSlider("Base Pump Up Pause", "ms", 2000, 10000, 1) }; + ParamSlider* _bodyThicknessSlider { new ParamSlider("Body Layer Thickness", "µm", 20, 8000, 1) }; + ParamSlider* _bodyExposureTimeSlider { new ParamSlider("Base Pump Up Pause", "ms", 2000, 8000, 1) }; //Body Pump Form QScrollArea* _bodyPumpForm { new QScrollArea }; QCheckBox* _addBodyPumpCheckbox { new QCheckBox("add body pump") }; - ParamSlider* _bodyPumpEveryNthLayer { new ParamSlider("Body Pump Distance", "µm", 5, 40, 1) }; - ParamSlider* _bodyDistanceSlider { new ParamSlider("Body Pump Distance", "µm", 1000, 2000, 1) }; - ParamSlider* _bodyUpTimeSlider { new ParamSlider("Body Pump Up Time", "ms", 1000, 2000, 1) }; - ParamSlider* _bodyUpPauseSlider { new ParamSlider("Body Pump Up Pause", "ms", 1000, 2000, 1) }; - ParamSlider* _bodyDownTimeSlider { new ParamSlider("Body Pump Down Time", "ms", 1000, 2000, 1) }; - ParamSlider* _bodyDownPauseSlider { new ParamSlider("Body Pump Down Pause", "ms", 1000, 2000, 1) }; - ParamSlider* _bodyUpVelocitySlider { new ParamSlider("Body Pump Up Velocity", "µm/ms", 1000, 2000, 1) }; + ParamSlider* _bodyPumpEveryNthLayer { new ParamSlider("Body Pump Every Nth Layer:", "", 5, 20, 1) }; + ParamSlider* _bodyDistanceSlider { new ParamSlider("Body Pump Distance", "µm", 1000, 8000, 1) }; + ParamSlider* _bodyUpTimeSlider { new ParamSlider("Body Pump Up Time", "ms", 1000, 8000, 1) }; + ParamSlider* _bodyUpPauseSlider { new ParamSlider("Body Pump Up Pause", "ms", 1000, 8000, 1) }; + ParamSlider* _bodyDownTimeSlider { new ParamSlider("Body Pump Down Time", "ms", 1000, 8000, 1) }; + ParamSlider* _bodyDownPauseSlider { new ParamSlider("Body Pump Down Pause", "ms", 1000, 8000, 1) }; + ParamSlider* _bodyUpVelocitySlider { new ParamSlider("Body Pump Up Velocity", "µm/ms", 1000, 8000, 1) }; static const int FORMS_COUNT { 6 }; @@ -124,6 +125,7 @@ class AdvancedTab: public TabBase { bool _isPrinterOnline { false }; bool _isPrinterAvailable { true }; bool _isProjectorOn { false }; + bool _lockUpdate { false }; void _updateControlGroups( ); void _projectImage( char const* fileName ); @@ -149,7 +151,8 @@ public slots: void setPrinterAvailable( bool const value ); void projectorPowerLevel_changed( int const percentage ); - + void loadPrintProfile (PrintProfile const* profile); + void updatePrintProfile(); protected slots: ; diff --git a/src/paramslider.cpp b/src/paramslider.cpp index 11d9bc91..97495539 100644 --- a/src/paramslider.cpp +++ b/src/paramslider.cpp @@ -1,5 +1,6 @@ #include "paramslider.h" + void ParamSlider::init(QString name, QString unit, int startValue, int maxValue, int step) { QString valueLabel = QString::number(startValue) + QString(" ") + unit; @@ -31,9 +32,15 @@ int ParamSlider::getValue() return _slider->value(); } -void ParamSlider::onvaluechanged(int value) +void ParamSlider::setValue(int value) +{ + _slider->setValue(value); +} + +void ParamSlider::onvaluechanged(int) { this->_valueLabel->setText(QString::number(_slider->value()) + " " + QString(_unit)); + emit valuechanged(); } ParamSlider::ParamSlider(QString name, QString unit, int startValue, int maxValue, int step) diff --git a/src/paramslider.h b/src/paramslider.h index 2d778283..981369b4 100644 --- a/src/paramslider.h +++ b/src/paramslider.h @@ -19,7 +19,10 @@ class ParamSlider: public QGroupBox { ~ParamSlider(); int getValue(); + void setValue(int value); + signals: + void valuechanged(); public slots: void onvaluechanged(int value); }; diff --git a/src/printprofile.h b/src/printprofile.h index 98bcbd2f..4a3a8269 100644 --- a/src/printprofile.h +++ b/src/printprofile.h @@ -17,6 +17,20 @@ class PrintProfile: public QObject { /*empty*/ } + PrintProfile* copy() { + PrintProfile* printProfile = new PrintProfile(); + + printProfile->_name = this->_name; + printProfile->_baseLayerCount = this->_baseLayerCount; + printProfile->_addBaseLayersPumpingParameters = this->_addBaseLayersPumpingParameters; + printProfile->_addBodyLayersPumpingParameters = this->_addBodyLayersPumpingParameters; + + printProfile->_baseLayersPumpingParameters = this->_baseLayersPumpingParameters; + printProfile->_bodyLayersPumpingParameters = this->_bodyLayersPumpingParameters; + + return printProfile; + } + // // Accessors // @@ -37,6 +51,14 @@ class PrintProfile: public QObject { return _bodyLayersPumpingParameters; } + bool whetherAddBaseLayersPumpingParameters() { + return _addBaseLayersPumpingParameters; + } + + bool whetherAddBodyLayersPumpingParameters() { + return _addBodyLayersPumpingParameters; + } + // // Mutators // @@ -58,13 +80,22 @@ class PrintProfile: public QObject { _bodyLayersPumpingParameters = newParameters; } + void setAddBaseLayersPumpingParameters(bool value) { + _addBaseLayersPumpingParameters = value; + } + + void setAddBodyLayersPumpingParameters(bool value) { + _addBodyLayersPumpingParameters = value; + } protected: private: QString _name; int _baseLayerCount; + bool _addBaseLayersPumpingParameters; PrintPumpingParameters _baseLayersPumpingParameters; + bool _addBodyLayersPumpingParameters; PrintPumpingParameters _bodyLayersPumpingParameters; signals: diff --git a/src/printprofilemanager.cpp b/src/printprofilemanager.cpp index ff281d89..2e13f2eb 100644 --- a/src/printprofilemanager.cpp +++ b/src/printprofilemanager.cpp @@ -1,7 +1,6 @@ #include "pch.h" #include "printprofilemanager.h" - PrintProfile* PrintProfileManager::_findProfile( QString const& profileName ) { auto iter = std::find_if( _profiles->begin( ), _profiles->end( ), [&profileName] ( PrintProfile* p ) { return profileName == p->profileName( ); } ); return ( iter != _profiles->end( ) ) ? *iter : nullptr; diff --git a/src/profilesjsonparser.h b/src/profilesjsonparser.h index c367b747..2d85cf70 100644 --- a/src/profilesjsonparser.h +++ b/src/profilesjsonparser.h @@ -27,14 +27,14 @@ class ProfilesJsonParser log( "get root array ..." ); QJsonArray array = jsonDocument.array(); - log( "iter over array ..." ); + log( "iter over array ... " + std::to_string(array.count()) ); for(int i=0; isetBaseLayersPumpingParameters(params); + printProfile->setAddBaseLayersPumpingParameters(true); } else - { - std::cerr<<"Error while parsing baseLayersPumpingParameters field"; - throw new QException(); - } + printProfile->setAddBaseLayersPumpingParameters(false); log( "get bodyLayersPumpingParameters ..." ); auto bodyLayersPumpingParameters = obj["bodyLayersPumpingParameters"]; - if(!bodyLayersPumpingParameters.isNull()) + if(!bodyLayersPumpingParameters.isUndefined() && !bodyLayersPumpingParameters.isNull()) { PrintPumpingParameters params = _parsePrintPumpingParameters(bodyLayersPumpingParameters.toObject()); printProfile->setBodyLayersPumpingParameters(params); + printProfile->setAddBodyLayersPumpingParameters(true); } else - { - std::cerr<<"Error while parsing bodyLayersPumpingParameters field"; - throw new QException(); - } + printProfile->setAddBodyLayersPumpingParameters(false); - log( "push back to list ..." ); + log( "push back to list ... " + std::to_string(array.count()) + " " + std::to_string(i) ); profilesList->push_back(printProfile); } catch (QException e) { } @@ -101,25 +97,38 @@ class ProfilesJsonParser static void saveProfiles(const QVector* profiles) { + log( "saveProfiles ..." ); QFile jsonFile(PrintProfilesPath); + QJsonDocument jsonDocument=QJsonDocument(); QJsonArray jsonArray; - + log( "loop start ..." ); for(int i=0; icount(); ++i) { auto profile = (*profiles)[i]; + + log( "serializing profile " + profile->profileName().toStdString() ); + if(profile->profileName() == "temp") + continue; + QJsonObject json; json["name"] = profile->profileName(); json["baseLayerCount"] = profile->baseLayerCount(); - QJsonObject baseParameters = _serializePrintPumpingParameters(profile->baseLayersPumpingParameters()); - json["baseLayersPumpingParameters"]= baseParameters; + if(profile->whetherAddBaseLayersPumpingParameters()) + { + QJsonObject baseParameters = _serializePrintPumpingParameters(profile->baseLayersPumpingParameters()); + json["baseLayersPumpingParameters"]= baseParameters; + } - QJsonObject bodyParameters = _serializePrintPumpingParameters(profile->bodyLayersPumpingParameters()); - json["bodyLayersPumpingParameters"]= bodyParameters; + if(profile->whetherAddBodyLayersPumpingParameters()) + { + QJsonObject bodyParameters = _serializePrintPumpingParameters(profile->bodyLayersPumpingParameters()); + json["bodyLayersPumpingParameters"]= bodyParameters; + } jsonArray.append(json); } diff --git a/src/profilestab.cpp b/src/profilestab.cpp index 17a02b57..3f6eebb6 100644 --- a/src/profilestab.cpp +++ b/src/profilestab.cpp @@ -108,7 +108,6 @@ void ProfilesTab::setPrintProfileManager( PrintProfileManager* printProfileManag QStandardItem* item = nullptr; QStandardItem* firstItem = nullptr; QVector* profiles = ProfilesJsonParser::loadProfiles(); - for(int i=0; icount(); ++i) { PrintProfile* profile = (*profiles)[i]; @@ -116,8 +115,10 @@ void ProfilesTab::setPrintProfileManager( PrintProfileManager* printProfileManag item = new QStandardItem(profile->profileName()); item->setEditable( false ); - if( !i ) + if( !i ) { firstItem=item; + _printProfileManager->setActiveProfile(profile->profileName()); + } _model->setItem( i, 0, item ); } @@ -289,15 +290,35 @@ void ProfilesTab::loadProfile_clicked(bool) bool ProfilesTab::_createNewProfile(QString profileName) { - return false; + PrintProfile* printProfile = (PrintProfile*)_printProfileManager->activeProfile(); + PrintProfile* profileCopy = printProfile->copy(); + + profileCopy->setProfileName(profileName); + _printProfileManager->addProfile(profileCopy); + + QStandardItem* item = new QStandardItem(profileName); + item->setEditable(false); + + _model->appendRow(item); + + ProfilesJsonParser::saveProfiles(_printProfileManager->profiles()); + + return true; } bool ProfilesTab::_deletePrintProfile() { - return false; + QModelIndex index = _profilesList->currentIndex(); + QString itemText = index.data(Qt::DisplayRole).toString(); + _printProfileManager->removeProfile(itemText); + + _model->removeRows(index.row(), 1); + ProfilesJsonParser::saveProfiles(_printProfileManager->profiles()); } bool ProfilesTab::_loadPrintProfile() { - return false; + QModelIndex index = _profilesList->currentIndex(); + QString itemText = index.data(Qt::DisplayRole).toString(); + return _printProfileManager->setActiveProfile(itemText); } diff --git a/src/profilestab.h b/src/profilestab.h index c72118b1..7a37bd6b 100644 --- a/src/profilestab.h +++ b/src/profilestab.h @@ -39,7 +39,6 @@ class ProfilesTab: public TabBase { bool _createNewProfile(QString profileName); bool _deletePrintProfile(); bool _loadPrintProfile(); - signals: ; diff --git a/src/window.cpp b/src/window.cpp index d849300c..47ade5ea 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -158,7 +158,9 @@ Window::Window( QWidget* parent ): QMainWindow( parent ) { QObject::connect( _advancedTab, &AdvancedTab::projectorPowerLevelChanged, _printTab, &PrintTab::projectorPowerLevel_changed ); QObject::connect( _advancedTab, &AdvancedTab::printerAvailabilityChanged, _statusTab, &StatusTab::setPrinterAvailable ); QObject::connect( _advancedTab, &AdvancedTab::printerAvailabilityChanged, _systemTab, &SystemTab::setPrinterAvailable ); + QObject::connect( _printProfileManager, &PrintProfileManager::activeProfileChanged, _advancedTab, &AdvancedTab::loadPrintProfile ); + _advancedTab->setPrintProfileManager( _printProfileManager ); // // "Profiles" tab // From ddbdcc58f4e7922f7deefbb07c5d99e661cb2e6a Mon Sep 17 00:00:00 2001 From: sagittarius89 Date: Fri, 31 Jan 2020 11:38:16 +0100 Subject: [PATCH 74/89] hotfix (unit conversion) --- src/advancedtab.cpp | 9 ++++----- src/advancedtab.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/advancedtab.cpp b/src/advancedtab.cpp index 39be4888..d82e4a72 100644 --- a/src/advancedtab.cpp +++ b/src/advancedtab.cpp @@ -642,11 +642,11 @@ void AdvancedTab::updatePrintProfile() { { PrintPumpingParameters baseParams; - baseParams.setPumpUpDistance(_distanceSlider->getValue()); + baseParams.setPumpUpDistance( ((double)_distanceSlider->getValue()) / 1000 ); baseParams.setPumpUpTime(_upTimeSlider->getValue()); baseParams.setPumpUpPause(_upPauseSlider->getValue()); baseParams.setPumpDownPause(_downPauseSlider->getValue()); - baseParams.setNoPumpUpVelocity(_upVelocitySlider->getValue()); + baseParams.setNoPumpUpVelocity( ((double)_upVelocitySlider->getValue()) / (1000/60)); baseParams.setPumpEveryNthLayer(0); baseParams.setLayerThickness(_baseThicknessSlider->getValue()); baseParams.setLayerExposureTime(_baseExposureTimeSlider->getValue()); @@ -660,11 +660,11 @@ void AdvancedTab::updatePrintProfile() { { PrintPumpingParameters bodyParams; - bodyParams.setPumpUpDistance(_bodyDistanceSlider->getValue()); + bodyParams.setPumpUpDistance( ((double)_bodyDistanceSlider->getValue()) / 1000 ); bodyParams.setPumpUpTime(_bodyUpTimeSlider->getValue()); bodyParams.setPumpUpPause(_bodyUpPauseSlider->getValue()); bodyParams.setPumpDownPause(_bodyDownPauseSlider->getValue()); - bodyParams.setNoPumpUpVelocity(_bodyUpVelocitySlider->getValue()); + bodyParams.setNoPumpUpVelocity( ((double)_bodyUpVelocitySlider->getValue()) / (1000/60)); bodyParams.setPumpEveryNthLayer(_bodyPumpEveryNthLayer->getValue()); bodyParams.setLayerThickness(_bodyThicknessSlider->getValue()); bodyParams.setLayerExposureTime(_bodyExposureTimeSlider->getValue()); @@ -675,7 +675,6 @@ void AdvancedTab::updatePrintProfile() { if(setActive) { - std::cout<<"!!!!!!!!!!!!!!!!!!!!!! setActiveProfile: " << std::endl; _printProfileManager->setActiveProfile(tempProfileName); } } diff --git a/src/advancedtab.h b/src/advancedtab.h index fb06a546..daad5eb2 100644 --- a/src/advancedtab.h +++ b/src/advancedtab.h @@ -83,7 +83,7 @@ class AdvancedTab: public TabBase { QScrollArea* _basePumpForm { new QScrollArea }; QCheckBox* _addBasePumpCheckbox { new QCheckBox("add base pump") }; - ParamSlider* _distanceSlider { new ParamSlider("Base Pump Distance", "µm", 1000, 8000, 1) }; + ParamSlider* _distanceSlider { new ParamSlider("Base Pump Distance", "µm", 1000, 8000, 250) }; ParamSlider* _upTimeSlider { new ParamSlider("Base Pump Up Time", "ms", 1000, 8000, 1) }; ParamSlider* _upPauseSlider { new ParamSlider("Base Pump Up Pause", "ms", 1000, 8000, 1) }; ParamSlider* _downTimeSlider { new ParamSlider("Base Pump Down Time", "ms", 1000, 8000, 1) }; From ea2935be18963b1071422d38810c81068af610cd Mon Sep 17 00:00:00 2001 From: sagittarius89 Date: Fri, 31 Jan 2020 16:09:55 +0100 Subject: [PATCH 75/89] added custom virtual keyboard --- qt/lf.pro | 5 + src/advancedtabselectionmodel.cpp | 8 -- src/app.cpp | 2 +- src/inputdialog.h | 104 ++++++++++++++ src/key.cpp | 72 ++++++++++ src/key.h | 40 ++++++ src/keyboard.cpp | 226 ++++++++++++++++++++++++++++++ src/keyboard.h | 40 ++++++ src/main.cpp | 3 +- src/paramslider.h | 1 - src/profilestab.cpp | 10 +- 11 files changed, 496 insertions(+), 15 deletions(-) create mode 100644 src/inputdialog.h create mode 100644 src/key.cpp create mode 100644 src/key.h create mode 100644 src/keyboard.cpp create mode 100644 src/keyboard.h diff --git a/qt/lf.pro b/qt/lf.pro index 86bd2e4b..e6a51a3b 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -24,6 +24,8 @@ SOURCES += \ ../src/glmesh.cpp \ ../src/gpgsignaturechecker.cpp \ ../src/hasher.cpp \ + ../src/key.cpp \ + ../src/keyboard.cpp \ ../src/lightfieldstyle.cpp \ ../src/loader.cpp \ ../src/main.cpp \ @@ -70,6 +72,9 @@ HEADERS += \ ../src/glmesh.h \ ../src/gpgsignaturechecker.h \ ../src/hasher.h \ + ../src/inputdialog.h \ + ../src/key.h \ + ../src/keyboard.h \ ../src/initialshoweventmixin.h \ ../src/lightfieldstyle.h \ ../src/loader.h \ diff --git a/src/advancedtabselectionmodel.cpp b/src/advancedtabselectionmodel.cpp index 95696fca..0059d0da 100644 --- a/src/advancedtabselectionmodel.cpp +++ b/src/advancedtabselectionmodel.cpp @@ -1,16 +1,8 @@ #include "advancedtabselectionmodel.h" -#include - void AdvancedTabSelectionModel::onclick(const QModelIndex &index) { - QStandardItem *item = this->itemFromIndex(index); - int row = index.row(); - int column = index.column(); - - std::cout << row << std::endl; - std::cout << column << std::endl; for(int i=0; i<_panelsCount; i++) { diff --git a/src/app.cpp b/src/app.cpp index e67633c7..0da1ba67 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -251,7 +251,7 @@ App::App( int& argc, char* argv[] ): QApplication( argc, argv ) { QCoreApplication::setApplicationName( "LightField" ); QCoreApplication::setApplicationVersion( LIGHTFIELD_VERSION_STRING ); QGuiApplication::setFont( ModifyFont( QGuiApplication::font( ), "Montserrat", NormalFontSize ) ); -QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + _parseCommandLine( ); if ( !_isAlreadyRunning( ) ) { diff --git a/src/inputdialog.h b/src/inputdialog.h new file mode 100644 index 00000000..fadc61ba --- /dev/null +++ b/src/inputdialog.h @@ -0,0 +1,104 @@ +#ifndef __INPUTDIALOG_H__ +#define __INPUTDIALOG_H__ + +#include "keyboard.h" +#include "window.h" + +#include +class InputDialog: public QDialog { + Q_OBJECT + private: + Keyboard* _keyboard { new Keyboard(this) }; + QLabel* _message { new QLabel }; + QLineEdit* _input { new QLineEdit }; + QPushButton* _okButton { new QPushButton("Ok") }; + QPushButton* _cancelButton { new QPushButton("Cancel")}; + + + public: + InputDialog() { } + InputDialog(QString text) { + auto origFont = font( ); + auto fontAwesome = ModifyFont( origFont, "FontAwesome", LargeFontSize ); + + Window* win = App::mainWindow(); + QRect r = win->geometry(); + move(r.x()+100, r.y()+100); + + _message->setText(text); + + _keyboard->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + + _message->setFont(fontAwesome); + _okButton->setFont(fontAwesome); + _cancelButton->setFont(fontAwesome); + _input->setFont(fontAwesome); + + _okButton->setMinimumSize(QSize(245, 70)); + _cancelButton->setMinimumSize(QSize(245, 70)); + setContentsMargins( { } ); + setMinimumSize(QSize(580, 355)); + setLayout( + WrapWidgetsInVBox( + _message, + _input, + _keyboard, + WrapWidgetsInHBox(_okButton, _cancelButton) + ) + ); + + setModal(true); + + QWidget::connect(_keyboard, &Keyboard::keyPressed, this, &InputDialog::keyPressed); + QWidget::connect(_keyboard, &Keyboard::backspacePressed, this, &InputDialog::backspacePressed); + QWidget::connect(_okButton, &QPushButton::clicked, this, &InputDialog::oklCLicked_clicked); + QWidget::connect(_cancelButton, &QPushButton::clicked, this, &InputDialog::cancelCLicked_clicked); + } + + ~InputDialog() { + delete _keyboard; + delete _message; + delete _input; + delete _okButton; + delete _cancelButton; + } + + QString getValue() { + return _input->text(); + } + + public slots: + void keyPressed( QString t) { + int cursorPos = _input->cursorPosition(); + _input->setText(_input->text().insert(cursorPos, t)); + _input->setCursorPosition(cursorPos+1); + } + + void backspacePressed( ) { + int cursorPos = _input->cursorPosition(); + int length = _input->text().length(); + QString text = _input->text(); + if(cursorPos == 0) + return; + + _input->setText(text.left(cursorPos-1) + (cursorPos < length ? text.right(cursorPos) : "")); + _input->setCursorPosition(cursorPos-1); + } + + void cancelCLicked_clicked(bool) { + this->setResult(QDialog::Rejected); + this->reject(); + this->close(); + } + + void oklCLicked_clicked(bool) { + this->setResult(QDialog::Accepted); + this->accept(); + this->close(); + } + + signals: + void okclicked(); +}; + +#endif // __INPUTDIALOG_H__ diff --git a/src/key.cpp b/src/key.cpp new file mode 100644 index 00000000..48ea6474 --- /dev/null +++ b/src/key.cpp @@ -0,0 +1,72 @@ +#include +#include "key.h" + + +key::key(QString t,QObject *parent) : QObject(parent) +{ + text = t; + X =10; + Y =10; + W =t.length()*4 + 50; + H =50; + pressed = false; +} + +QRect key::getRect() +{ + return QRect(X,Y,W,H); +} + +void key::setX(int x ) +{ + // qDebug() << "setX " << x << " " << text; + X = x; +} + + +void key::setY(int y ) +{ + // qDebug() << "setY " << y; + Y = y; +} + + +void key::setIconFile(QString i ) +{ + iconFilename = i; + W = 25; +} + +void key::setPressed( bool b) +{ +// qDebug() << "setPessed " << b; + pressed = b; +} + +void key::draw(QPainter *p,QStyle *style) { + + QStyleOptionButton opt; + opt.palette = QPalette(Qt::red); + /* + if ( pressed ) + { + opt.state = QStyle::State_Active; + } else { + opt.state = QStyle::State_Enabled; + } + */ + opt.rect = QRect(X, Y, W, H); + + if ( iconFilename !="" ) + { + opt.icon = QIcon(iconFilename); + opt.iconSize=QSize(16,16); + } + else + { + if (text =="&") opt.text = "&&"; + else opt.text = text; + } + style->drawControl(QStyle::CE_PushButton, &opt, p); + +} diff --git a/src/key.h b/src/key.h new file mode 100644 index 00000000..ab05f096 --- /dev/null +++ b/src/key.h @@ -0,0 +1,40 @@ +#ifndef KEY_H +#define KEY_H + +#include +#include +#include +#include +#include +#include + + +class key : public QObject +{ + Q_OBJECT +public: + explicit key(QString t,QObject *parent = 0); + + void setX(int ); + void setY(int ); + void setIconFile(QString ); + void setPressed( bool b); + + void draw(QPainter *painter,QStyle *s); + QRect getRect(); + +signals: + +public slots: + +public : + int X; + int Y; + int W; + int H; + QString text; + QString iconFilename; + bool pressed; +}; + +#endif // KEY_H diff --git a/src/keyboard.cpp b/src/keyboard.cpp new file mode 100644 index 00000000..b8b34991 --- /dev/null +++ b/src/keyboard.cpp @@ -0,0 +1,226 @@ +#include +#include +#include +#include "key.h" +#include "keyboard.h" + +#define KEYS_TYPE 4 + +// TODO make an enum +#define LOWERCASE 0 +#define NUMBER 1 +#define UPPERCASE 2 +#define PUNCTUATION 3 + +// Declaration off the differente keys... + + +const char *en_lower_keymap[] = { + "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", + "a", "s", "d", "f", "g", "h", "j", "k", "l",";", + "Caps", "z", "x", "c", "v", "b", "n", "m",",", "backspace", + "123", ".", "space", "@" +}; + +const char *en_upper_keymap[] = { + "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", + "A", "S", "D", "F", "G", "H", "J", "K", "L",";", + "Caps", "Z", "X", "C", "V", "B", "N", "M",",", "backspace", + "123", ".", "space", "@" +}; + +const char *en_number_keymap[] = { + "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", + "-", "/", ":", ";", "(", ")", "€", "&", "@", "\"", + "#+=", ".", ",", "?", "!", "'", "+","\\","%","backspace", + "ABC", ",", "space", "." +}; + +const char *en_punctuation_keymap[] = { + "[", "]", "{", "}", "#", "%", "^", "*", "+", "=", + "_", "\\", "|", "~", "<", ">", "=","$", "@", "\"", + "123", ".", ",", "?", "!", "'","/",":",";", "backspace", + "ABC", ",", "space", "." +}; + +// In witch row are the key... (there's 4 rows ) +const int row_keymapp[] = { + 0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3 +}; + +const int nbkey = sizeof(en_lower_keymap)/ sizeof(char *); + +Keyboard::Keyboard(QWidget *p) : QWidget(p) +{ + currentKey = 0x0; + currentindexkeyboard = LOWERCASE; + uppercase = false; + + initTooltip(); + + keys = QVector >(KEYS_TYPE); + for (int n=0;n < KEYS_TYPE ; n++) + { + keys[n] = QVector< key * >(nbkey); + } + + initKeys(LOWERCASE,en_lower_keymap); + initKeys(NUMBER,en_number_keymap); + initKeys(UPPERCASE,en_upper_keymap); + initKeys(PUNCTUATION,en_punctuation_keymap); +} + +void Keyboard::initKeys( int indexArraykeys,const char *keymap[]) +{ + int row = 0; + for(int n=0; n< nbkey; n++) + { + keys[indexArraykeys][n] = new key(keymap[n]); + if ( n>0) + { + //if (keymap[n] == "return" ) keys[indexArraykeys][n]->setIconFile(":/img/img/enter.png"); + if (keymap[n] == "backspace" ) keys[indexArraykeys][n]->W=70; + if (keymap[n] == "Caps" ) keys[indexArraykeys][n]->W=70; + if (keymap[n] == "space" ) keys[indexArraykeys][n]->W=380; + + if (row_keymapp[n-1]!=row_keymapp[n]) + { + row ++; + keys[indexArraykeys][n]->setX(0); //offetrows[row]); + } else { + keys[indexArraykeys][n]->setX(keys[indexArraykeys][n-1]->X + keys[indexArraykeys][n-1]->W); + } + keys[indexArraykeys][n]->setY(row_keymapp[n]*50); + } else { + // keys[indexArraykeys][n]->setX(offetrows[0]); + keys[indexArraykeys][n]->setX(0); + keys[indexArraykeys][n]->setY(row_keymapp[n]*50); + } + } +} + +void Keyboard::initTooltip() +{ + tooltip = new QLabel(""); + tooltip->setWindowFlags( Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint ); + + QFont serifFont("Times", 18, QFont::Bold); + tooltip->setFont(serifFont); + tooltip->setAlignment(Qt::AlignCenter); + +} + +void Keyboard::mousePressEvent(QMouseEvent * e) { + QPoint pos = e->pos(); + setKeyPressed( findKey(pos), pos ); +} + +void Keyboard::mouseMoveEvent(QMouseEvent * e) { + QPoint pos = e->pos(); + + if (currentKey != 0x0 && !currentKey->getRect().contains(pos)) { + tooltip->hide(); + currentKey->setPressed(false); + this->repaint(); + } + setKeyPressed( findKey(pos), pos ); + } + +void Keyboard::mouseReleaseEvent(QMouseEvent *e) { + QPoint pos = e->pos(); + tooltip->hide(); + key *k= findKey( pos ); + if (k != 0x0 ) + { + if ( k->text == "ABC") + { + currentindexkeyboard = LOWERCASE; + repaint(); + return; + } + if (k->text== "#+=" ) + { + currentindexkeyboard = PUNCTUATION; + repaint(); + return; + } + + if (k->text=="Caps") + { if ( uppercase ==false) + { + currentindexkeyboard = UPPERCASE; + uppercase = true; + } + else + { + currentindexkeyboard = LOWERCASE; + uppercase = false; + } + repaint(); + return; + } + + if (k->text=="123") + { + currentindexkeyboard = NUMBER; + repaint(); + } + else + { + if ( k->text =="backspace" ) + { + emit backspacePressed(); + return; + } + + if (k->text=="enter") + { + emit returnPressed(); + return; + } + if ( k->text == "space") + { + emit keyPressed(" "); + } else + { + emit keyPressed(k->text); + } + } + } +} +key *Keyboard::findKey(QPoint p) +{ + foreach (key *k, keys[currentindexkeyboard]) + { + if (k->getRect().contains(p)) + { + return k; + } + } + return 0x0; +} + +void Keyboard::setKeyPressed( key *k, QPoint pos) +{ + currentKey = k; + if (k == 0x0) return; + + k->setPressed(true); + this->repaint(); + + QPoint p = QWidget::mapToGlobal(this->pos() +QPoint( k->X, k->Y)); + tooltip->setGeometry(p.x(),p.y()-50,50, 50); + tooltip->setText(k->text); + tooltip->show(); +} + +void Keyboard::paintEvent(QPaintEvent*) { + QPainter painter(this); + foreach (key *k, keys[currentindexkeyboard]) + { + k->draw(&painter,style()); + } +} diff --git a/src/keyboard.h b/src/keyboard.h new file mode 100644 index 00000000..3f2f4a7c --- /dev/null +++ b/src/keyboard.h @@ -0,0 +1,40 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include +#include +#include +#include +class key; + +class Keyboard : public QWidget +{ + Q_OBJECT + +public: + Keyboard(QWidget *p); + + signals: + void keyPressed( QString t); + void backspacePressed( ); + void returnPressed( ); + +private : + void paintEvent(QPaintEvent*); + void mousePressEvent(QMouseEvent * E); + void mouseMoveEvent(QMouseEvent * e); + void mouseReleaseEvent(QMouseEvent *); + + void initTooltip(); + void initKeys( int indexArraykeys,const char *keymap[]); + key *findKey(QPoint p); + void setKeyPressed( key *k,QPoint ); + + QVector > keys; + QLabel *tooltip; + key *currentKey; + int currentindexkeyboard; + bool uppercase ; +}; + +#endif // KEYBOARD_H diff --git a/src/main.cpp b/src/main.cpp index 67ac6ff7..2b0c184a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,8 +9,7 @@ void _initializeOpenGL( ) { } int main( int argc, char* argv[] ) { - qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); - qputenv("QT_VIRTUALKEYBOARD_STYLE_PATH","./styles"); + //qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); _initializeOpenGL( ); return App( argc, argv ).exec( ); } diff --git a/src/paramslider.h b/src/paramslider.h index 981369b4..d6189b4f 100644 --- a/src/paramslider.h +++ b/src/paramslider.h @@ -1,7 +1,6 @@ #ifndef __PARAMSLIDER_H__ #define __PARAMSLIDER_H__ -#include "tabbase.h" class ParamSlider: public QGroupBox { Q_OBJECT diff --git a/src/profilestab.cpp b/src/profilestab.cpp index 3f6eebb6..94b9ff28 100644 --- a/src/profilestab.cpp +++ b/src/profilestab.cpp @@ -1,5 +1,6 @@ #include "pch.h" +#include "inputdialog.h" #include "profilestab.h" #include "profilesjsonparser.h" #include "window.h" @@ -212,15 +213,18 @@ void ProfilesTab::newProfile_clicked(bool) msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); - QInputDialog inputDialog; + /*QInputDialog inputDialog; inputDialog.setModal(true); inputDialog.move(r.x()+100, r.y()+100); inputDialog.setFont(*_fontAwesome); inputDialog.setFocus(Qt::FocusReason::ActiveWindowFocusReason); inputDialog.setLabelText("Enter a profile name: "); - int ret = inputDialog.exec(); + int ret = inputDialog.exec();*/ - QString filename = inputDialog.textValue(); + InputDialog* inputDialog = new InputDialog(QString("Entry profile name: ")); + int ret = inputDialog->exec(); + + QString filename = inputDialog->getValue(); if (ret && !filename.isEmpty()) { if(!_createNewProfile(filename)) From fa743e40dc3ff7cb25a6a76d08ca5cf99730346c Mon Sep 17 00:00:00 2001 From: Jakub Klama Date: Fri, 31 Jan 2020 19:01:14 +0000 Subject: [PATCH 76/89] Keyboard color and layout fixes --- src/inputdialog.h | 1 + src/key.cpp | 144 +++++++-------- src/keyboard.cpp | 452 +++++++++++++++++++++++----------------------- 3 files changed, 299 insertions(+), 298 deletions(-) diff --git a/src/inputdialog.h b/src/inputdialog.h index fadc61ba..d25c144e 100644 --- a/src/inputdialog.h +++ b/src/inputdialog.h @@ -24,6 +24,7 @@ class InputDialog: public QDialog { Window* win = App::mainWindow(); QRect r = win->geometry(); move(r.x()+100, r.y()+100); + resize(824, 400); _message->setText(text); diff --git a/src/key.cpp b/src/key.cpp index 48ea6474..55a88c38 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,72 +1,72 @@ -#include -#include "key.h" - - -key::key(QString t,QObject *parent) : QObject(parent) -{ - text = t; - X =10; - Y =10; - W =t.length()*4 + 50; - H =50; - pressed = false; -} - -QRect key::getRect() -{ - return QRect(X,Y,W,H); -} - -void key::setX(int x ) -{ - // qDebug() << "setX " << x << " " << text; - X = x; -} - - -void key::setY(int y ) -{ - // qDebug() << "setY " << y; - Y = y; -} - - -void key::setIconFile(QString i ) -{ - iconFilename = i; - W = 25; -} - -void key::setPressed( bool b) -{ -// qDebug() << "setPessed " << b; - pressed = b; -} - -void key::draw(QPainter *p,QStyle *style) { - - QStyleOptionButton opt; - opt.palette = QPalette(Qt::red); - /* - if ( pressed ) - { - opt.state = QStyle::State_Active; - } else { - opt.state = QStyle::State_Enabled; - } - */ - opt.rect = QRect(X, Y, W, H); - - if ( iconFilename !="" ) - { - opt.icon = QIcon(iconFilename); - opt.iconSize=QSize(16,16); - } - else - { - if (text =="&") opt.text = "&&"; - else opt.text = text; - } - style->drawControl(QStyle::CE_PushButton, &opt, p); - -} +#include +#include "key.h" + + +key::key(QString t,QObject *parent) : QObject(parent) +{ + text = t; + X =10; + Y =10; + W =t.length()*4 + 78; + H =62; + pressed = false; +} + +QRect key::getRect() +{ + return QRect(X,Y,W,H); +} + +void key::setX(int x ) +{ + // qDebug() << "setX " << x << " " << text; + X = x; +} + + +void key::setY(int y ) +{ + // qDebug() << "setY " << y; + Y = y; +} + + +void key::setIconFile(QString i ) +{ + iconFilename = i; + W = 25; +} + +void key::setPressed( bool b) +{ +// qDebug() << "setPessed " << b; + pressed = b; +} + +void key::draw(QPainter *p,QStyle *style) { + + QStyleOptionButton opt; + opt.palette = QPalette(QColor(0,51,102,127)); + /* + if ( pressed ) + { + opt.state = QStyle::State_Active; + } else { + opt.state = QStyle::State_Enabled; + } + */ + opt.rect = QRect(X, Y, W, H); + + if ( iconFilename !="" ) + { + opt.icon = QIcon(iconFilename); + opt.iconSize=QSize(16,16); + } + else + { + if (text =="&") opt.text = "&&"; + else opt.text = text; + } + style->drawControl(QStyle::CE_PushButton, &opt, p); + +} diff --git a/src/keyboard.cpp b/src/keyboard.cpp index b8b34991..8c989809 100644 --- a/src/keyboard.cpp +++ b/src/keyboard.cpp @@ -1,226 +1,226 @@ -#include -#include -#include -#include "key.h" -#include "keyboard.h" - -#define KEYS_TYPE 4 - -// TODO make an enum -#define LOWERCASE 0 -#define NUMBER 1 -#define UPPERCASE 2 -#define PUNCTUATION 3 - -// Declaration off the differente keys... - - -const char *en_lower_keymap[] = { - "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", - "a", "s", "d", "f", "g", "h", "j", "k", "l",";", - "Caps", "z", "x", "c", "v", "b", "n", "m",",", "backspace", - "123", ".", "space", "@" -}; - -const char *en_upper_keymap[] = { - "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", - "A", "S", "D", "F", "G", "H", "J", "K", "L",";", - "Caps", "Z", "X", "C", "V", "B", "N", "M",",", "backspace", - "123", ".", "space", "@" -}; - -const char *en_number_keymap[] = { - "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", - "-", "/", ":", ";", "(", ")", "€", "&", "@", "\"", - "#+=", ".", ",", "?", "!", "'", "+","\\","%","backspace", - "ABC", ",", "space", "." -}; - -const char *en_punctuation_keymap[] = { - "[", "]", "{", "}", "#", "%", "^", "*", "+", "=", - "_", "\\", "|", "~", "<", ">", "=","$", "@", "\"", - "123", ".", ",", "?", "!", "'","/",":",";", "backspace", - "ABC", ",", "space", "." -}; - -// In witch row are the key... (there's 4 rows ) -const int row_keymapp[] = { - 0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3 -}; - -const int nbkey = sizeof(en_lower_keymap)/ sizeof(char *); - -Keyboard::Keyboard(QWidget *p) : QWidget(p) -{ - currentKey = 0x0; - currentindexkeyboard = LOWERCASE; - uppercase = false; - - initTooltip(); - - keys = QVector >(KEYS_TYPE); - for (int n=0;n < KEYS_TYPE ; n++) - { - keys[n] = QVector< key * >(nbkey); - } - - initKeys(LOWERCASE,en_lower_keymap); - initKeys(NUMBER,en_number_keymap); - initKeys(UPPERCASE,en_upper_keymap); - initKeys(PUNCTUATION,en_punctuation_keymap); -} - -void Keyboard::initKeys( int indexArraykeys,const char *keymap[]) -{ - int row = 0; - for(int n=0; n< nbkey; n++) - { - keys[indexArraykeys][n] = new key(keymap[n]); - if ( n>0) - { - //if (keymap[n] == "return" ) keys[indexArraykeys][n]->setIconFile(":/img/img/enter.png"); - if (keymap[n] == "backspace" ) keys[indexArraykeys][n]->W=70; - if (keymap[n] == "Caps" ) keys[indexArraykeys][n]->W=70; - if (keymap[n] == "space" ) keys[indexArraykeys][n]->W=380; - - if (row_keymapp[n-1]!=row_keymapp[n]) - { - row ++; - keys[indexArraykeys][n]->setX(0); //offetrows[row]); - } else { - keys[indexArraykeys][n]->setX(keys[indexArraykeys][n-1]->X + keys[indexArraykeys][n-1]->W); - } - keys[indexArraykeys][n]->setY(row_keymapp[n]*50); - } else { - // keys[indexArraykeys][n]->setX(offetrows[0]); - keys[indexArraykeys][n]->setX(0); - keys[indexArraykeys][n]->setY(row_keymapp[n]*50); - } - } -} - -void Keyboard::initTooltip() -{ - tooltip = new QLabel(""); - tooltip->setWindowFlags( Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint ); - - QFont serifFont("Times", 18, QFont::Bold); - tooltip->setFont(serifFont); - tooltip->setAlignment(Qt::AlignCenter); - -} - -void Keyboard::mousePressEvent(QMouseEvent * e) { - QPoint pos = e->pos(); - setKeyPressed( findKey(pos), pos ); -} - -void Keyboard::mouseMoveEvent(QMouseEvent * e) { - QPoint pos = e->pos(); - - if (currentKey != 0x0 && !currentKey->getRect().contains(pos)) { - tooltip->hide(); - currentKey->setPressed(false); - this->repaint(); - } - setKeyPressed( findKey(pos), pos ); - } - -void Keyboard::mouseReleaseEvent(QMouseEvent *e) { - QPoint pos = e->pos(); - tooltip->hide(); - key *k= findKey( pos ); - if (k != 0x0 ) - { - if ( k->text == "ABC") - { - currentindexkeyboard = LOWERCASE; - repaint(); - return; - } - if (k->text== "#+=" ) - { - currentindexkeyboard = PUNCTUATION; - repaint(); - return; - } - - if (k->text=="Caps") - { if ( uppercase ==false) - { - currentindexkeyboard = UPPERCASE; - uppercase = true; - } - else - { - currentindexkeyboard = LOWERCASE; - uppercase = false; - } - repaint(); - return; - } - - if (k->text=="123") - { - currentindexkeyboard = NUMBER; - repaint(); - } - else - { - if ( k->text =="backspace" ) - { - emit backspacePressed(); - return; - } - - if (k->text=="enter") - { - emit returnPressed(); - return; - } - if ( k->text == "space") - { - emit keyPressed(" "); - } else - { - emit keyPressed(k->text); - } - } - } -} -key *Keyboard::findKey(QPoint p) -{ - foreach (key *k, keys[currentindexkeyboard]) - { - if (k->getRect().contains(p)) - { - return k; - } - } - return 0x0; -} - -void Keyboard::setKeyPressed( key *k, QPoint pos) -{ - currentKey = k; - if (k == 0x0) return; - - k->setPressed(true); - this->repaint(); - - QPoint p = QWidget::mapToGlobal(this->pos() +QPoint( k->X, k->Y)); - tooltip->setGeometry(p.x(),p.y()-50,50, 50); - tooltip->setText(k->text); - tooltip->show(); -} - -void Keyboard::paintEvent(QPaintEvent*) { - QPainter painter(this); - foreach (key *k, keys[currentindexkeyboard]) - { - k->draw(&painter,style()); - } -} +#include +#include +#include +#include "key.h" +#include "keyboard.h" + +#define KEYS_TYPE 4 + +// TODO make an enum +#define LOWERCASE 0 +#define NUMBER 1 +#define UPPERCASE 2 +#define PUNCTUATION 3 + +// Declaration off the differente keys... + + +const char *en_lower_keymap[] = { + "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", + "a", "s", "d", "f", "g", "h", "j", "k", "l",";", + "Caps", "z", "x", "c", "v", "b", "n", "m",",", "backspace", + "123", ".", "space", "@" +}; + +const char *en_upper_keymap[] = { + "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", + "A", "S", "D", "F", "G", "H", "J", "K", "L",";", + "Caps", "Z", "X", "C", "V", "B", "N", "M",",", "backspace", + "123", ".", "space", "@" +}; + +const char *en_number_keymap[] = { + "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", + "-", "/", ":", ";", "(", ")", "€", "&", "@", "\"", + "#+=", ".", ",", "?", "!", "'", "+","\\","%","backspace", + "ABC", ",", "space", "." +}; + +const char *en_punctuation_keymap[] = { + "[", "]", "{", "}", "#", "%", "^", "*", "+", "=", + "_", "\\", "|", "~", "<", ">", "=","$", "@", "\"", + "123", ".", ",", "?", "!", "'","/",":",";", "backspace", + "ABC", ",", "space", "." +}; + +// In witch row are the key... (there's 4 rows ) +const int row_keymapp[] = { + 0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3 +}; + +const int nbkey = sizeof(en_lower_keymap)/ sizeof(char *); + +Keyboard::Keyboard(QWidget *p) : QWidget(p) +{ + currentKey = 0x0; + currentindexkeyboard = LOWERCASE; + uppercase = false; + + initTooltip(); + + keys = QVector >(KEYS_TYPE); + for (int n=0;n < KEYS_TYPE ; n++) + { + keys[n] = QVector< key * >(nbkey); + } + + initKeys(LOWERCASE,en_lower_keymap); + initKeys(NUMBER,en_number_keymap); + initKeys(UPPERCASE,en_upper_keymap); + initKeys(PUNCTUATION,en_punctuation_keymap); +} + +void Keyboard::initKeys( int indexArraykeys,const char *keymap[]) +{ + int row = 0; + for(int n=0; n< nbkey; n++) + { + keys[indexArraykeys][n] = new key(keymap[n]); + if ( n>0) + { + //if (keymap[n] == "return" ) keys[indexArraykeys][n]->setIconFile(":/img/img/enter.png"); + if (keymap[n] == "backspace" ) keys[indexArraykeys][n]->W=95; + if (keymap[n] == "Caps" ) keys[indexArraykeys][n]->W=70; + if (keymap[n] == "space" ) keys[indexArraykeys][n]->W=570; + + if (row_keymapp[n-1]!=row_keymapp[n]) + { + row ++; + keys[indexArraykeys][n]->setX(0); //offetrows[row]); + } else { + keys[indexArraykeys][n]->setX(keys[indexArraykeys][n-1]->X + keys[indexArraykeys][n-1]->W); + } + keys[indexArraykeys][n]->setY(row_keymapp[n]*62); + } else { + // keys[indexArraykeys][n]->setX(offetrows[0]); + keys[indexArraykeys][n]->setX(0); + keys[indexArraykeys][n]->setY(row_keymapp[n]*62); + } + } +} + +void Keyboard::initTooltip() +{ + tooltip = new QLabel(""); + tooltip->setWindowFlags( Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint ); + + QFont serifFont("Times", 18, QFont::Bold); + tooltip->setFont(serifFont); + tooltip->setAlignment(Qt::AlignCenter); + +} + +void Keyboard::mousePressEvent(QMouseEvent * e) { + QPoint pos = e->pos(); + setKeyPressed( findKey(pos), pos ); +} + +void Keyboard::mouseMoveEvent(QMouseEvent * e) { + QPoint pos = e->pos(); + + if (currentKey != 0x0 && !currentKey->getRect().contains(pos)) { + tooltip->hide(); + currentKey->setPressed(false); + this->repaint(); + } + setKeyPressed( findKey(pos), pos ); + } + +void Keyboard::mouseReleaseEvent(QMouseEvent *e) { + QPoint pos = e->pos(); + tooltip->hide(); + key *k= findKey( pos ); + if (k != 0x0 ) + { + if ( k->text == "ABC") + { + currentindexkeyboard = LOWERCASE; + repaint(); + return; + } + if (k->text== "#+=" ) + { + currentindexkeyboard = PUNCTUATION; + repaint(); + return; + } + + if (k->text=="Caps") + { if ( uppercase ==false) + { + currentindexkeyboard = UPPERCASE; + uppercase = true; + } + else + { + currentindexkeyboard = LOWERCASE; + uppercase = false; + } + repaint(); + return; + } + + if (k->text=="123") + { + currentindexkeyboard = NUMBER; + repaint(); + } + else + { + if ( k->text =="backspace" ) + { + emit backspacePressed(); + return; + } + + if (k->text=="enter") + { + emit returnPressed(); + return; + } + if ( k->text == "space") + { + emit keyPressed(" "); + } else + { + emit keyPressed(k->text); + } + } + } +} +key *Keyboard::findKey(QPoint p) +{ + foreach (key *k, keys[currentindexkeyboard]) + { + if (k->getRect().contains(p)) + { + return k; + } + } + return 0x0; +} + +void Keyboard::setKeyPressed( key *k, QPoint pos) +{ + currentKey = k; + if (k == 0x0) return; + + k->setPressed(true); + this->repaint(); + + QPoint p = QWidget::mapToGlobal(this->pos() +QPoint( k->X, k->Y)); + tooltip->setGeometry(p.x(),p.y()-50,50, 50); + tooltip->setText(k->text); + tooltip->show(); +} + +void Keyboard::paintEvent(QPaintEvent*) { + QPainter painter(this); + foreach (key *k, keys[currentindexkeyboard]) + { + k->draw(&painter,style()); + } +} From 25aa0da7fe74af2553f429be51704b48952ab663 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 31 Jan 2020 11:41:18 -0800 Subject: [PATCH 77/89] Small fixes after merge. --- qt/lf.pro | 6 +-- src/key.h | 80 ++++++++++++++++++++-------------------- src/keyboard.cpp | 16 ++++---- src/keyboard.h | 80 ++++++++++++++++++++-------------------- src/profilesjsonparser.h | 2 +- src/profilestab.cpp | 2 + 6 files changed, 93 insertions(+), 93 deletions(-) diff --git a/qt/lf.pro b/qt/lf.pro index e6a51a3b..02a2f2ff 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -1,4 +1,4 @@ -QT += core gui widgets xml +QT += core gui virtualkeyboard widgets xml TARGET = lf TEMPLATE = app @@ -6,7 +6,6 @@ TEMPLATE = app QMAKE_CXXFLAGS_RELEASE -= -O2 QMAKE_CXXFLAGS_RELEASE += -O3 -DNDEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result QMAKE_CXXFLAGS_DEBUG += -Og -D_DEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result -QT += virtualkeyboard SOURCES += \ ../src/advancedtab.cpp \ @@ -132,7 +131,7 @@ debug { dlp4710 { DEFINES += DLP4710 -}QT += core gui widgets xml +} TARGET = lf TEMPLATE = app @@ -140,4 +139,3 @@ TEMPLATE = app QMAKE_CXXFLAGS_RELEASE -= -O2 QMAKE_CXXFLAGS_RELEASE += -O3 -DNDEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result QMAKE_CXXFLAGS_DEBUG += -Og -D_DEBUG -Wall -Wextra -Winvalid-pch -Wno-unused-result -QT += virtualkeyboard diff --git a/src/key.h b/src/key.h index ab05f096..ed1b920b 100644 --- a/src/key.h +++ b/src/key.h @@ -1,40 +1,40 @@ -#ifndef KEY_H -#define KEY_H - -#include -#include -#include -#include -#include -#include - - -class key : public QObject -{ - Q_OBJECT -public: - explicit key(QString t,QObject *parent = 0); - - void setX(int ); - void setY(int ); - void setIconFile(QString ); - void setPressed( bool b); - - void draw(QPainter *painter,QStyle *s); - QRect getRect(); - -signals: - -public slots: - -public : - int X; - int Y; - int W; - int H; - QString text; - QString iconFilename; - bool pressed; -}; - -#endif // KEY_H +#ifndef KEY_H +#define KEY_H + +#include +#include +#include +#include +#include +#include + + +class key : public QObject +{ + Q_OBJECT +public: + explicit key(QString t,QObject *parent = 0); + + void setX(int ); + void setY(int ); + void setIconFile(QString ); + void setPressed( bool b); + + void draw(QPainter *painter,QStyle *s); + QRect getRect(); + +signals: + +public slots: + +public : + int X; + int Y; + int W; + int H; + QString text; + QString iconFilename; + bool pressed; +}; + +#endif // KEY_H diff --git a/src/keyboard.cpp b/src/keyboard.cpp index 8c989809..790f3793 100644 --- a/src/keyboard.cpp +++ b/src/keyboard.cpp @@ -44,7 +44,7 @@ const char *en_punctuation_keymap[] = { }; // In witch row are the key... (there's 4 rows ) -const int row_keymapp[] = { +const int row_keymap[] = { 0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2, @@ -82,22 +82,22 @@ void Keyboard::initKeys( int indexArraykeys,const char *keymap[]) if ( n>0) { //if (keymap[n] == "return" ) keys[indexArraykeys][n]->setIconFile(":/img/img/enter.png"); - if (keymap[n] == "backspace" ) keys[indexArraykeys][n]->W=95; - if (keymap[n] == "Caps" ) keys[indexArraykeys][n]->W=70; - if (keymap[n] == "space" ) keys[indexArraykeys][n]->W=570; + if (0 == strcasecmp( keymap[n], "backspace")) keys[indexArraykeys][n]->W=95; + if (0 == strcasecmp( keymap[n], "Caps" )) keys[indexArraykeys][n]->W=70; + if (0 == strcasecmp( keymap[n], "space" )) keys[indexArraykeys][n]->W=570; - if (row_keymapp[n-1]!=row_keymapp[n]) + if (row_keymap[n-1]!=row_keymap[n]) { row ++; keys[indexArraykeys][n]->setX(0); //offetrows[row]); } else { keys[indexArraykeys][n]->setX(keys[indexArraykeys][n-1]->X + keys[indexArraykeys][n-1]->W); } - keys[indexArraykeys][n]->setY(row_keymapp[n]*62); + keys[indexArraykeys][n]->setY(row_keymap[n]*62); } else { // keys[indexArraykeys][n]->setX(offetrows[0]); keys[indexArraykeys][n]->setX(0); - keys[indexArraykeys][n]->setY(row_keymapp[n]*62); + keys[indexArraykeys][n]->setY(row_keymap[n]*62); } } } @@ -203,7 +203,7 @@ key *Keyboard::findKey(QPoint p) return 0x0; } -void Keyboard::setKeyPressed( key *k, QPoint pos) +void Keyboard::setKeyPressed( key *k, QPoint /*pos*/) { currentKey = k; if (k == 0x0) return; diff --git a/src/keyboard.h b/src/keyboard.h index 3f2f4a7c..d5ec057a 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -1,40 +1,40 @@ -#ifndef KEYBOARD_H -#define KEYBOARD_H - -#include -#include -#include -#include -class key; - -class Keyboard : public QWidget -{ - Q_OBJECT - -public: - Keyboard(QWidget *p); - - signals: - void keyPressed( QString t); - void backspacePressed( ); - void returnPressed( ); - -private : - void paintEvent(QPaintEvent*); - void mousePressEvent(QMouseEvent * E); - void mouseMoveEvent(QMouseEvent * e); - void mouseReleaseEvent(QMouseEvent *); - - void initTooltip(); - void initKeys( int indexArraykeys,const char *keymap[]); - key *findKey(QPoint p); - void setKeyPressed( key *k,QPoint ); - - QVector > keys; - QLabel *tooltip; - key *currentKey; - int currentindexkeyboard; - bool uppercase ; -}; - -#endif // KEYBOARD_H +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include +#include +#include +#include +class key; + +class Keyboard : public QWidget +{ + Q_OBJECT + +public: + Keyboard(QWidget *p); + + signals: + void keyPressed( QString t); + void backspacePressed( ); + void returnPressed( ); + +private : + void paintEvent(QPaintEvent*); + void mousePressEvent(QMouseEvent * E); + void mouseMoveEvent(QMouseEvent * e); + void mouseReleaseEvent(QMouseEvent *); + + void initTooltip(); + void initKeys( int indexArraykeys,const char *keymap[]); + key *findKey(QPoint p); + void setKeyPressed( key *k,QPoint ); + + QVector > keys; + QLabel *tooltip; + key *currentKey; + int currentindexkeyboard; + bool uppercase ; +}; + +#endif // KEYBOARD_H diff --git a/src/profilesjsonparser.h b/src/profilesjsonparser.h index 2d85cf70..96250e75 100644 --- a/src/profilesjsonparser.h +++ b/src/profilesjsonparser.h @@ -84,7 +84,7 @@ class ProfilesJsonParser log( "push back to list ... " + std::to_string(array.count()) + " " + std::to_string(i) ); profilesList->push_back(printProfile); } - catch (QException e) { } + catch (QException const& e) { } catch (...) { std::cerr<<"Unknow error while parsing print profile"; diff --git a/src/profilestab.cpp b/src/profilestab.cpp index 94b9ff28..f6133925 100644 --- a/src/profilestab.cpp +++ b/src/profilestab.cpp @@ -318,6 +318,8 @@ bool ProfilesTab::_deletePrintProfile() _model->removeRows(index.row(), 1); ProfilesJsonParser::saveProfiles(_printProfileManager->profiles()); + + return true; } bool ProfilesTab::_loadPrintProfile() From 3b9ae68f1d0f054b442ccdc4857a97bb8a0efd45 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 31 Jan 2020 11:41:33 -0800 Subject: [PATCH 78/89] Debian packaging: Add new dependencies for virtual keyboard. --- debian/control.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/debian/control.in b/debian/control.in index 33158484..ab53f285 100644 --- a/debian/control.in +++ b/debian/control.in @@ -6,7 +6,8 @@ Build-Depends: debhelper (>= 11), libhidapi-dev (>> 0.8), python3 (>> 3.6), - qtbase5-dev (>= 5.11.1) + qtbase5-dev (>= 5.11.1), + libqt5virtualkeyboard5-dev (>= 5.11.1) Standards-Version: 4.1.3 Homepage: https://github.com/VolumetricBio/LightField Vcs-Browser: https://github.com/VolumetricBio/LightField @@ -25,9 +26,11 @@ Depends: libhidapi-libusb0 (>> 0.8), libqt5dbus5 (>= 5.11.1), libqt5network5 (>= 5.11.1), + libqt5virtualkeyboard5 (>= 5.11.1), avrdude (>= 6.3), gpg (>= 2.2.8), graphicsmagick (>> 1.3), + qtvirtualkeyboard-plugin (>= 5.11.1), slic3r (>= 1.2.9), sudo (>= 1.8.23) Recommends: From 1cb490746ae233a909f1777166f1f988730d5ab2 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Fri, 31 Jan 2020 12:51:14 -0800 Subject: [PATCH 79/89] Rip out references to QtVirtualKeyboard because it doesn't seem to exist on Ubuntu 18.10, and we aren't actually using it (yet), anyway. --- debian/control.in | 5 +---- qt/lf.pro | 2 +- src/advancedtab.cpp | 1 - src/main.cpp | 1 - 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/debian/control.in b/debian/control.in index ab53f285..33158484 100644 --- a/debian/control.in +++ b/debian/control.in @@ -6,8 +6,7 @@ Build-Depends: debhelper (>= 11), libhidapi-dev (>> 0.8), python3 (>> 3.6), - qtbase5-dev (>= 5.11.1), - libqt5virtualkeyboard5-dev (>= 5.11.1) + qtbase5-dev (>= 5.11.1) Standards-Version: 4.1.3 Homepage: https://github.com/VolumetricBio/LightField Vcs-Browser: https://github.com/VolumetricBio/LightField @@ -26,11 +25,9 @@ Depends: libhidapi-libusb0 (>> 0.8), libqt5dbus5 (>= 5.11.1), libqt5network5 (>= 5.11.1), - libqt5virtualkeyboard5 (>= 5.11.1), avrdude (>= 6.3), gpg (>= 2.2.8), graphicsmagick (>> 1.3), - qtvirtualkeyboard-plugin (>= 5.11.1), slic3r (>= 1.2.9), sudo (>= 1.8.23) Recommends: diff --git a/qt/lf.pro b/qt/lf.pro index 02a2f2ff..41a38653 100644 --- a/qt/lf.pro +++ b/qt/lf.pro @@ -1,4 +1,4 @@ -QT += core gui virtualkeyboard widgets xml +QT += core gui widgets xml TARGET = lf TEMPLATE = app diff --git a/src/advancedtab.cpp b/src/advancedtab.cpp index d82e4a72..c0629bbc 100644 --- a/src/advancedtab.cpp +++ b/src/advancedtab.cpp @@ -9,7 +9,6 @@ #include "advancedtabselectionmodel.h" #include "paramslider.h" #include -#include namespace { diff --git a/src/main.cpp b/src/main.cpp index 2b0c184a..682b6dc8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,6 @@ void _initializeOpenGL( ) { } int main( int argc, char* argv[] ) { - //qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); _initializeOpenGL( ); return App( argc, argv ).exec( ); } From 32f15ee311e913ef5bb84f23fcd5d1f30604b601 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Sun, 2 Feb 2020 14:13:24 -0800 Subject: [PATCH 80/89] Class ProfilesJsonParser: Replace log() and std::cerr with debug(). --- src/profilesjsonparser.h | 152 +++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/src/profilesjsonparser.h b/src/profilesjsonparser.h index 96250e75..e1b9b77f 100644 --- a/src/profilesjsonparser.h +++ b/src/profilesjsonparser.h @@ -2,7 +2,6 @@ #define PROFILESJSONPARSER_H #include "printprofile.h" -#include #define DEBUG_LOGS @@ -12,104 +11,109 @@ class ProfilesJsonParser static QVector* loadProfiles() { QVector* profilesList = new QVector(); - log( "opening file ..." ); + debug( "ProfilesJsonParser::loadProfiles: opening file\n" ); QFile jsonFile(PrintProfilesPath); if(!jsonFile.exists()) { - std::cerr << "File " << PrintProfilesPath.toStdString() << " does not exists" << std::endl; + debug( "ProfilesJsonParser::loadProfiles: File %s does not exist\n", PrintProfilesPath.toUtf8( ).data( ) ); return profilesList; } jsonFile.open(QIODevice::ReadOnly); - log( "building document ..." ); + debug( "ProfilesJsonParser::loadProfiles: building document\n" ); QJsonDocument jsonDocument=QJsonDocument().fromJson(jsonFile.readAll()); - log( "get root array ..." ); + debug( "ProfilesJsonParser::loadProfiles: get root array\n" ); QJsonArray array = jsonDocument.array(); - log( "iter over array ... " + std::to_string(array.count()) ); + debug( "ProfilesJsonParser::loadProfiles: iter over array: %d elements\n", array.count() ); for(int i=0; isetProfileName(obj["name"].toString()); - } - else - { - std::cerr<<"Error while parsing name field"; - throw new QException(); - } - - log( "get baseLayerCount ..." ); - auto blc = obj["baseLayerCount"]; - - if(blc.isDouble()) - printProfile->setBaseLayerCount(blc.toInt()); - else - { - std::cerr<<"Error while parsing baseLayerCount field"; - throw new QException(); - } - - log( "get baseLayersPumpingParameters ..." ); - auto baseLayersPumpingParameters = obj["baseLayersPumpingParameters"]; - - if(!baseLayersPumpingParameters.isUndefined() && !baseLayersPumpingParameters.isNull()) - { - PrintPumpingParameters params = _parsePrintPumpingParameters(baseLayersPumpingParameters.toObject()); - printProfile->setBaseLayersPumpingParameters(params); - printProfile->setAddBaseLayersPumpingParameters(true); - } - else - printProfile->setAddBaseLayersPumpingParameters(false); - - log( "get bodyLayersPumpingParameters ..." ); - auto bodyLayersPumpingParameters = obj["bodyLayersPumpingParameters"]; - - if(!bodyLayersPumpingParameters.isUndefined() && !bodyLayersPumpingParameters.isNull()) - { - PrintPumpingParameters params = _parsePrintPumpingParameters(bodyLayersPumpingParameters.toObject()); - printProfile->setBodyLayersPumpingParameters(params); - printProfile->setAddBodyLayersPumpingParameters(true); - } - else - printProfile->setAddBodyLayersPumpingParameters(false); - - log( "push back to list ... " + std::to_string(array.count()) + " " + std::to_string(i) ); - profilesList->push_back(printProfile); + PrintProfile* printProfile = new PrintProfile(); + try { + + QJsonObject obj = array[i].toObject(); + + debug( "ProfilesJsonParser::loadProfiles: element %d\n", i ); + debug( " + get name\n"); + auto name = obj["name"]; + + if(name.isString()) { + printProfile->setProfileName(obj["name"].toString()); + } + else + { + debug( "ProfilesJsonParser::loadProfiles: Error while parsing name field" ); + throw new QException(); + } + + debug( " + get baseLayerCount\n" ); + auto blc = obj["baseLayerCount"]; + + if(blc.isDouble()) + printProfile->setBaseLayerCount(blc.toInt()); + else + { + debug( "ProfilesJsonParser::loadProfiles: Error while parsing baseLayerCount field" ); + throw new QException(); } - catch (QException const& e) { } - catch (...) + + debug( " + get baseLayersPumpingParameters\n" ); + auto baseLayersPumpingParameters = obj["baseLayersPumpingParameters"]; + + if(!baseLayersPumpingParameters.isUndefined() && !baseLayersPumpingParameters.isNull()) { - std::cerr<<"Unknow error while parsing print profile"; + PrintPumpingParameters params = _parsePrintPumpingParameters(baseLayersPumpingParameters.toObject()); + printProfile->setBaseLayersPumpingParameters(params); + printProfile->setAddBaseLayersPumpingParameters(true); } + else + printProfile->setAddBaseLayersPumpingParameters(false); + + debug( " + get bodyLayersPumpingParameters\n" ); + auto bodyLayersPumpingParameters = obj["bodyLayersPumpingParameters"]; + + if(!bodyLayersPumpingParameters.isUndefined() && !bodyLayersPumpingParameters.isNull()) + { + PrintPumpingParameters params = _parsePrintPumpingParameters(bodyLayersPumpingParameters.toObject()); + printProfile->setBodyLayersPumpingParameters(params); + printProfile->setAddBodyLayersPumpingParameters(true); + } + else + printProfile->setAddBodyLayersPumpingParameters(false); + + debug( " + push back to list\n" ); + profilesList->push_back(printProfile); + } + catch (QException*) + { + delete printProfile; } + catch (...) + { + debug( "ProfilesJsonParser::loadProfiles: Unknown error while parsing print profile\n" ); + delete printProfile; + } + } - log( "end parsing ..." ); + debug( "ProfilesJsonParser::loadProfiles: end parsing\n" ); return profilesList; } static void saveProfiles(const QVector* profiles) { - log( "saveProfiles ..." ); + debug( "ProfilesJsonParser::saveProfiles:\n" ); QFile jsonFile(PrintProfilesPath); QJsonDocument jsonDocument=QJsonDocument(); QJsonArray jsonArray; - log( "loop start ..." ); + debug( " + loop start: %d profiles\n", profiles->count( ) ); for(int i=0; icount(); ++i) { auto profile = (*profiles)[i]; - log( "serializing profile " + profile->profileName().toStdString() ); + debug( " + serializing profile %s\n", profile->profileName( ).toUtf8( ).data( ) ); if(profile->profileName() == "temp") continue; @@ -167,8 +171,8 @@ class ProfilesJsonParser return value.toInt(); else { - std::cerr<<"Error while parsing " << propertyName.toStdString() << " field"; - throw new QException(); + debug( "ProfilesJsonParser::_parseIntValue: Error while parsing field %s\n", propertyName.toUtf8( ).data( ) ); + throw new QException(); } } @@ -182,8 +186,8 @@ class ProfilesJsonParser return value.toDouble(); else { - std::cerr<<"Error while parsing " << propertyName.toStdString() << " field"; - throw new QException(); + debug( "ProfilesJsonParser::_parseDoubleValue: Error while parsing field %s\n", propertyName.toUtf8( ).data( ) ); + throw new QException(); } } @@ -204,12 +208,6 @@ class ProfilesJsonParser return result; } - static void log(std::string message) - { -#ifdef DEBUG_LOGS - std::cout << message << std::endl; -#endif - } }; #endif // PROFILESJSONPARSER_H From 858b73c62fc1729193e2d5b5da7692289cd133d0 Mon Sep 17 00:00:00 2001 From: Ice Karma Date: Tue, 4 Feb 2020 19:19:35 -0800 Subject: [PATCH 81/89] Naming. --- src/advancedtab.cpp | 8 ++++---- src/printprofile.h | 24 ++++++++++++------------ src/profilesjsonparser.h | 12 ++++++------ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/advancedtab.cpp b/src/advancedtab.cpp index c0629bbc..ba8f7df8 100644 --- a/src/advancedtab.cpp +++ b/src/advancedtab.cpp @@ -636,7 +636,7 @@ void AdvancedTab::updatePrintProfile() { profile->setBaseLayerCount(_numberOfBaseLayersSlider->getValue()); - profile->setAddBaseLayersPumpingParameters(_addBasePumpCheckbox->isChecked()); + profile->setBaseLayersPumpingEnabled(_addBasePumpCheckbox->isChecked()); if(_addBasePumpCheckbox->isChecked()) { PrintPumpingParameters baseParams; @@ -654,7 +654,7 @@ void AdvancedTab::updatePrintProfile() { profile->setBaseLayersPumpingParameters(baseParams); } - profile->setAddBaseLayersPumpingParameters(_addBodyPumpCheckbox->isChecked()); + profile->setBaseLayersPumpingEnabled(_addBodyPumpCheckbox->isChecked()); if(_addBodyPumpCheckbox->isChecked()) { PrintPumpingParameters bodyParams; @@ -684,7 +684,7 @@ void AdvancedTab::loadPrintProfile(PrintProfile const* profilePtr) { _numberOfBaseLayersSlider->setValue(profile->baseLayerCount()); - if(profile->whetherAddBaseLayersPumpingParameters()) + if(profile->baseLayersPumpingEnabled()) { PrintPumpingParameters baseParams = profile->baseLayersPumpingParameters(); @@ -698,7 +698,7 @@ void AdvancedTab::loadPrintProfile(PrintProfile const* profilePtr) { _powerLevelSlider->setValue(baseParams.powerLevel()); } - if(profile->whetherAddBodyLayersPumpingParameters()) + if(profile->bodyLayersPumpingEnabled()) { PrintPumpingParameters bodyParams = profile->baseLayersPumpingParameters(); diff --git a/src/printprofile.h b/src/printprofile.h index 4a3a8269..4f08912a 100644 --- a/src/printprofile.h +++ b/src/printprofile.h @@ -22,8 +22,8 @@ class PrintProfile: public QObject { printProfile->_name = this->_name; printProfile->_baseLayerCount = this->_baseLayerCount; - printProfile->_addBaseLayersPumpingParameters = this->_addBaseLayersPumpingParameters; - printProfile->_addBodyLayersPumpingParameters = this->_addBodyLayersPumpingParameters; + printProfile->_baseLayersPumpingEnabled = this->_baseLayersPumpingEnabled; + printProfile->_bodyLayersPumpingEnabled = this->_bodyLayersPumpingEnabled; printProfile->_baseLayersPumpingParameters = this->_baseLayersPumpingParameters; printProfile->_bodyLayersPumpingParameters = this->_bodyLayersPumpingParameters; @@ -51,12 +51,12 @@ class PrintProfile: public QObject { return _bodyLayersPumpingParameters; } - bool whetherAddBaseLayersPumpingParameters() { - return _addBaseLayersPumpingParameters; + bool baseLayersPumpingEnabled() { + return _baseLayersPumpingEnabled; } - bool whetherAddBodyLayersPumpingParameters() { - return _addBodyLayersPumpingParameters; + bool bodyLayersPumpingEnabled() { + return _bodyLayersPumpingEnabled; } // @@ -80,12 +80,12 @@ class PrintProfile: public QObject { _bodyLayersPumpingParameters = newParameters; } - void setAddBaseLayersPumpingParameters(bool value) { - _addBaseLayersPumpingParameters = value; + void setBaseLayersPumpingEnabled(bool value) { + _baseLayersPumpingEnabled = value; } - void setAddBodyLayersPumpingParameters(bool value) { - _addBodyLayersPumpingParameters = value; + void setBodyLayersPumpingEnabled(bool value) { + _bodyLayersPumpingEnabled = value; } protected: @@ -93,9 +93,9 @@ class PrintProfile: public QObject { QString _name; int _baseLayerCount; - bool _addBaseLayersPumpingParameters; + bool _baseLayersPumpingEnabled; PrintPumpingParameters _baseLayersPumpingParameters; - bool _addBodyLayersPumpingParameters; + bool _bodyLayersPumpingEnabled; PrintPumpingParameters _bodyLayersPumpingParameters; signals: diff --git a/src/profilesjsonparser.h b/src/profilesjsonparser.h index e1b9b77f..fc219614 100644 --- a/src/profilesjsonparser.h +++ b/src/profilesjsonparser.h @@ -64,10 +64,10 @@ class ProfilesJsonParser { PrintPumpingParameters params = _parsePrintPumpingParameters(baseLayersPumpingParameters.toObject()); printProfile->setBaseLayersPumpingParameters(params); - printProfile->setAddBaseLayersPumpingParameters(true); + printProfile->setBaseLayersPumpingEnabled(true); } else - printProfile->setAddBaseLayersPumpingParameters(false); + printProfile->setBaseLayersPumpingEnabled(false); debug( " + get bodyLayersPumpingParameters\n" ); auto bodyLayersPumpingParameters = obj["bodyLayersPumpingParameters"]; @@ -76,10 +76,10 @@ class ProfilesJsonParser { PrintPumpingParameters params = _parsePrintPumpingParameters(bodyLayersPumpingParameters.toObject()); printProfile->setBodyLayersPumpingParameters(params); - printProfile->setAddBodyLayersPumpingParameters(true); + printProfile->setBodyLayersPumpingEnabled(true); } else - printProfile->setAddBodyLayersPumpingParameters(false); + printProfile->setBodyLayersPumpingEnabled(false); debug( " + push back to list\n" ); profilesList->push_back(printProfile); @@ -122,13 +122,13 @@ class ProfilesJsonParser json["name"] = profile->profileName(); json["baseLayerCount"] = profile->baseLayerCount(); - if(profile->whetherAddBaseLayersPumpingParameters()) + if(profile->baseLayersPumpingEnabled()) { QJsonObject baseParameters = _serializePrintPumpingParameters(profile->baseLayersPumpingParameters()); json["baseLayersPumpingParameters"]= baseParameters; } - if(profile->whetherAddBodyLayersPumpingParameters()) + if(profile->bodyLayersPumpingEnabled()) { QJsonObject bodyParameters = _serializePrintPumpingParameters(profile->bodyLayersPumpingParameters()); json["bodyLayersPumpingParameters"]= bodyParameters; From 48ce1c1394f8947639ca4a4f4b0df9894aa3f0c5 Mon Sep 17 00:00:00 2001 From: sagittarius89 Date: Thu, 6 Feb 2020 17:47:19 +0100 Subject: [PATCH 82/89] new operations: update, rename profile --- src/profilestab.cpp | 151 ++++++++++++++++++++++++++++++++++++++++++-- src/profilestab.h | 6 ++ 2 files changed, 151 insertions(+), 6 deletions(-) diff --git a/src/profilestab.cpp b/src/profilestab.cpp index f6133925..fff82dd7 100644 --- a/src/profilestab.cpp +++ b/src/profilestab.cpp @@ -38,6 +38,14 @@ ProfilesTab::ProfilesTab( QWidget* parent ): TabBase( parent ) { _newProfile->setFixedSize( MainButtonSize ); _newProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + _renameProfile->setFont(fontAwesome); + _renameProfile->setFixedSize( MainButtonSize ); + _renameProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + _overwriteProfile->setFont(fontAwesome); + _overwriteProfile->setFixedSize( MainButtonSize ); + _overwriteProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + _deleteProfile->setFont(fontAwesome); _deleteProfile->setFixedSize( MainButtonSize ); _deleteProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); @@ -51,6 +59,8 @@ ProfilesTab::ProfilesTab( QWidget* parent ): TabBase( parent ) { QObject::connect( _importParams, &QPushButton::clicked, this, &ProfilesTab::importParams_clicked ); QObject::connect( _exportParams, &QPushButton::clicked, this, &ProfilesTab::exportParams_clicked ); QObject::connect( _newProfile, &QPushButton::clicked, this, &ProfilesTab::newProfile_clicked ); + QObject::connect( _renameProfile, &QPushButton::clicked, this, &ProfilesTab::renamePProfile_clicked ); + QObject::connect( _overwriteProfile, &QPushButton::clicked, this, &ProfilesTab::updateProfile_clicked ); QObject::connect( _deleteProfile, &QPushButton::clicked, this, &ProfilesTab::deleteProfile_clicked ); QObject::connect( _loadProfile, &QPushButton::clicked, this, &ProfilesTab::loadProfile_clicked ); @@ -60,11 +70,19 @@ ProfilesTab::ProfilesTab( QWidget* parent ): TabBase( parent ) { _exportParams, cpyProfilesUsbBox, cpyStlFilesUsbBox, - _importParams, - _newProfile, - _loadProfile, - _deleteProfile, - nullptr + nullptr, + WrapWidgetsInHBox( + WrapWidgetsInVBox( + _importParams, + _loadProfile, + _deleteProfile + ), + WrapWidgetsInVBox( + _newProfile, + _renameProfile, + _overwriteProfile + ) + ) ), _profilesList ) @@ -98,7 +116,6 @@ void ProfilesTab::_setupProfilesList(QFont font) { _profilesList->setFont( font ); _profilesList->setVisible( true ); _profilesList->setSelectionBehavior( QAbstractItemView::SelectRows ); - _profilesList->setMinimumSize(QSize(MaximalRightHandPaneSize.width(), MaximalRightHandPaneSize.height())); _profilesList->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); } @@ -240,6 +257,61 @@ void ProfilesTab::newProfile_clicked(bool) } } +void ProfilesTab::renamePProfile_clicked(bool) +{ + Window* w = App::mainWindow(); + QRect r = w->geometry(); + + QMessageBox msgBox; + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.move(r.x()+100, r.y()+100); + msgBox.setFont(*_fontAwesome); + + InputDialog* inputDialog = new InputDialog(QString("Entry profile name: ")); + int ret = inputDialog->exec(); + + QString filename = inputDialog->getValue(); + if (ret && !filename.isEmpty()) + { + if(!_renamePProfile(filename)) + { + msgBox.setText("Something went wrong."); + msgBox.exec(); + } + else + { + msgBox.setText("Profile successfuly renamed."); + msgBox.exec(); + } + } +} + +void ProfilesTab::updateProfile_clicked(bool) +{ + Window* w = App::mainWindow(); + QRect r = w->geometry(); + + QMessageBox msgBox; + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.move(r.x()+100, r.y()+100); + msgBox.setFont(*_fontAwesome); + msgBox.setText("Are You sure to update selected profile?"); + int ret = msgBox.exec(); + + switch (ret) { + case QMessageBox::Yes: + if(!_updateProfile()) + { + msgBox.setText("Something went wrong."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } + break; + default: + break; + } +} + void ProfilesTab::deleteProfile_clicked(bool) { Window* w = App::mainWindow(); @@ -310,6 +382,73 @@ bool ProfilesTab::_createNewProfile(QString profileName) return true; } +bool ProfilesTab::_renamePProfile(QString profileName) +{ + QModelIndex index = _profilesList->currentIndex(); + + QString oldName = index.data(Qt::DisplayRole).toString(); + _model->setData(index, QVariant(profileName)); + + QVector* c = (QVector*)_printProfileManager->profiles(); + auto iter = std::find_if( c->begin( ), c->end( ), [&oldName] ( PrintProfile* p ) { return oldName == p->profileName( ); } ); + PrintProfile* profile = ( iter != c->end( ) ) ? *iter : nullptr; + + profile->setProfileName(profileName); + + ProfilesJsonParser::saveProfiles(_printProfileManager->profiles()); + + return true; +} + +bool ProfilesTab::_updateProfile() +{ + QModelIndex index = _profilesList->currentIndex(); + QString profileName = index.data(Qt::DisplayRole).toString(); + + QVector* c = (QVector*)_printProfileManager->profiles(); + auto iter = std::find_if( c->begin( ), c->end( ), [&profileName] ( PrintProfile* p ) { return profileName == p->profileName( ); } ); + PrintProfile* profile = ( iter != c->end( ) ) ? *iter : nullptr; + PrintProfile* activeProfile = (PrintProfile*)_printProfileManager->activeProfile(); + + profile->setBaseLayerCount(activeProfile->baseLayerCount()); + profile->setBaseLayersPumpingEnabled(activeProfile->baseLayersPumpingEnabled()); + profile->setBodyLayersPumpingEnabled(activeProfile->bodyLayersPumpingEnabled()); + + PrintPumpingParameters baseParams = activeProfile->baseLayersPumpingParameters(); + PrintPumpingParameters newBaseParams; + + newBaseParams.setPumpUpDistance( baseParams.pumpUpDistance() ); + newBaseParams.setPumpUpTime( baseParams.pumpUpTime() ); + newBaseParams.setPumpUpPause( baseParams.pumpUpPause() ); + newBaseParams.setPumpDownPause( baseParams.pumpDownPause() ); + newBaseParams.setNoPumpUpVelocity( baseParams.noPumpUpVelocity() ); + newBaseParams.setPumpEveryNthLayer( baseParams.pumpEveryNthLayer() ); + newBaseParams.setLayerThickness( baseParams.layerThickness() ); + newBaseParams.setLayerExposureTime( baseParams.layerExposureTime() ); + newBaseParams.setPowerLevel( baseParams.powerLevel() ); + + profile->setBaseLayersPumpingParameters(newBaseParams); + + PrintPumpingParameters bodyParams = activeProfile->bodyLayersPumpingParameters(); + PrintPumpingParameters newBodyParams; + + newBodyParams.setPumpUpDistance( bodyParams.pumpUpDistance() ); + newBodyParams.setPumpUpTime( bodyParams.pumpUpTime() ); + newBodyParams.setPumpUpPause( bodyParams.pumpUpPause() ); + newBodyParams.setPumpDownPause( bodyParams.pumpDownPause() ); + newBodyParams.setNoPumpUpVelocity( bodyParams.noPumpUpVelocity() ); + newBodyParams.setPumpEveryNthLayer( bodyParams.pumpEveryNthLayer() ); + newBodyParams.setLayerThickness( bodyParams.layerThickness() ); + newBodyParams.setLayerExposureTime( bodyParams.layerExposureTime() ); + newBodyParams.setPowerLevel( bodyParams.powerLevel() ); + + profile->setBaseLayersPumpingParameters(bodyParams); + + ProfilesJsonParser::saveProfiles(_printProfileManager->profiles()); + + return true; +} + bool ProfilesTab::_deletePrintProfile() { QModelIndex index = _profilesList->currentIndex(); diff --git a/src/profilestab.h b/src/profilestab.h index 7a37bd6b..48afb2ad 100644 --- a/src/profilestab.h +++ b/src/profilestab.h @@ -27,6 +27,8 @@ class ProfilesTab: public TabBase { QPushButton* _importParams { new QPushButton("Import") }; QPushButton* _exportParams { new QPushButton("Export") }; QPushButton* _newProfile { new QPushButton("Create profile") }; + QPushButton* _renameProfile { new QPushButton("Rename profile") }; + QPushButton* _overwriteProfile { new QPushButton("Update profile") }; QPushButton* _deleteProfile { new QPushButton("Delete selected") }; QPushButton* _loadProfile { new QPushButton("Load selected") }; QCheckBox* _cpyProfilesUsb { new QCheckBox("Copy profiles to USB") }; @@ -37,6 +39,8 @@ class ProfilesTab: public TabBase { void _setupProfilesList(QFont font); bool _createNewProfile(QString profileName); + bool _renamePProfile(QString profileName); + bool _updateProfile(); bool _deletePrintProfile(); bool _loadPrintProfile(); signals: @@ -56,6 +60,8 @@ private slots: void importParams_clicked(bool); void exportParams_clicked(bool); void newProfile_clicked(bool); + void renamePProfile_clicked(bool); + void updateProfile_clicked(bool); void deleteProfile_clicked(bool); void loadProfile_clicked(bool); From 3f6bce12abb45639c8bdbdeafdd9185097f26592 Mon Sep 17 00:00:00 2001 From: Mateusz Gancarczyk Date: Fri, 14 Feb 2020 15:10:38 +0000 Subject: [PATCH 83/89] UPDATE: Keyboard review --- src/inputdialog.h | 34 +++++++++---- src/key.cpp | 21 +++++--- src/key.h | 10 +++- src/keyboard.cpp | 120 +++++++++++++++++++++++++------------------- src/keyboard.h | 3 ++ src/profilestab.cpp | 7 ++- 6 files changed, 123 insertions(+), 72 deletions(-) diff --git a/src/inputdialog.h b/src/inputdialog.h index d25c144e..120b80b2 100644 --- a/src/inputdialog.h +++ b/src/inputdialog.h @@ -5,16 +5,18 @@ #include "window.h" #include +#include + + class InputDialog: public QDialog { Q_OBJECT private: - Keyboard* _keyboard { new Keyboard(this) }; - QLabel* _message { new QLabel }; - QLineEdit* _input { new QLineEdit }; - QPushButton* _okButton { new QPushButton("Ok") }; - QPushButton* _cancelButton { new QPushButton("Cancel")}; - - + Keyboard* _keyboard { new Keyboard(this) }; + QLabel* _message { new QLabel }; + QLineEdit* _input { new QLineEdit }; + QPushButton* _okButton { new QPushButton("Ok") }; + QPushButton* _cancelButton { new QPushButton("Cancel")}; + QWidget* _widget { new QWidget }; public: InputDialog() { } InputDialog(QString text) { @@ -23,13 +25,12 @@ class InputDialog: public QDialog { Window* win = App::mainWindow(); QRect r = win->geometry(); + move(r.x()+100, r.y()+100); resize(824, 400); - _message->setText(text); - + _message ->setText(text); _keyboard->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - _message->setFont(fontAwesome); _okButton->setFont(fontAwesome); _cancelButton->setFont(fontAwesome); @@ -39,6 +40,7 @@ class InputDialog: public QDialog { _cancelButton->setMinimumSize(QSize(245, 70)); setContentsMargins( { } ); setMinimumSize(QSize(580, 355)); + setLayout( WrapWidgetsInVBox( _message, @@ -50,6 +52,13 @@ class InputDialog: public QDialog { setModal(true); + // Window backgorund + // TODO: make it transparent + _widget->setStyleSheet("background-color: rgba(255, 255, 255, 10%);"); + _widget->setFixedSize(QSize(1024,600)); + _widget->setWindowOpacity(0.5); + _widget->show(); + QWidget::connect(_keyboard, &Keyboard::keyPressed, this, &InputDialog::keyPressed); QWidget::connect(_keyboard, &Keyboard::backspacePressed, this, &InputDialog::backspacePressed); QWidget::connect(_okButton, &QPushButton::clicked, this, &InputDialog::oklCLicked_clicked); @@ -62,6 +71,7 @@ class InputDialog: public QDialog { delete _input; delete _okButton; delete _cancelButton; + delete _widget; } QString getValue() { @@ -87,17 +97,21 @@ class InputDialog: public QDialog { } void cancelCLicked_clicked(bool) { + _widget->hide(); this->setResult(QDialog::Rejected); this->reject(); this->close(); } void oklCLicked_clicked(bool) { + _widget->hide(); this->setResult(QDialog::Accepted); this->accept(); this->close(); } + + signals: void okclicked(); }; diff --git a/src/key.cpp b/src/key.cpp index 55a88c38..44cc9340 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,4 +1,5 @@ #include +#include #include "key.h" @@ -7,7 +8,7 @@ key::key(QString t,QObject *parent) : QObject(parent) text = t; X =10; Y =10; - W =t.length()*4 + 78; + W =82;//t.length()*4 + 78; H =62; pressed = false; } @@ -43,10 +44,15 @@ void key::setPressed( bool b) pressed = b; } -void key::draw(QPainter *p,QStyle *style) { - +void key::draw(QPainter *p,QStyle *style) +{ QStyleOptionButton opt; + QFont font; + auto fontAwesome = ModifyFont( font, "FontAwesome", 20.0 ); + opt.palette = QPalette(QColor(0,51,102,127)); + p->setFont(fontAwesome); + /* if ( pressed ) { @@ -59,14 +65,13 @@ void key::draw(QPainter *p,QStyle *style) { if ( iconFilename !="" ) { - opt.icon = QIcon(iconFilename); - opt.iconSize=QSize(16,16); + opt.icon = QIcon(iconFilename); + opt.iconSize = QSize(16,16); } else { - if (text =="&") opt.text = "&&"; - else opt.text = text; + if (text =="&") opt.text = "&&"; + else opt.text = text; } style->drawControl(QStyle::CE_PushButton, &opt, p); - } diff --git a/src/key.h b/src/key.h index ed1b920b..86a4de7e 100644 --- a/src/key.h +++ b/src/key.h @@ -9,11 +9,19 @@ #include +#define KEYS_TYPE 4 + +#define LOWERCASE 0 +#define NUMBER 1 +#define UPPERCASE 2 +#define PUNCTUATION 3 + + class key : public QObject { Q_OBJECT public: - explicit key(QString t,QObject *parent = 0); + explicit key(QString t, QObject *parent = 0); void setX(int ); void setY(int ); diff --git a/src/keyboard.cpp b/src/keyboard.cpp index 790f3793..2041eac7 100644 --- a/src/keyboard.cpp +++ b/src/keyboard.cpp @@ -1,45 +1,36 @@ #include #include -#include +#include #include "key.h" #include "keyboard.h" -#define KEYS_TYPE 4 - -// TODO make an enum -#define LOWERCASE 0 -#define NUMBER 1 -#define UPPERCASE 2 -#define PUNCTUATION 3 // Declaration off the differente keys... - - const char *en_lower_keymap[] = { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a", "s", "d", "f", "g", "h", "j", "k", "l",";", - "Caps", "z", "x", "c", "v", "b", "n", "m",",", "backspace", + "caps", "z", "x", "c", "v", "b", "n", "m",",", "back\nspace", "123", ".", "space", "@" }; const char *en_upper_keymap[] = { "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "A", "S", "D", "F", "G", "H", "J", "K", "L",";", - "Caps", "Z", "X", "C", "V", "B", "N", "M",",", "backspace", + "caps", "Z", "X", "C", "V", "B", "N", "M",",", "back\nspace", "123", ".", "space", "@" }; const char *en_number_keymap[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "/", ":", ";", "(", ")", "€", "&", "@", "\"", - "#+=", ".", ",", "?", "!", "'", "+","\\","%","backspace", + "#+=", ".", ",", "?", "!", "'", "+","\\","%","back\nspace", "ABC", ",", "space", "." }; const char *en_punctuation_keymap[] = { "[", "]", "{", "}", "#", "%", "^", "*", "+", "=", "_", "\\", "|", "~", "<", ">", "=","$", "@", "\"", - "123", ".", ",", "?", "!", "'","/",":",";", "backspace", + "123", ".", ",", "?", "!", "'","/",":",";", "back\nspace", "ABC", ",", "space", "." }; @@ -51,7 +42,8 @@ const int row_keymap[] = { 3,3,3,3,3 }; -const int nbkey = sizeof(en_lower_keymap)/ sizeof(char *); + +const int nbkey = sizeof(en_lower_keymap)/sizeof(char *); Keyboard::Keyboard(QWidget *p) : QWidget(p) { @@ -67,38 +59,48 @@ Keyboard::Keyboard(QWidget *p) : QWidget(p) keys[n] = QVector< key * >(nbkey); } - initKeys(LOWERCASE,en_lower_keymap); - initKeys(NUMBER,en_number_keymap); - initKeys(UPPERCASE,en_upper_keymap); - initKeys(PUNCTUATION,en_punctuation_keymap); + initKeys(LOWERCASE, en_lower_keymap); + initKeys(NUMBER, en_number_keymap); + initKeys(UPPERCASE, en_upper_keymap); + initKeys(PUNCTUATION, en_punctuation_keymap); } -void Keyboard::initKeys( int indexArraykeys,const char *keymap[]) +void Keyboard::initKeys( int indexArraykeys, const char *keymap[]) { - int row = 0; - for(int n=0; n< nbkey; n++) + int xCoor = 0; + int yCoor = 0; + + for(int n=0; n0) + // qDebug() << "n="<< n; + + if ( n > 0 ) { - //if (keymap[n] == "return" ) keys[indexArraykeys][n]->setIconFile(":/img/img/enter.png"); - if (0 == strcasecmp( keymap[n], "backspace")) keys[indexArraykeys][n]->W=95; - if (0 == strcasecmp( keymap[n], "Caps" )) keys[indexArraykeys][n]->W=70; - if (0 == strcasecmp( keymap[n], "space" )) keys[indexArraykeys][n]->W=570; + // Special length button + if (0 == strcasecmp( keymap[n], "space" )) keys[indexArraykeys][n]->W=574; + // Calculat e new ccooridinate of button if (row_keymap[n-1]!=row_keymap[n]) { - row ++; - keys[indexArraykeys][n]->setX(0); //offetrows[row]); - } else { - keys[indexArraykeys][n]->setX(keys[indexArraykeys][n-1]->X + keys[indexArraykeys][n-1]->W); + xCoor = 2; } - keys[indexArraykeys][n]->setY(row_keymap[n]*62); - } else { - // keys[indexArraykeys][n]->setX(offetrows[0]); - keys[indexArraykeys][n]->setX(0); - keys[indexArraykeys][n]->setY(row_keymap[n]*62); + else + { + xCoor = keys[indexArraykeys][n-1]->X + keys[indexArraykeys][n-1]->W; + } + yCoor = row_keymap[n] * DEFAULT_YSIZE_BUTTON; + } + else + { + xCoor = 2; + yCoor = 0; } + + keys[indexArraykeys][n]->setX(xCoor); + keys[indexArraykeys][n]->setY(yCoor); + + //qDebug() <<"N: " << n << "\t\t" <text << "\t\t W= " << keys[indexArraykeys][n]->W; } } @@ -111,27 +113,40 @@ void Keyboard::initTooltip() tooltip->setFont(serifFont); tooltip->setAlignment(Qt::AlignCenter); + // Bug fix of first pres button + tooltip->show(); + tooltip->hide(); } -void Keyboard::mousePressEvent(QMouseEvent * e) { +void Keyboard::mousePressEvent(QMouseEvent * e) +{ + //qDebug() << "Mouse press event"; + QWidget::mousePressEvent(e); + QPoint pos = e->pos(); setKeyPressed( findKey(pos), pos ); } -void Keyboard::mouseMoveEvent(QMouseEvent * e) { +void Keyboard::mouseMoveEvent(QMouseEvent * e) +{ + //qDebug() << "Mouse move event"; QPoint pos = e->pos(); - if (currentKey != 0x0 && !currentKey->getRect().contains(pos)) { + if (currentKey != 0x0 && !currentKey->getRect().contains(pos)) + { tooltip->hide(); currentKey->setPressed(false); this->repaint(); } setKeyPressed( findKey(pos), pos ); - } +} -void Keyboard::mouseReleaseEvent(QMouseEvent *e) { +void Keyboard::mouseReleaseEvent(QMouseEvent *e) +{ + //qDebug() << "Mouse release event"; QPoint pos = e->pos(); tooltip->hide(); + key *k= findKey( pos ); if (k != 0x0 ) { @@ -148,8 +163,9 @@ void Keyboard::mouseReleaseEvent(QMouseEvent *e) { return; } - if (k->text=="Caps") - { if ( uppercase ==false) + if (k->text=="caps") + { + if ( uppercase ==false) { currentindexkeyboard = UPPERCASE; uppercase = true; @@ -170,27 +186,28 @@ void Keyboard::mouseReleaseEvent(QMouseEvent *e) { } else { - if ( k->text =="backspace" ) + if ( k->text =="back\nspace" ) { emit backspacePressed(); return; } - - if (k->text=="enter") + else if (k->text=="enter") { emit returnPressed(); return; } - if ( k->text == "space") + else if ( k->text == "space") { - emit keyPressed(" "); - } else + emit keyPressed(" "); + } + else { emit keyPressed(k->text); } } } } + key *Keyboard::findKey(QPoint p) { foreach (key *k, keys[currentindexkeyboard]) @@ -214,10 +231,11 @@ void Keyboard::setKeyPressed( key *k, QPoint /*pos*/) QPoint p = QWidget::mapToGlobal(this->pos() +QPoint( k->X, k->Y)); tooltip->setGeometry(p.x(),p.y()-50,50, 50); tooltip->setText(k->text); - tooltip->show(); + tooltip->show(); // this line makes bug with first relase event } -void Keyboard::paintEvent(QPaintEvent*) { +void Keyboard::paintEvent(QPaintEvent*) +{ QPainter painter(this); foreach (key *k, keys[currentindexkeyboard]) { diff --git a/src/keyboard.h b/src/keyboard.h index d5ec057a..4eb0d062 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -5,6 +5,9 @@ #include #include #include + +#define DEFAULT_YSIZE_BUTTON 62 + class key; class Keyboard : public QWidget diff --git a/src/profilestab.cpp b/src/profilestab.cpp index fff82dd7..2bda398d 100644 --- a/src/profilestab.cpp +++ b/src/profilestab.cpp @@ -230,13 +230,15 @@ void ProfilesTab::newProfile_clicked(bool) msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); - /*QInputDialog inputDialog; + /* + QInputDialog inputDialog; inputDialog.setModal(true); inputDialog.move(r.x()+100, r.y()+100); inputDialog.setFont(*_fontAwesome); inputDialog.setFocus(Qt::FocusReason::ActiveWindowFocusReason); inputDialog.setLabelText("Enter a profile name: "); - int ret = inputDialog.exec();*/ + int ret = inputDialog.exec(); + */ InputDialog* inputDialog = new InputDialog(QString("Entry profile name: ")); int ret = inputDialog->exec(); @@ -255,6 +257,7 @@ void ProfilesTab::newProfile_clicked(bool) msgBox.exec(); } } + } void ProfilesTab::renamePProfile_clicked(bool) From f004ea1a76ce4b393d1182be64d188c0f7f6e001 Mon Sep 17 00:00:00 2001 From: Mateusz Gancarczyk Date: Fri, 21 Feb 2020 11:16:10 +0000 Subject: [PATCH 84/89] Update print protile tabs: =- remove bug with enabled/disable buttons when no profile is selected --, center all messege box, code refactoring --- print-profiles/print-profiles.json | 76 ++++++++++++++++++++++----- src/constants.cpp | 4 +- src/profilesjsonparser.h | 9 +++- src/profilestab.cpp | 84 ++++++++++++++++-------------- src/profilestab.h | 5 +- 5 files changed, 124 insertions(+), 54 deletions(-) diff --git a/print-profiles/print-profiles.json b/print-profiles/print-profiles.json index 814ab89d..c6243f30 100644 --- a/print-profiles/print-profiles.json +++ b/print-profiles/print-profiles.json @@ -1,28 +1,80 @@ [ { - "name": "exampleProfile", "baseLayerCount": 1, "baseLayersPumpingParameters": { - "pumpUpDistance": 2.00, - "pumpUpTime": 600, - "pumpUpPause": 2000, - "pumpDownPause": 4000, + "layerExposureTime": 1000, + "layerThickness": 100, "noPumpUpVelocity": 200, + "powerLevel": 50, + "pumpDownPause": 4000, "pumpEveryNthLayer": 1, - "layerThickness": 100, - "layerExposureTime": 1000, - "powerLevel": 50.0 + "pumpUpDistance": 2, + "pumpUpPause": 2000, + "pumpUpTime": 600 }, "bodyLayersPumpingParameters": { - "pumpUpDistance": 2.00, - "pumpUpTime": 600, + "layerExposureTime": 1000, + "layerThickness": 100, + "noPumpUpVelocity": 200, + "powerLevel": 50, + "pumpDownPause": 4000, + "pumpEveryNthLayer": 1, + "pumpUpDistance": 2, "pumpUpPause": 2000, + "pumpUpTime": 600 + }, + "name": "profile_1" + }, + { + "baseLayerCount": 1, + "baseLayersPumpingParameters": { + "layerExposureTime": 1000, + "layerThickness": 100, + "noPumpUpVelocity": 200, + "powerLevel": 50, "pumpDownPause": 4000, + "pumpEveryNthLayer": 1, + "pumpUpDistance": 2, + "pumpUpPause": 2000, + "pumpUpTime": 600 + }, + "bodyLayersPumpingParameters": { + "layerExposureTime": 1000, + "layerThickness": 100, "noPumpUpVelocity": 200, + "powerLevel": 50, + "pumpDownPause": 4000, "pumpEveryNthLayer": 1, + "pumpUpDistance": 2, + "pumpUpPause": 2000, + "pumpUpTime": 600 + }, + "name": "profile_2" + }, + { + "baseLayerCount": 1, + "baseLayersPumpingParameters": { + "layerExposureTime": 1000, "layerThickness": 100, + "noPumpUpVelocity": 200, + "powerLevel": 50, + "pumpDownPause": 4000, + "pumpEveryNthLayer": 1, + "pumpUpDistance": 2, + "pumpUpPause": 2000, + "pumpUpTime": 600 + }, + "bodyLayersPumpingParameters": { "layerExposureTime": 1000, - "powerLevel": 50.0 - } + "layerThickness": 100, + "noPumpUpVelocity": 200, + "powerLevel": 50, + "pumpDownPause": 4000, + "pumpEveryNthLayer": 1, + "pumpUpDistance": 2, + "pumpUpPause": 2000, + "pumpUpTime": 600 + }, + "name": "profile_3" } ] diff --git a/src/constants.cpp b/src/constants.cpp index a0ebfd64..069f20ce 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -24,8 +24,10 @@ QString const ShepherdPath { "/usr/share/lig QString const SlicedSvgFileName { "sliced.svg" }; QString const StlModelLibraryPath { "/var/lib/lightfield/model-library" }; QString const UpdatesRootPath { "/var/lib/lightfield/software-updates" }; + //QString const PrintProfilesPath { "/var/lib/lightfield/print-profiles.json" }; -QString const PrintProfilesPath { "/home/lumen/Volumetric/LightField/print-profiles/print-profiles.json" }; +QString const PrintProfilesPath { "/home/lumen/Volumetric/LightField/print-profiles/print-profiles.json"}; +//QString const PrintProfilesPath { "/home/mateusz/LightField/print-profiles/print-profiles.json"}; //QString const PrintProfilesSchemaPath { "/var/lib/lightfield/print-profiles-schema.json" }; diff --git a/src/profilesjsonparser.h b/src/profilesjsonparser.h index fc219614..d96d3b6b 100644 --- a/src/profilesjsonparser.h +++ b/src/profilesjsonparser.h @@ -2,6 +2,7 @@ #define PROFILESJSONPARSER_H #include "printprofile.h" +#include #define DEBUG_LOGS @@ -10,11 +11,17 @@ class ProfilesJsonParser public: static QVector* loadProfiles() { QVector* profilesList = new QVector(); - debug( "ProfilesJsonParser::loadProfiles: opening file\n" ); QFile jsonFile(PrintProfilesPath); if(!jsonFile.exists()) { + // Show message when not found a profile in path + QMessageBox msgBox; + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText("File print profile does not exist\n" ); + msgBox.open(); + msgBox.exec(); + debug( "ProfilesJsonParser::loadProfiles: File %s does not exist\n", PrintProfilesPath.toUtf8( ).data( ) ); return profilesList; } diff --git a/src/profilestab.cpp b/src/profilestab.cpp index 2bda398d..e90ea9a8 100644 --- a/src/profilestab.cpp +++ b/src/profilestab.cpp @@ -23,6 +23,7 @@ ProfilesTab::ProfilesTab( QWidget* parent ): TabBase( parent ) { cpyStlFilesUsbBox->setLayout(WrapWidgetsInVBox(_cpyStlFilesUsb, nullptr)); cpyStlFilesUsbBox->setContentsMargins( { } ); + _cpyProfilesUsb->setFont(fontAwesome); _cpyStlFilesUsb->setFont(fontAwesome); @@ -51,9 +52,11 @@ ProfilesTab::ProfilesTab( QWidget* parent ): TabBase( parent ) { _deleteProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); _loadProfile->setFont(fontAwesome); - _loadProfile->setFixedSize( MainButtonSize ); + _loadProfile->setFixedSize( MainButtonSize ); _loadProfile->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + // Default disabled all button no profile is selected + _enableButtonProfile(false); _setupProfilesList(fontAwesome); QObject::connect( _importParams, &QPushButton::clicked, this, &ProfilesTab::importParams_clicked ); @@ -63,6 +66,7 @@ ProfilesTab::ProfilesTab( QWidget* parent ): TabBase( parent ) { QObject::connect( _overwriteProfile, &QPushButton::clicked, this, &ProfilesTab::updateProfile_clicked ); QObject::connect( _deleteProfile, &QPushButton::clicked, this, &ProfilesTab::deleteProfile_clicked ); QObject::connect( _loadProfile, &QPushButton::clicked, this, &ProfilesTab::loadProfile_clicked ); + QObject::connect( _profilesList, &QListView::clicked, this, &ProfilesTab::itemClicked ); setLayout( WrapWidgetsInHBox( @@ -93,6 +97,20 @@ ProfilesTab::~ProfilesTab( ) { /*empty*/ } + +void ProfilesTab::itemClicked(QModelIndex const& index) +{ + _enableButtonProfile(true); +} + + +void ProfilesTab::_enableButtonProfile( bool enabled ) { + _overwriteProfile->setEnabled(enabled); + _deleteProfile->setEnabled(enabled); + _loadProfile->setEnabled(enabled); + _renameProfile->setEnabled(enabled); +} + void ProfilesTab::tab_uiStateChanged( TabIndex const sender, UiState const state ) { debug( "+ ProfilesTab::tab_uiStateChanged: from %sTab: %s => %s\n", ToString( sender ), ToString( _uiState ), ToString( state ) ); _uiState = state; @@ -136,6 +154,7 @@ void ProfilesTab::setPrintProfileManager( PrintProfileManager* printProfileManag if( !i ) { firstItem=item; _printProfileManager->setActiveProfile(profile->profileName()); + _enableButtonProfile(true); } _model->setItem( i, 0, item ); @@ -150,16 +169,14 @@ void ProfilesTab::setPrintProfileManager( PrintProfileManager* printProfileManag void ProfilesTab::importParams_clicked(bool) { Window* w = App::mainWindow(); - QRect r = w->geometry(); QMessageBox msgBox; - msgBox.setText("Are You sure to import all profiles from USB memory stick?"); + // because when two line text causes wrong value of sizeHint().width() and sizeHint().height() + msgBox.setText("

Are You sure to import all profiles
from USB memory stick?
"); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); - msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); - - + msgBox.move( w->x() + (w->width() - msgBox.sizeHint().width())/2, w->y() + (w->height() - msgBox.sizeHint().height())/2 ); int ret = msgBox.exec(); switch (ret) { @@ -168,14 +185,16 @@ void ProfilesTab::importParams_clicked(bool) // @todo how to pass checkboxes values? what filename means? if(!_printProfileManager->importProfiles(nullptr)) { - msgBox.setText("Something went wrong. Make sure memory stick is inserted into USB drive."); + msgBox.setText("
Something went wrong. Make sure memory
stick is inserted into USB drive.
"); msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } else { msgBox.setText("Import successed."); msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } break; @@ -187,31 +206,30 @@ void ProfilesTab::importParams_clicked(bool) void ProfilesTab::exportParams_clicked(bool) { Window* w = App::mainWindow(); - QRect r = w->geometry(); QMessageBox msgBox; - msgBox.setText("Are You sure to export all profiles to USB memory stick?"); + msgBox.setText("
Are You sure to export all profiles
to USB memory stick?
"); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); - msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); - + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); int ret = msgBox.exec(); switch (ret) { case QMessageBox::Yes: - // @todo how to pass checkboxes values? what filename means? if(!_printProfileManager->exportProfiles(nullptr)) { msgBox.setText("Something went wrong. Make sure memory stick is inserted into USB drive."); msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } else { msgBox.setText("Import successed."); msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } break; @@ -223,53 +241,40 @@ void ProfilesTab::exportParams_clicked(bool) void ProfilesTab::newProfile_clicked(bool) { Window* w = App::mainWindow(); - QRect r = w->geometry(); QMessageBox msgBox; msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); - /* - QInputDialog inputDialog; - inputDialog.setModal(true); - inputDialog.move(r.x()+100, r.y()+100); - inputDialog.setFont(*_fontAwesome); - inputDialog.setFocus(Qt::FocusReason::ActiveWindowFocusReason); - inputDialog.setLabelText("Enter a profile name: "); - int ret = inputDialog.exec(); - */ - InputDialog* inputDialog = new InputDialog(QString("Entry profile name: ")); int ret = inputDialog->exec(); QString filename = inputDialog->getValue(); + if (ret && !filename.isEmpty()) { - if(!_createNewProfile(filename)) + if(!_createNewProfile(filename)) { msgBox.setText("Something went wrong."); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } else { msgBox.setText("Profile successfuly added."); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } } - } void ProfilesTab::renamePProfile_clicked(bool) { Window* w = App::mainWindow(); - QRect r = w->geometry(); QMessageBox msgBox; msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); - InputDialog* inputDialog = new InputDialog(QString("Entry profile name: ")); int ret = inputDialog->exec(); @@ -279,11 +284,13 @@ void ProfilesTab::renamePProfile_clicked(bool) if(!_renamePProfile(filename)) { msgBox.setText("Something went wrong."); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } else { msgBox.setText("Profile successfuly renamed."); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } } @@ -292,13 +299,12 @@ void ProfilesTab::renamePProfile_clicked(bool) void ProfilesTab::updateProfile_clicked(bool) { Window* w = App::mainWindow(); - QRect r = w->geometry(); QMessageBox msgBox; msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); msgBox.setText("Are You sure to update selected profile?"); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); int ret = msgBox.exec(); switch (ret) { @@ -318,13 +324,13 @@ void ProfilesTab::updateProfile_clicked(bool) void ProfilesTab::deleteProfile_clicked(bool) { Window* w = App::mainWindow(); - QRect r = w->geometry(); + QMessageBox msgBox; msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); msgBox.setText("Are You sure to delete selected profile?"); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); int ret = msgBox.exec(); switch (ret) { @@ -344,13 +350,13 @@ void ProfilesTab::deleteProfile_clicked(bool) void ProfilesTab::loadProfile_clicked(bool) { Window* w = App::mainWindow(); - QRect r = w->geometry(); QMessageBox msgBox; msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.move(r.x()+100, r.y()+100); msgBox.setFont(*_fontAwesome); msgBox.setText("Are You sure to load selected profile?"); + //qDebug() << w->width() << " " << msgBox.sizeHint().width() << " " << w->height() << " " << msgBox.sizeHint().height(); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); int ret = msgBox.exec(); switch (ret) { @@ -359,6 +365,7 @@ void ProfilesTab::loadProfile_clicked(bool) { msgBox.setText("Something went wrong."); msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.move( (w->width() - msgBox.sizeHint().width())/2, (w->height() - msgBox.sizeHint().height())/2 ); msgBox.exec(); } break; @@ -374,14 +381,10 @@ bool ProfilesTab::_createNewProfile(QString profileName) profileCopy->setProfileName(profileName); _printProfileManager->addProfile(profileCopy); - QStandardItem* item = new QStandardItem(profileName); item->setEditable(false); - _model->appendRow(item); - ProfilesJsonParser::saveProfiles(_printProfileManager->profiles()); - return true; } @@ -461,6 +464,9 @@ bool ProfilesTab::_deletePrintProfile() _model->removeRows(index.row(), 1); ProfilesJsonParser::saveProfiles(_printProfileManager->profiles()); + ProfilesJsonParser::loadProfiles(); + _enableButtonProfile(false); // Because no item is selected after deleted + return true; } diff --git a/src/profilestab.h b/src/profilestab.h index 48afb2ad..621fff48 100644 --- a/src/profilestab.h +++ b/src/profilestab.h @@ -28,7 +28,7 @@ class ProfilesTab: public TabBase { QPushButton* _exportParams { new QPushButton("Export") }; QPushButton* _newProfile { new QPushButton("Create profile") }; QPushButton* _renameProfile { new QPushButton("Rename profile") }; - QPushButton* _overwriteProfile { new QPushButton("Update profile") }; + QPushButton* _overwriteProfile { new QPushButton("Update profile") }; QPushButton* _deleteProfile { new QPushButton("Delete selected") }; QPushButton* _loadProfile { new QPushButton("Load selected") }; QCheckBox* _cpyProfilesUsb { new QCheckBox("Copy profiles to USB") }; @@ -43,6 +43,7 @@ class ProfilesTab: public TabBase { bool _updateProfile(); bool _deletePrintProfile(); bool _loadPrintProfile(); + void _enableButtonProfile( bool enabled ); signals: ; @@ -64,6 +65,8 @@ private slots: void updateProfile_clicked(bool); void deleteProfile_clicked(bool); void loadProfile_clicked(bool); + //void listChange_clicked(const QModelIndex ¤t, const QModelIndex &previous); + void itemClicked(const QModelIndex &index); }; From ec2e343bcc6d0795fb8e7aab1cf6abb2a8b3311f Mon Sep 17 00:00:00 2001 From: Mateusz Gancarczyk Date: Mon, 24 Feb 2020 10:35:09 +0000 Subject: [PATCH 85/89] Add default profile. Default profile not show in profile list. TODO: create profile list json file when not exist, add default profile when is not on the list, copy default profile from text file in /text folder --- text/defaultProfle.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 text/defaultProfle.txt diff --git a/text/defaultProfle.txt b/text/defaultProfle.txt new file mode 100644 index 00000000..e487f477 --- /dev/null +++ b/text/defaultProfle.txt @@ -0,0 +1,28 @@ +[ + { + "baseLayerCount": 1, + "baseLayersPumpingParameters": { + "layerExposureTime": 1000, + "layerThickness": 100, + "noPumpUpVelocity": 200, + "powerLevel": 10, + "pumpDownPause": 4000, + "pumpEveryNthLayer": 1, + "pumpUpDistance": 1, + "pumpUpPause": 2000, + "pumpUpTime": 600 + }, + "bodyLayersPumpingParameters": { + "layerExposureTime": 1000, + "layerThickness": 100, + "noPumpUpVelocity": 200, + "powerLevel": 10, + "pumpDownPause": 4000, + "pumpEveryNthLayer": 1, + "pumpUpDistance": 1, + "pumpUpPause": 2000, + "pumpUpTime": 600 + }, + "name": "default" + } +] From c2a2bf6508cb4006e150892cc2cc568cbbef862a Mon Sep 17 00:00:00 2001 From: Mateusz Gancarczyk Date: Fri, 28 Feb 2020 14:31:15 +0000 Subject: [PATCH 86/89] Add all unstaged files from last commit --- print-profiles/.print-profiles.json.swp | Bin 0 -> 12288 bytes print-profiles/print-profiles.json | 76 ++++-------------------- src/constants.cpp | 4 +- src/printprofile.h | 1 + src/profilesjsonparser.h | 3 +- src/profilestab.cpp | 31 +++++++++- text/text.qrc | 4 +- 7 files changed, 48 insertions(+), 71 deletions(-) create mode 100644 print-profiles/.print-profiles.json.swp diff --git a/print-profiles/.print-profiles.json.swp b/print-profiles/.print-profiles.json.swp new file mode 100644 index 0000000000000000000000000000000000000000..740722018d6736958290d594f3fa6a8a41f72f47 GIT binary patch literal 12288 zcmeI2KX21O7>D1oAyGhdi$Dt3frTb+_@k-|5>1gnp~}#7K_EHKH8I%dlYMSus=~+z zfUm&J2I5m77CJETB@h$hJ;#Y6mzFFHRemQuzWDsh-Mx>KA&R@s&cnymwVFfRE)y;F z?>`&X8}z zzyE(6~^N6{@5c7lA zJYv3%*FS^)uw@v40T_S*7=Qs7fB_hQf&XvdDOI^0=Etd5Ck)#JH=(0_8Mow~48(f% zj>Atq=i|je1a2nd&7qExG?v?bC^@z2IL^(##I^^%_d>}e8D&ZlorZdIPsZ7n8MN%I zHdV@~sLiR4-jPA%`6lB?bZNu<))t) zS9x}gMr}GHx=}C7Ye?*VebsNfu^UPwV_W;d#LZ!;KFF@lEc<-rYQ?s$o5)#Qi-~nH kdCNwWD#LeV>T@O(-zXjua_ELSknPcP_F{CW4(JK}0nSqgQ2+n{ literal 0 HcmV?d00001 diff --git a/print-profiles/print-profiles.json b/print-profiles/print-profiles.json index c6243f30..814ab89d 100644 --- a/print-profiles/print-profiles.json +++ b/print-profiles/print-profiles.json @@ -1,80 +1,28 @@ [ { + "name": "exampleProfile", "baseLayerCount": 1, "baseLayersPumpingParameters": { - "layerExposureTime": 1000, - "layerThickness": 100, - "noPumpUpVelocity": 200, - "powerLevel": 50, - "pumpDownPause": 4000, - "pumpEveryNthLayer": 1, - "pumpUpDistance": 2, + "pumpUpDistance": 2.00, + "pumpUpTime": 600, "pumpUpPause": 2000, - "pumpUpTime": 600 - }, - "bodyLayersPumpingParameters": { - "layerExposureTime": 1000, - "layerThickness": 100, - "noPumpUpVelocity": 200, - "powerLevel": 50, "pumpDownPause": 4000, - "pumpEveryNthLayer": 1, - "pumpUpDistance": 2, - "pumpUpPause": 2000, - "pumpUpTime": 600 - }, - "name": "profile_1" - }, - { - "baseLayerCount": 1, - "baseLayersPumpingParameters": { - "layerExposureTime": 1000, - "layerThickness": 100, "noPumpUpVelocity": 200, - "powerLevel": 50, - "pumpDownPause": 4000, "pumpEveryNthLayer": 1, - "pumpUpDistance": 2, - "pumpUpPause": 2000, - "pumpUpTime": 600 - }, - "bodyLayersPumpingParameters": { - "layerExposureTime": 1000, "layerThickness": 100, - "noPumpUpVelocity": 200, - "powerLevel": 50, - "pumpDownPause": 4000, - "pumpEveryNthLayer": 1, - "pumpUpDistance": 2, - "pumpUpPause": 2000, - "pumpUpTime": 600 - }, - "name": "profile_2" - }, - { - "baseLayerCount": 1, - "baseLayersPumpingParameters": { "layerExposureTime": 1000, - "layerThickness": 100, - "noPumpUpVelocity": 200, - "powerLevel": 50, - "pumpDownPause": 4000, - "pumpEveryNthLayer": 1, - "pumpUpDistance": 2, - "pumpUpPause": 2000, - "pumpUpTime": 600 + "powerLevel": 50.0 }, "bodyLayersPumpingParameters": { - "layerExposureTime": 1000, - "layerThickness": 100, - "noPumpUpVelocity": 200, - "powerLevel": 50, + "pumpUpDistance": 2.00, + "pumpUpTime": 600, + "pumpUpPause": 2000, "pumpDownPause": 4000, + "noPumpUpVelocity": 200, "pumpEveryNthLayer": 1, - "pumpUpDistance": 2, - "pumpUpPause": 2000, - "pumpUpTime": 600 - }, - "name": "profile_3" + "layerThickness": 100, + "layerExposureTime": 1000, + "powerLevel": 50.0 + } } ] diff --git a/src/constants.cpp b/src/constants.cpp index 069f20ce..d45aeabb 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -26,8 +26,8 @@ QString const StlModelLibraryPath { "/var/lib/light QString const UpdatesRootPath { "/var/lib/lightfield/software-updates" }; //QString const PrintProfilesPath { "/var/lib/lightfield/print-profiles.json" }; -QString const PrintProfilesPath { "/home/lumen/Volumetric/LightField/print-profiles/print-profiles.json"}; -//QString const PrintProfilesPath { "/home/mateusz/LightField/print-profiles/print-profiles.json"}; +//QString const PrintProfilesPath { "/home/lumen/Volumetric/LightField/print-profiles/print-profiles.json"}; +QString const PrintProfilesPath { "/home/mateusz/LightField/print-profiles/print-profiles.json"}; //QString const PrintProfilesSchemaPath { "/var/lib/lightfield/print-profiles-schema.json" }; diff --git a/src/printprofile.h b/src/printprofile.h index 4f08912a..17f43e95 100644 --- a/src/printprofile.h +++ b/src/printprofile.h @@ -97,6 +97,7 @@ class PrintProfile: public QObject { PrintPumpingParameters _baseLayersPumpingParameters; bool _bodyLayersPumpingEnabled; PrintPumpingParameters _bodyLayersPumpingParameters; + bool _defaultProfile; signals: ; diff --git a/src/profilesjsonparser.h b/src/profilesjsonparser.h index d96d3b6b..3f10f632 100644 --- a/src/profilesjsonparser.h +++ b/src/profilesjsonparser.h @@ -2,7 +2,7 @@ #define PROFILESJSONPARSER_H #include "printprofile.h" -#include +//#include #define DEBUG_LOGS @@ -16,6 +16,7 @@ class ProfilesJsonParser if(!jsonFile.exists()) { // Show message when not found a profile in path + // TODO: Create file with default profile QMessageBox msgBox; msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText("File print profile does not exist\n" ); diff --git a/src/profilestab.cpp b/src/profilestab.cpp index e90ea9a8..8d7b63ef 100644 --- a/src/profilestab.cpp +++ b/src/profilestab.cpp @@ -144,6 +144,9 @@ void ProfilesTab::setPrintProfileManager( PrintProfileManager* printProfileManag QStandardItem* item = nullptr; QStandardItem* firstItem = nullptr; QVector* profiles = ProfilesJsonParser::loadProfiles(); + bool findDefaultProfile = false; + int listItemIndex; + for(int i=0; icount(); ++i) { PrintProfile* profile = (*profiles)[i]; @@ -151,15 +154,38 @@ void ProfilesTab::setPrintProfileManager( PrintProfileManager* printProfileManag item = new QStandardItem(profile->profileName()); item->setEditable( false ); - if( !i ) { + + findDefaultProfile = false; + if ( profile->profileName() == "default" ) + { + findDefaultProfile = true; + } + + if( !i ) + { firstItem=item; _printProfileManager->setActiveProfile(profile->profileName()); _enableButtonProfile(true); } - _model->setItem( i, 0, item ); + if ( !findDefaultProfile ) + { + _model->setItem( listItemIndex, 0, item ); + listItemIndex++; + } } + if ( findDefaultProfile ) + { + qDebug() << "Find default profile"; + } + else + { + // TODO: create default profile ? + qDebug() << "Not find default profile"; + } + + if( firstItem ) _profilesList->setCurrentIndex( _model->indexFromItem(firstItem) ); @@ -325,7 +351,6 @@ void ProfilesTab::deleteProfile_clicked(bool) { Window* w = App::mainWindow(); - QMessageBox msgBox; msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setFont(*_fontAwesome); diff --git a/text/text.qrc b/text/text.qrc index 3f577fe0..6774ce90 100644 --- a/text/text.qrc +++ b/text/text.qrc @@ -1,5 +1,7 @@ - + copyright-message.txt + text.qrc + defaultProfle.txt From 2fee1b6a241ff285b656d10cb8f9f38f584f27f3 Mon Sep 17 00:00:00 2001 From: PiotrConclusive <60405796+PiotrConclusive@users.noreply.github.com> Date: Mon, 2 Mar 2020 11:54:33 +0100 Subject: [PATCH 87/89] Deleted paths pointing to home directories --- src/constants.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/constants.cpp b/src/constants.cpp index d45aeabb..fee26d53 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -24,12 +24,8 @@ QString const ShepherdPath { "/usr/share/lig QString const SlicedSvgFileName { "sliced.svg" }; QString const StlModelLibraryPath { "/var/lib/lightfield/model-library" }; QString const UpdatesRootPath { "/var/lib/lightfield/software-updates" }; +QString const PrintProfilesPath { "/var/lib/lightfield/print-profiles.json" }; -//QString const PrintProfilesPath { "/var/lib/lightfield/print-profiles.json" }; -//QString const PrintProfilesPath { "/home/lumen/Volumetric/LightField/print-profiles/print-profiles.json"}; -QString const PrintProfilesPath { "/home/mateusz/LightField/print-profiles/print-profiles.json"}; - -//QString const PrintProfilesSchemaPath { "/var/lib/lightfield/print-profiles-schema.json" }; QChar const LineFeed { L'\u000A' }; QChar const CarriageReturn { L'\u000D' }; From 9f9d0c35b0defb6b67f9752b709259e2133fecea Mon Sep 17 00:00:00 2001 From: PiotrConclusive <60405796+PiotrConclusive@users.noreply.github.com> Date: Mon, 2 Mar 2020 11:55:27 +0100 Subject: [PATCH 88/89] Deleted unnecessary line --- src/profilesjsonparser.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/profilesjsonparser.h b/src/profilesjsonparser.h index 3f10f632..2fb2733c 100644 --- a/src/profilesjsonparser.h +++ b/src/profilesjsonparser.h @@ -2,7 +2,6 @@ #define PROFILESJSONPARSER_H #include "printprofile.h" -//#include #define DEBUG_LOGS From 4ca1b223e09164866ae2717fb1145e0cda7136bb Mon Sep 17 00:00:00 2001 From: PiotrConclusive <60405796+PiotrConclusive@users.noreply.github.com> Date: Mon, 2 Mar 2020 11:57:41 +0100 Subject: [PATCH 89/89] deleted unnecessary line --- src/profilestab.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/profilestab.h b/src/profilestab.h index 621fff48..0dff0f88 100644 --- a/src/profilestab.h +++ b/src/profilestab.h @@ -65,7 +65,6 @@ private slots: void updateProfile_clicked(bool); void deleteProfile_clicked(bool); void loadProfile_clicked(bool); - //void listChange_clicked(const QModelIndex ¤t, const QModelIndex &previous); void itemClicked(const QModelIndex &index); };