diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..daf34fc2e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,272 @@ +name: Build Configurator +# Don't enable CI on push, just on PR. If you +# are working on the main repo and want to trigger +# a CI build submit a draft PR. +on: + pull_request: + + workflow_call: + #inputs: + # release_build: + # description: 'Specifies if it is a build that should include commit hash in hex file names or not' + # default: false + # required: false + # type: boolean + +jobs: + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup environment + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + run: | + # This is the hash of the commit for the PR + # when the action is triggered by PR, empty otherwise + COMMIT_ID=${{ github.event.pull_request.head.sha }} + # This is the hash of the commit when triggered by push + # but the hash of refs/pull//merge, which is different + # from the hash of the latest commit in the PR, that's + # why we try github.event.pull_request.head.sha first + COMMIT_ID=${COMMIT_ID:-${{ github.sha }}} + BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID}) + VERSION=$(grep version package.json | sed 's/.*"\([0-9][0-9]*.[0-9]*.[0-9]*\)".*/\1/g') + echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV + echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAME=inav-configurator_linux_x64_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + - uses: actions/setup-node@v4 + with: + node-version: 20 + check-latest: true + cache: 'npm' + - name: Install dependencies + run: sudo apt-get update && sudo apt-get -y install dpkg fakeroot rpm build-essential libudev-dev + - name: Install deps + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + retry_on: error + command: npm install + timeout_minutes: 10 + - name: Build Linux + run: npm run make + - name: Upload Linux deb + uses: actions/upload-artifact@v4 + with: + name: ${{ env.BUILD_NAME }}_DEB + path: ./out/make/deb/x64/*.deb + - name: Upload Linux rpm + uses: actions/upload-artifact@v4 + with: + name: ${{ env.BUILD_NAME }}_RPM + path: ./out/make/rpm/x64/*.rpm + - name: Upload Linux zip + uses: actions/upload-artifact@v4 + with: + name: ${{ env.BUILD_NAME }}_ZIP + path: ./out/make/zip/linux/x64/*.zip + + + build-mac-arm64: + runs-on: macos-13 + steps: + - uses: actions/checkout@v4 + - name: Setup environment + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + run: | + # This is the hash of the commit for the PR + # when the action is triggered by PR, empty otherwise + COMMIT_ID=${{ github.event.pull_request.head.sha }} + # This is the hash of the commit when triggered by push + # but the hash of refs/pull//merge, which is different + # from the hash of the latest commit in the PR, that's + # why we try github.event.pull_request.head.sha first + COMMIT_ID=${COMMIT_ID:-${{ github.sha }}} + BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID}) + VERSION=$(grep version package.json | sed 's/.*"\([0-9][0-9]*.[0-9]*.[0-9]*\)".*/\1/g') + echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAMEarm64=inav-configurator_darwin_arm64_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + - uses: actions/setup-node@v4 + with: + node-version: 20 + check-latest: true + cache: 'npm' + # Workaround due to a bug in node-gyp: https://github.com/electron/rebuild/issues/1116 + - name: Install Setuptools + run: python3 -m pip install --break-system-packages setuptools + - name: Install deps + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + retry_on: error + command: npm install + timeout_minutes: 10 + - name: Build MacOS arm64 + run: npm run make -- --arch="arm64" + - name: Upload MacOS arm64 zip + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEarm64}}_ZIP + path: ./out/make/zip/darwin/arm64/*.zip + - name: Upload MacOS arm64 dmg + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEarm64}}_DMG + path: ./out/make/*arm64*.dmg + + build-mac: + runs-on: macos-13 + steps: + - uses: actions/checkout@v4 + - name: Setup environment + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + run: | + # This is the hash of the commit for the PR + # when the action is triggered by PR, empty otherwise + COMMIT_ID=${{ github.event.pull_request.head.sha }} + # This is the hash of the commit when triggered by push + # but the hash of refs/pull//merge, which is different + # from the hash of the latest commit in the PR, that's + # why we try github.event.pull_request.head.sha first + COMMIT_ID=${COMMIT_ID:-${{ github.sha }}} + BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID}) + VERSION=$(grep version package.json | sed 's/.*"\([0-9][0-9]*.[0-9]*.[0-9]*\)".*/\1/g') + echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAMEx64=inav-configurator_darwin_x64_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + - uses: actions/setup-node@v4 + with: + node-version: 20 + check-latest: true + cache: 'npm' + # Workaround due to a bug in node-gyp: https://github.com/electron/rebuild/issues/1116 + - name: Install Setuptools + run: python3 -m pip install --break-system-packages setuptools + - name: Install deps + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + retry_on: error + command: npm install + timeout_minutes: 10 + - name: Build MacOS x64 + run: npm run make -- --arch="x64" + - name: Upload MacOS x64 zip + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEx64}}_ZIP + path: ./out/make/zip/darwin/x64/*.zip + - name: Upload MacOS x64 dmg + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEx64}}_DMG + path: ./out/make/*x64*.dmg + + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Install linux utils + run: choco install --force -y awk grep sed + - name: Setup environment + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + run: | + # This is the hash of the commit for the PR + # when the action is triggered by PR, empty otherwise + COMMIT_ID=${{ github.event.pull_request.head.sha }} + # This is the hash of the commit when triggered by push + # but the hash of refs/pull//merge, which is different + # from the hash of the latest commit in the PR, that's + # why we try github.event.pull_request.head.sha first + COMMIT_ID=${COMMIT_ID:-${{ github.sha }}} + BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID}) + VERSION=$(grep version package.json | sed 's/.*"\([0-9][0-9]*.[0-9]*.[0-9]*\)".*/\1/g') + echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV + echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAMEx64=inav-configurator_win32_x64_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + shell: bash + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + - uses: engineerd/configurator@v0.0.10 + with: + name: "Wix Toolset 3.1.4" + url: "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip" + pathInArchive: "/" + - name: Install deps + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + retry_on: error + command: npm install + timeout_minutes: 10 + - name: Build Win x64 + run: npm run make -- --arch="x64" + - name: Upload Windows x64 zip + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEx64}}_ZIP + path: ./out/make/zip/win32/x64/*.zip + - name: Upload Windows x64 msi + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEx64}}_MSI + path: ./out/make/wix/x64/*.msi + + build-windows-win32: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Install linux utils + run: choco install --force -y awk grep sed + - name: Setup environment + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + run: | + # This is the hash of the commit for the PR + # when the action is triggered by PR, empty otherwise + COMMIT_ID=${{ github.event.pull_request.head.sha }} + # This is the hash of the commit when triggered by push + # but the hash of refs/pull//merge, which is different + # from the hash of the latest commit in the PR, that's + # why we try github.event.pull_request.head.sha first + COMMIT_ID=${COMMIT_ID:-${{ github.sha }}} + BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID}) + VERSION=$(grep version package.json | sed 's/.*"\([0-9][0-9]*.[0-9]*.[0-9]*\)".*/\1/g') + echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV + echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAMEia32=inav-configurator_win32_ia32_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + shell: bash + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + - uses: engineerd/configurator@v0.0.10 + with: + name: "Wix Toolset 3.1.4" + url: "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip" + pathInArchive: "/" + - name: Install deps + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + retry_on: error + command: npm install + timeout_minutes: 10 + - name: Build Win32 + run: npm run make -- --arch="ia32" + - name: Upload Windows ia32 zip + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEia32}}_ZIP + path: ./out/make/zip/win32/ia32/*.zip + - name: Upload Windows ia32 msi + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEia32}}_MSI + path: ./out/make/wix/ia32/*.msi + diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml new file mode 100644 index 000000000..4523279e5 --- /dev/null +++ b/.github/workflows/nightly-build.yml @@ -0,0 +1,62 @@ +name: Build pre-release + +on: + push: + branches: + - master +# pull_request: + +jobs: + build: + name: build + uses: ./.github/workflows/ci.yml + + release: + name: Build nightly-release + runs-on: ubuntu-latest + needs: [build] + + steps: + - name: Get current date + id: date + run: echo "today=$(date '+%Y%m%d')" >> $GITHUB_OUTPUT + - name: Code Checkout + uses: actions/checkout@v4 + - name: Fetch build artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + - name: Upload release artifacts + uses: softprops/action-gh-release@v2 + with: + name: inav-configurator-dev-${{ steps.date.outputs.today }}-${{ github.run_number }}-${{ github.sha }} + tag_name: v${{ steps.date.outputs.today }}.${{ github.run_number }} + # To create release on a different repo, we need a token setup + token: ${{ secrets.NIGHTLY_TOKEN }} + repository: iNavFlight/inav-configurator-nightly + prerelease: true + draft: false + #generate_release_notes: true + make_latest: false + files: | + artifacts/* + body: | + ${{ steps.notes.outputs.notes }} + + ### MacOS builds + + MacOS builds are not signed, so you will need to run ```xattr -cr /path/to/your/INAV Configurator.app``` from the command line to remove the warnings about it being a downloaded application or moving it to trash when trying to launch. + + ### Repository: + ${{ github.repository }} ([link](${{ github.event.repository.html_url }})) + + ### Branch: + ${{ github.ref_name }} ([link](${{ github.event.repository.html_url }}/tree/${{ github.ref_name }})) + + ### Latest changeset: + ${{ github.event.head_commit.id }} ([link](${{ github.event.head_commit.url }})) + + ### Changes: + ${{ github.event.head_commit.message }} + diff --git a/.gitignore b/.gitignore index ec45237d6..875f3c012 100644 --- a/.gitignore +++ b/.gitignore @@ -5,14 +5,15 @@ npm-debug.log .idea/ npm-debug.log inav-configurator.iml -# Generated scripts and styles -/build -# Used by nw-builder to download runtimes -/cache -# Where we put the final app directory structure -/dist -# Path where the NW.js apps get built -/apps -/.vscode/ -.eslintrc.json -/.project +/out +.eslintrc.json +/.project +/cache/ +/dist/ +/apps/ +/build/ +*~ +*.swp +*.bak +eeprom.bin +yarn.lock \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..f8efa898f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug Configurator", + "runtimeExecutable": "${workspaceFolder}/node_modules/@electron-forge/cli/script/vscode.sh", + "windows": { + "runtimeExecutable": "${workspaceFolder}/node_modules/@electron-forge/cli/script/vscode.cmd", + + }, + "args": ["--inspect"], + "env": { + "NODE_ENV": "development", + "NODE_PATH": "${workspaceFolder}/js/" + }, + "cwd": "${workspaceFolder}", + "console": "integratedTerminal" + } + ] + } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..889586c40 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "problemMatcher": [], + "label": "npm: start", + "detail": "node node_modules/gulp/bin/gulp.js build && node node_modules/nw/bin/nw .", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index d84f356d8..b68442941 100644 --- a/README.md +++ b/README.md @@ -18,40 +18,34 @@ everything, the hardware is not working, or you have any other _support_ problem * [RC Groups Support](https://www.rcgroups.com/forums/showthread.php?2495732-Cleanflight-iNav-(navigation-rewrite)-project) * [INAV Official on Telegram](https://t.me/INAVFlight) -## INAV Configurator starts minimized, what should I do? - -You have to remove the `C:\Users%Your_UserName%\AppData\Local\inav-configurator` folder and all its content. - -[https://www.youtube.com/watch?v=XMoULyiFDp4](https://www.youtube.com/watch?v=XMoULyiFDp4) - -Alternatively, on Windows with PowerShell, you can use the `post_install_cleanup.ps1` script that will do the cleaning. (thank you, James Cherrill) - ## Installation -Depending on the target operating system, _INAV Configurator_ is distributed as a _standalone_ application or Chrome App. + _INAV Configurator_ is distributed as a _standalone_ application. ### Windows 1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases) -1. Download Configurator for Windows platform (win32 or win64 is present) -1. Extract ZIP archive -1. Run the INAV Configurator app from the unpacked folder -1. Configurator is not signed, so you have to allow Windows to run untrusted applications. There might be a monit for it during the first run +2. Download Configurator for Windows platform (ia32 or win64 is present) +3. Install + * Extract ZIP archive and run the INAV Configurator app from the unpacked folder + * OR just use the setup program `INAV-Configurator_win32_arch_x.y.z.msi`, **arch** is your computer architecture (ia32 (32bit) or x64 (64bit)), **x.y.z** is the INAV Configurator version number. + +4. Configurator is not signed, so you have to allow Windows to run untrusted applications. There might be a monit for it during the first run ### Linux 1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases) -2. Download Configurator for Linux platform (linux32 and linux64 are present) - * **.rpm** is the Fedora installation file. Just download and install using `sudo dnf localinstall /path/to/INAV-Configurator_linux64-x.y.z-x86_64.rpm` or open it with a package manager (e.g. via Files) - * **.deb** is the Debian/Ubuntu installation file. Just download and install using `sudo apt install /path/to/INAV-Configurator_linux64_x.y.z.deb` or open it with a package manager (e.g. via the File Manager) - * **.tar.gz** is a universal archive. Download and continue with these instructions to install -3. Change to the directory containing the downloaded **tar.gz** file +2. Download Configurator for Linux platform (only linux64 is present) + * **.rpm** is the Fedora installation file. Just download and install using `sudo dnf localinstall /path/to/INAV-Configurator_linux_x64-x.y.z.rpm` or open it with a package manager (e.g. via Files) + * **.deb** is the Debian/Ubuntu installation file. Just download and install using `sudo apt install /path/to/INAV-Configurator_linux_x64_x.y.z.deb` or open it with a package manager (e.g. via the File Manager) + * **.zip** is a universal archive. Download and continue with these instructions to install +3. Change to the directory containing the downloaded **zip** file 4. download [this](https://raw.githubusercontent.com/iNavFlight/inav-configurator/master/assets/linux/inav-configurator.desktop) file to the same directory. Its filename should be `inav-configurator.desktop`. -5. Extract **tar.gz** archive +5. Extract **zip** archive ``` -tar -C /tmp/ -xf INAV-Configurator_linuxNN_x.y.z.tar.gz +unzip INAV-Configurator_linux_arch_x.y.z.zip -d /tmp/ ``` - **NN** is the bits of your OS. **x.y.z** is the INAV Configurator version number. + **arch** is your computer architecture (x64, armv7l, ...), **x.y.z** is the INAV Configurator version number. 6. If this is the first time installing INAV Configurator, create a home for its files ``` @@ -73,57 +67,55 @@ sudo mv inav-configurator.desktop /usr/share/applications/ ``` 10. Make the following files executable: * inav-configurator `chmod +x /opt/inav/inav-configurator/inav-configurator` - * (5.0.0+) chrome_crashpad_handler `chmod +x /opt/inav/inav-configurator/chrome_crashpad_handler` 11. Run the INAV Configurator app from the unpacked folder `/opt/inav/inav-configurator/inav-configurator` -#### Notes - -On some Linux distros, you may be missing `libatomic` and/or `NW.JS` (especially `libnode.so`) dependencies. If so, please install `libatomic` using your distro's package manager, e.g: - -* Arch Linux: `sudo pacman -S --needed libatomic_ops` -* Debian / Ubuntu: `sudo apt install libatomic1` -* Fedora: `sudo dnf install libatomic` - -1. Don't forget to add your user to the dialout group "sudo usermod -aG dialout YOUR_USERNAME" for serial access -2. If you have 3D model animation problems, enable "Override software rendering list" in Chrome flags chrome://flags/#ignore-gpu-blacklist - ### Mac 1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases) -1. Download Configurator for the Mac platform -1. Extract ZIP archive -1. Run INAV Configurator +2. Download Configurator for the Mac platform +3. Install + * Extract ZIP archive and run INAV Configurator + * OR use the DMG package for installation ## Building and running INAV Configurator locally (for development) For local development, the **node.js** build system is used. 1. Install node.js -1. From the project folder run `npm install` -1. To build the JS and CSS files and start the configurator: - - With NW.js: Run `npm start`. - - With Chrome: Run `npm run gulp`. Then open `chrome://extensions`, enable - the `Developer mode`, click on the `Load unpacked extension...` button, and select the `inav-configurator` directory. - -Other tasks are also defined in `gulpfile.js`. To run a task, use `node ./node_modules/gulp/bin/gulp.js task-name`. Available ones are: - -- **build**: Generate JS and CSS output files used by the configurator from their sources. It must be run whenever changes are made to any `.js` or `.css` files in order to have those changes appear -in the configurator. If new files are added, they must be included in `gulpfile.js`. See the comments at the top of `gulpfile.js` to learn how to do so. See also the `watch` task. -- **watch**: Watch JS and CSS sources for changes and run the `build` task whenever they're edited. -- **dist**: Create a distribution of the app (valid for packaging both as a Chrome app or NW.js app) -in the `./dist/` directory. -- **release**: Create NW.js apps for each supported platform (win32, osx64 and linux64) in the `./apps` -directory. Running this task on macOS or Linux requires Wine since it's needed to set the icon -for the Windows app. If you don't have Wine installed, you can create a release by running the **release-only-Linux** task. -
`--installer` argument can be added to build installers for a particular OS. NOTE: MacOS Installer can be built with MacOS only. - -To build a specific release, use the command `release --platform="win64"` for example. +1. From the project folder run `yarn install` and then `npm install` +1. To build the and start the configurator: + - Run `npm start`. + +To build the App run `npm run make` to build for your platform. + +Options: +* Architecture: --arch - Allowed values are: "ia32", "x64", "armv7l", "arm64", "universal", or "mips64el". + +See [Electron Forge CLI Documentation](https://www.electronforge.io/cli#options-2) for details + +Note: Not all architectures are available for all platforms. For example, ia32 (32bit) support is not available for Linux. +Tested architectures: +- Windows: x64 and ia32 +- Linux: x64 and armv7l +- MacOS: x64 and arm64 + +To build the setup program for windows, you have to install [WiX Toolset V3](https://github.com/wixtoolset/wix3/releases) and add the `bin` folder to you `PATH`, e.g. +```C:\Program Files (x86)\WiX Toolset v3.14\bin``` + +To build deb and rpm packages for Linux, you have to install the following packages: +- Ubuntu/Debian: `dpkg, fakeroot, rpm, build-essential, libudev-dev` +- OpenSuse/Fedora: `dpkg, fakeroot, rpmbuild, systemd-devel, devel-basis (zypper install -t pattern devel_basis), zip` + +Example (note the double -- ): +```npm run make -- --arch="x64"``` ### Running with debug | Inspector -To be able to open Inspector, you will need SDK flavours of NW.js +To be able to open Inspector, set environment variable `NODE_ENV` to `development` or set the flag directly when run `npm start`: + +```NODE_ENV=development npm start``` or ```$env:NODE_ENV="development" | npm start``` for Windows PowerShell -`npm install nw@0.61.0 --nwjs_build_type=sdk` +Or use vscode and start a debug session `Debug Configurator` (Just hit F5!) ## Different map providers diff --git a/_config.yml b/_config.yml deleted file mode 100644 index c74188174..000000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-slate \ No newline at end of file diff --git a/assets/linux/inav-configurator.desktop b/assets/linux/inav-configurator.desktop index 40b0c778d..3b8124bcd 100644 --- a/assets/linux/inav-configurator.desktop +++ b/assets/linux/inav-configurator.desktop @@ -2,7 +2,7 @@ Name=INAV Configurator Comment=Crossplatform configuration tool for the INAV flight control system Exec=/opt/inav/inav-configurator/inav-configurator -Icon=/opt/inav/inav-configurator/icon/inav_icon_128.png +Icon=/opt/inav/inav-configurator/resources/app/assets/linux/icon/inav_icon_128.png Terminal=false Type=Application Categories=Utility \ No newline at end of file diff --git a/assets/windows/background.jpg b/assets/windows/background.jpg new file mode 100644 index 000000000..6e044d5b6 Binary files /dev/null and b/assets/windows/background.jpg differ diff --git a/assets/windows/banner.jpg b/assets/windows/banner.jpg new file mode 100644 index 000000000..66c3f04f8 Binary files /dev/null and b/assets/windows/banner.jpg differ diff --git a/assets/windows/inav_installer.bmp b/assets/windows/inav_installer.bmp deleted file mode 100644 index d65f39407..000000000 Binary files a/assets/windows/inav_installer.bmp and /dev/null differ diff --git a/assets/windows/inav_installer_small.bmp b/assets/windows/inav_installer_small.bmp deleted file mode 100644 index 535303fb4..000000000 Binary files a/assets/windows/inav_installer_small.bmp and /dev/null differ diff --git a/assets/windows/installer.iss b/assets/windows/installer.iss deleted file mode 100644 index 89f56efa7..000000000 --- a/assets/windows/installer.iss +++ /dev/null @@ -1,169 +0,0 @@ -; ------------------------------------------ -; Installer for INAV -; ------------------------------------------ -; It receives from the command line with /D the parameters: -; version -; archName -; archAllowed -; archInstallIn64bit -; sourceFolder -; targetFolder - -#define ApplicationName "INAV Configurator" -#define CompanyName "The INAV open source project" -#define CompanyUrl "https://github.com/iNavFlight/inav" -#define ExecutableFileName "inav-configurator.exe" -#define GroupName "INAV" -#define InstallerFileName "INAV-Configurator_" + archName + "_" + version -#define SourcePath "..\..\" + sourceFolder + "\inav-configurator\" + archName -#define TargetFolderName "INAV-Configurator" -#define UpdatesUrl "https://github.com/iNavFlight/inav-configurator/releases" - -[CustomMessages] -AppName=inav-configurator -LaunchProgram=Start {#ApplicationName} - -[Files] -Source: "{#SourcePath}\*"; DestDir: "{app}"; Flags: recursesubdirs - -[Icons] -; Programs group -Name: "{group}\{#ApplicationName}"; Filename: "{app}\{#ExecutableFileName}"; -; Desktop icon -Name: "{autodesktop}\{#ApplicationName}"; Filename: "{app}\{#ExecutableFileName}"; -; Non admin users, uninstall icon -Name: "{group}\Uninstall {#ApplicationName}"; Filename: "{uninstallexe}"; Check: not IsAdminInstallMode - -[Languages] -; English default, it must be first -Name: "en"; MessagesFile: "compiler:Default.isl" -; Official languages -;Name: "ca"; MessagesFile: "compiler:Languages\Catalan.isl" -;Name: "da"; MessagesFile: "compiler:Languages\Danish.isl" -;Name: "de"; MessagesFile: "compiler:Languages\German.isl" -;Name: "es"; MessagesFile: "compiler:Languages\Spanish.isl" -;Name: "fr"; MessagesFile: "compiler:Languages\French.isl" -;Name: "it"; MessagesFile: "compiler:Languages\Italian.isl" -;Name: "ja"; MessagesFile: "compiler:Languages\Japanese.isl" -;Name: "nl"; MessagesFile: "compiler:Languages\Dutch.isl" -;Name: "pt"; MessagesFile: "compiler:Languages\Portuguese.isl" -;Name: "pl"; MessagesFile: "compiler:Languages\Polish.isl" -;Name: "ru"; MessagesFile: "compiler:Languages\Russian.isl" -; Not official. Sometimes not updated to latest version (strings missing) -;Name: "ga"; MessagesFile: "unofficial_inno_languages\Galician.isl" -;Name: "eu"; MessagesFile: "unofficial_inno_languages\Basque.isl" -;Name: "hr"; MessagesFile: "unofficial_inno_languages\Croatian.isl" -;Name: "hu"; MessagesFile: "unofficial_inno_languages\Hungarian.isl" -;Name: "id"; MessagesFile: "unofficial_inno_languages\Indonesian.isl" -;Name: "ko"; MessagesFile: "unofficial_inno_languages\Korean.isl" -;Name: "lv"; MessagesFile: "unofficial_inno_languages\Latvian.isl" -;Name: "sv"; MessagesFile: "unofficial_inno_languages\Swedish.isl" -;Name: "zh_CN"; MessagesFile: "unofficial_inno_languages\ChineseSimplified.isl" -;Name: "zh_TW"; MessagesFile: "unofficial_inno_languages\ChineseTraditional.isl" -; Not available -; pt_BR (Portuguese Brasileiro) - -[Run] -; Add a checkbox to start the app after installed -Filename: {app}\{cm:AppName}.exe; Description: {cm:LaunchProgram,{cm:AppName}}; Flags: nowait postinstall skipifsilent - -[Setup] -AppId=2e5662ca-1fb3-8f1e-a7e1-e390add7a19d -AppName={#ApplicationName} -AppPublisher={#CompanyName} -AppPublisherURL={#CompanyUrl} -AppUpdatesURL={#UpdatesUrl} -AppVersion={#version} -ArchitecturesAllowed={#archAllowed} -ArchitecturesInstallIn64BitMode={#archInstallIn64bit} -Compression=lzma2 -DefaultDirName={autopf}\{#GroupName}\{#TargetFolderName} -DefaultGroupName={#GroupName}\{#ApplicationName} -LicenseFile=..\..\LICENSE -MinVersion=6.2 -OutputBaseFilename={#InstallerFileName} -OutputDir=..\..\{#targetFolder}\ -PrivilegesRequiredOverridesAllowed=commandline dialog -SetupIconFile=inav_installer_icon.ico -ShowLanguageDialog=yes -SolidCompression=yes -UninstallDisplayIcon={app}\{#ExecutableFileName} -UninstallDisplayName={#ApplicationName} -WizardImageFile=inav_installer.bmp -WizardSmallImageFile=inav_installer_small.bmp -WizardStyle=modern - -[Code] -function GetOldNsisUninstallerPath(): String; -var - RegKey: String; -begin - Result := ''; - // Look into the different registry entries: win32, win64 and without user rights - if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\INAV Configurator', 'UninstallString', Result) then - begin - if not RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\INAV Configurator', 'UninstallString', Result) then - begin - RegQueryStringValue(HKCU, 'SOFTWARE\INAV\INAV Configurator', 'UninstallString', Result) - end; - end; -end; - -function GetQuietUninstallerPath(): String; -var - RegKey: String; -begin - Result := ''; - RegKey := Format('%s\%s_is1', ['Software\Microsoft\Windows\CurrentVersion\Uninstall', '{#emit SetupSetting("AppId")}']); - if not RegQueryStringValue(HKEY_LOCAL_MACHINE, RegKey, 'QuietUninstallString', Result) then - begin - RegQueryStringValue(HKEY_CURRENT_USER, RegKey, 'QuietUninstallString', Result); - end; -end; - -function InitializeSetup(): Boolean; -var - ResultCode: Integer; - ParameterStr : String; - UninstPath : String; -begin - - Result := True; - - // Check if the application is already installed by the old NSIS installer, and uninstall it - UninstPath := GetOldNsisUninstallerPath(); - - // Found, start uninstall - if UninstPath <> '' then - begin - - UninstPath := RemoveQuotes(UninstPath); - - // Add this parameter to not return until uninstall finished. The drawback is that the uninstaller file is not deleted - ParameterStr := '_?=' + ExtractFilePath(UninstPath); - - if Exec(UninstPath, ParameterStr, '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then - begin - // Delete the unistaller file and empty folders. Not deleting the files. - DeleteFile(UninstPath); - DelTree(ExtractFilePath(UninstPath), True, False, True); - end - else begin - Result := False; - MsgBox('Error uninstalling old Configurator ' + SysErrorMessage(ResultCode) + '.', mbError, MB_OK); - end; - end - else begin - - // Search for new Inno Setup installations - UninstPath := GetQuietUninstallerPath(); - if UninstPath <> '' then - begin - if not Exec('>', UninstPath, '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then - begin - Result := False; - MsgBox('Error uninstalling Configurator ' + SysErrorMessage(ResultCode) + '.', mbError, MB_OK); - end; - end; - end; -end; \ No newline at end of file diff --git a/assets/windows/wix.xml b/assets/windows/wix.xml new file mode 100644 index 000000000..acace5f6e --- /dev/null +++ b/assets/windows/wix.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eventPage.js b/eventPage.js deleted file mode 100755 index 2843cdbf0..000000000 --- a/eventPage.js +++ /dev/null @@ -1,117 +0,0 @@ -/* - If an id is also specified and a window with a matching id has been shown before, the remembered bounds of the window will be used instead. -*/ -'use strict'; - -function startApplication() { - var applicationStartTime = new Date().getTime(); - - chrome.app.window.create('main.html', { - id: 'main-window', - frame: 'chrome', - innerBounds: { - minWidth: 1024, - minHeight: 550 - } - }, function (createdWindow) { - createdWindow.contentWindow.addEventListener('load', function () { - createdWindow.contentWindow.catch_startup_time(applicationStartTime); - }); - - createdWindow.onClosed.addListener(function () { - // automatically close the port when application closes - // save connectionId in separate variable before createdWindow.contentWindow is destroyed - var connectionId = createdWindow.contentWindow.CONFIGURATOR.connection.connectionId, - valid_connection = createdWindow.contentWindow.CONFIGURATOR.connectionValid, - mincommand = createdWindow.contentWindow.MISC.mincommand; - - console.log("EP:" + connectionId); - if (connectionId && valid_connection) { - // code below is handmade MSP message (without pretty JS wrapper), it behaves exactly like MSP.send_message - // sending exit command just in case the cli tab was open. - // reset motors to default (mincommand) - - var bufferOut = new ArrayBuffer(5), - bufView = new Uint8Array(bufferOut); - - bufView[0] = 0x65; // e - bufView[1] = 0x78; // x - bufView[2] = 0x69; // i - bufView[3] = 0x74; // t - bufView[4] = 0x0D; // enter - - chrome.serial.send(connectionId, bufferOut, function () { console.log('Send exit') }); - - setTimeout(function() { - bufferOut = new ArrayBuffer(22); - bufView = new Uint8Array(bufferOut); - var checksum = 0; - - bufView[0] = 36; // $ - bufView[1] = 77; // M - bufView[2] = 60; // < - bufView[3] = 16; // data length - bufView[4] = 214; // MSP_SET_MOTOR - - checksum = bufView[3] ^ bufView[4]; - - for (var i = 0; i < 16; i += 2) { - bufView[i + 5] = mincommand & 0x00FF; - bufView[i + 6] = mincommand >> 8; - - checksum ^= bufView[i + 5]; - checksum ^= bufView[i + 6]; - } - - bufView[5 + 16] = checksum; - - chrome.serial.send(connectionId, bufferOut, function (sendInfo) { - chrome.serial.disconnect(connectionId, function (result) { - console.log('SERIAL: Connection closed - ' + result); - }); - }); - }, 100); - } else if (connectionId) { - chrome.serial.disconnect(connectionId, function (result) { - console.log('SERIAL: Connection closed - ' + result); - }); - } - }); - }); -} - -chrome.app.runtime.onLaunched.addListener(startApplication); - -chrome.runtime.onInstalled.addListener(function (details) { - if (details.reason == 'update') { - var previousVersionArr = details.previousVersion.split('.'), - currentVersionArr = chrome.runtime.getManifest().version.split('.'); - - // only fire up notification sequence when one of the major version numbers changed - if (currentVersionArr[0] > previousVersionArr[0] || currentVersionArr[1] > previousVersionArr[1]) { - chrome.storage.local.get('update_notify', function (result) { - if (result.update_notify === 'undefined' || result.update_notify) { - var manifest = chrome.runtime.getManifest(); - var options = { - priority: 0, - type: 'basic', - title: manifest.name, - message: chrome.i18n.getMessage('notifications_app_just_updated_to_version', [manifest.version]), - iconUrl: '/images/icon_128.png', - buttons: [{'title': chrome.i18n.getMessage('notifications_click_here_to_start_app')}] - }; - - chrome.notifications.create('baseflight_update', options, function (notificationId) { - // empty - }); - } - }); - } - } -}); - -chrome.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) { - if (notificationId == 'baseflight_update') { - startApplication(); - } -}); \ No newline at end of file diff --git a/forge.config.js b/forge.config.js new file mode 100644 index 000000000..223287f53 --- /dev/null +++ b/forge.config.js @@ -0,0 +1,114 @@ +const path = require('path'); +const fs = require('fs'); + +module.exports = { + packagerConfig: { + executableName: "inav-configurator", + asar: false, + icon: 'images/inav', + ignore: [ + "^(\/\.vscode$)", + "^(\/support$)", + ".gitattributes", + ".gitignore", + "3D_model_creation.md", + "LICENSE", + "MAPPROXY.md", + "package-lock.json", + "README.md", + "inav_icon_128.psd", + ] + }, + hooks: { + // Uniform artifact file names + postMake: async (config, makeResults) => { + makeResults.forEach(result => { + var baseName = `${result.packageJSON.productName.replace(' ', '-')}_${result.platform}_${result.arch}_${result.packageJSON.version}`; + result.artifacts.forEach(artifact => { + var artifactStr = artifact.toString(); + var newPath = path.join(path.dirname(artifactStr), baseName + path.extname(artifactStr)); + fs.renameSync(artifactStr, newPath); + console.log('Artifact: ' + newPath); + }); + }); + } + }, + rebuildConfig: {}, + makers: [ + { + name: '@electron-forge/maker-wix', + config: { + name: "INAV Configurator", + shortName: "INAV", + exe: "inav-configurator", + description: "Configurator for the open source flight controller software INAV.", + programFilesFolderName: "inav-configurator", + shortcutFolderName: "INAV", + manufacturer: "The INAV open source project", + appUserModelId: "com.inav.configurator", + icon: path.join(__dirname, "./assets/windows/inav_installer_icon.ico"), + upgradeCode: "13606ff3-b0bc-4dde-8fac-805bc8aed2f8", + ui : { + enabled: false, + chooseDirectory: true, + images: { + background: path.join(__dirname, "./assets/windows/background.jpg"), + banner: path.join(__dirname, "./assets/windows/banner.jpg") + } + }, + // Standard WiX template appends the unsightly "(Machine - WSI)" to the name, so use our own template + beforeCreate: (msiCreator) => { + return new Promise((resolve, reject) => { + fs.readFile(path.join(__dirname,"./assets/windows/wix.xml"), "utf8" , (err, content) => { + if (err) { + reject (err); + } + msiCreator.wixTemplate = content; + resolve(); + }); + }); + } + } + }, + { + name: '@electron-forge/maker-dmg', + config: { + name: "INAV Configurator", + background: "./assets/osx/dmg-background.png", + icon: "./images/inav.icns" + } + }, + { + name: '@electron-forge/maker-zip', + platforms: ['win32', 'linux', 'darwin'], + }, + { + name: '@electron-forge/maker-deb', + config: { + options: { + name: "inav-configurator", + productName: "INAV Configurator", + categories: ["Utility"], + icon: "./assets/linux/icon/inav_icon_128.png", + description: "Configurator for the open source flight controller software INAV.", + homepage: "https://github.com/inavflight/", + + } + }, + }, + { + name: '@electron-forge/maker-rpm', + config: { + options: { + name: "inav-configurator", + productName: "INAV Configurator", + license: "GPL-3.0", + categories: ["Utility"], + icon: "./assets/linux/icon/inav_icon_128.png", + description: "Configurator for the open source flight controller software INAV.", + homepage: "https://github.com/inavflight/", + } + }, + }, + ], +}; diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index a7db937c5..000000000 --- a/gulpfile.js +++ /dev/null @@ -1,728 +0,0 @@ -'use strict'; - -var child_process = require('child_process'); -var fs = require('fs'); -var path = require('path'); -var minimist = require('minimist'); - -var archiver = require('archiver'); -var del = require('del'); -var NwBuilder = require('nw-builder'); -var semver = require('semver'); - -var gulp = require('gulp'); -var concat = require('gulp-concat'); - -const commandExistsSync = require('command-exists').sync; - -// Each key in the *sources* variable must be an array of -// the source files that will be combined into a single -// file and stored in *outputDir*. Each key in *sources* -// must be also present in *output*, whose value indicates -// the filename for the output file which combines the -// contents of the source files. -// -// Keys must be camel cased and end with either 'Css' or -// 'Js' (e.g. someSourcesCss or someSourcesJs). For each -// key, a build task will be generated named by prepending -// 'build-' and converting the key to dash-separated words -// (e.g. someSourcesCss will generate build-some-sources-css). -// -// Tasks with names ending with '-js' will be executed by the -// build-all-js task, while the ones ending with '-css' will -// be done by build-all-css. There's also a build task which -// runs both build-all-css and build-all-js. -// -// The watch task will monitor any files mentioned in the *sources* -// variable and regenerate the corresponding output file when -// they change. -// -// See README.md for details on the other tasks. - -var sources = {}; - -sources.css = [ - './main.css', - './js/libraries/jquery.nouislider.min.css', - './js/libraries/jquery.nouislider.pips.min.css', - './js/libraries/flightindicators.css', - './src/css/tabs/*.css', - './src/css/opensans_webfontkit/fonts.css', - './src/css/font-awesome/css/font-awesome.css', - './src/css/dropdown-lists/css/style_lists.css', - './js/libraries/switchery/switchery.css', - './js/libraries/jbox/jBox.css', - './node_modules/openlayers/dist/ol.css', - './src/css/logic.css', - './src/css/defaults_dialog.css', -]; - -sources.js = [ - './js/libraries/google-analytics-bundle.js', - './node_modules/jquery/dist/jquery.min.js', - './node_modules/jquery-ui-npm/jquery-ui.min.js', - './node_modules/marked/lib/marked.js', - './js/libraries/d3.min.js', - './js/libraries/jquery.nouislider.all.min.js', - './node_modules/three/build/three.min.js', - './node_modules/three/examples/js/loaders/GLTFLoader.js', - './node_modules/three/examples/js/controls/OrbitControls.js', - './js/libraries/nw-dialog.js', - './js/libraries/bundle_xml2js.js', - './js/libraries/Projector.js', - './js/libraries/CanvasRenderer.js', - './js/libraries/jquery.flightindicators.js', - './js/libraries/semver.js', - './js/libraries/jbox/jBox.min.js', - './js/libraries/switchery/switchery.js', - './js/libraries/jquery.ba-throttle-debounce.js', - './js/helpers.js', - './node_modules/inflection/inflection.min.js', - './node_modules/bluebird/js/browser/bluebird.min.js', - './js/injected_methods.js', - './js/intervals.js', - './js/timeouts.js', - './js/pid_controller.js', - './js/simple_smooth_filter.js', - './js/walking_average_filter.js', - './js/gui.js', - './js/msp/MSPCodes.js', - './js/msp/MSPHelper.js', - './js/msp/MSPchainer.js', - './js/port_handler.js', - './js/connection/connection.js', - './js/connection/connectionBle.js', - './js/connection/connectionSerial.js', - './js/connection/connectionTcp.js', - './js/connection/connectionUdp.js', - './js/servoMixRule.js', - './js/motorMixRule.js', - './js/logicCondition.js', - './js/settings.js', - './js/outputMapping.js', - './js/model.js', - './js/serial_backend.js', - './js/data_storage.js', - './js/fc.js', - './js/msp.js', - './js/protocols/stm32.js', - './js/protocols/stm32usbdfu.js', - './js/localization.js', - './js/boards.js', - './js/servoMixerRuleCollection.js', - './js/motorMixerRuleCollection.js', - './js/logicConditionsCollection.js', - './js/logicConditionsStatus.js', - './js/globalVariablesStatus.js', - './js/programmingPid.js', - './js/programmingPidCollection.js', - './js/programmingPidStatus.js', - './js/vtx.js', - './main.js', - './js/tabs.js', - './tabs/*.js', - './js/eventFrequencyAnalyzer.js', - './js/periodicStatusUpdater.js', - './js/serial_queue.js', - './js/msp_balanced_interval.js', - './tabs/advanced_tuning.js', - './tabs/ez_tune.js', - './js/peripherals.js', - './js/appUpdater.js', - './js/feature_framework.js', - './js/defaults_dialog.js', - './js/safehomeCollection.js', - './js/safehome.js', - './js/waypointCollection.js', - './js/waypoint.js', - './node_modules/openlayers/dist/ol.js', - './js/libraries/plotly-latest.min.js', - './js/sitl.js', - './js/CliAutoComplete.js', - './node_modules/jquery-textcomplete/dist/jquery.textcomplete.js' -]; - -sources.receiverCss = [ - './src/css/tabs/receiver_msp.css', - './src/css/opensans_webfontkit/fonts.css', - './js/libraries/jquery.nouislider.min.css', - './js/libraries/jquery.nouislider.pips.min.css', -]; - -sources.receiverJs = [ - './node_modules/jquery/dist/jquery.min.js', - './node_modules/jquery-ui-npm/jquery-ui.min.js', - './js/libraries/jquery.nouislider.all.min.js', - './tabs/receiver_msp.js' -]; - -sources.debugTraceJs = [ - './js/debug_trace.js' -]; - -sources.hexParserJs = [ - './js/workers/hex_parser.js', -]; - -var output = { - css: 'styles.css', - js: 'script.js', - receiverCss: 'receiver-msp.css', - receiverJs: 'receiver-msp.js', - debugTraceJs: 'debug-trace.js', - hexParserJs: 'hex_parser.js', -}; - - -var outputDir = './build/'; -var distDir = './dist/'; -var appsDir = './apps/'; - -function get_task_name(key) { - return 'build-' + key.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();}); -} - -function getArguments() { - return minimist(process.argv.slice(2)); -} - -function getPlatforms() { - const defaultPlatforms = ['win32', 'win64', 'osx64', 'linux32', 'linux64']; - const platform = getArguments().platform; - if (platform) { - if (defaultPlatforms.indexOf(platform) < 0) { - throw new Error(`Invalid platform "${platform}". Available ones are: ${defaultPlatforms}`) - } - return [platform]; - } - return defaultPlatforms; -} - -function execSync() { - const cmd = arguments[0]; - const args = Array.prototype.slice.call(arguments, 1); - const result = child_process.spawnSync(cmd, args, {stdio: 'inherit'}); - if (result.error) { - throw result.error; - } -} - -// Define build tasks dynamically based on the sources -// and output variables. -var buildCssTasks = []; -var buildJsTasks = []; -(function() { - // Convers fooBarBaz to foo-bar-baz - for (var k in output) { - (function (key) { - var name = get_task_name(key); - if (name.endsWith('-css')) { - buildCssTasks.push(name); - } else if (name.endsWith('-js')) { - buildJsTasks.push(name); - } else { - throw 'Invalid task name: "' + name + '": must end with -css or -js'; - } - gulp.task(name, function() { - return gulp.src(sources[key]) - .pipe(concat(output[key])) - .pipe(gulp.dest(outputDir)); - }); - })(k); - } -})(); - -gulp.task('build-all-js', gulp.parallel(buildJsTasks)) -gulp.task('build-all-css', gulp.parallel(buildCssTasks)); -gulp.task('build', gulp.parallel('build-all-css', 'build-all-js')); - -gulp.task('clean', function() { return del(['./build/**', './dist/**'], {force: true}); }); - -// Real work for dist task. Done in another task to call it via -// run-sequence. -gulp.task('dist-build', gulp.series('build', function() { - var distSources = [ - './package.json', // For NW.js - './manifest.json', // For Chrome app - './eventPage.js', - './*.html', - './tabs/*.html', - './images/**/*', - './_locales/**/*', - './build/*', - './src/css/font-awesome/webfonts/*', - './src/css/opensans_webfontkit/*.{eot,svg,ttf,woff,woff2}', - './resources/*.json', - './resources/models/*', - './resources/osd/analogue/*.mcm', - './resources/motor_order/*.svg', - './resources/sitl/windows/*', - './resources/sitl/linux/*' - ]; - return gulp.src(distSources, { base: '.' }) - .pipe(gulp.dest(distDir)); -})); - -gulp.task('dist', gulp.series('clean', 'dist-build')); - -// Create app directories in ./apps -gulp.task('apps', gulp.series('dist', function(done) { - var builder = new NwBuilder({ - files: './dist/**/*', - buildDir: appsDir, - platforms: getPlatforms(), - flavor: 'normal', - macIcns: './images/inav.icns', - winIco: './images/inav.ico', - version: get_nw_version(), - zip: false - }); - builder.on('log', console.log); - builder.build(function (err) { - if (err) { - console.log("Error building NW apps:" + err); - done(); - return; - } - // Package apps as .zip files - done(); - }); -})); - -function get_nw_version() { - return semver.valid(semver.coerce(require('./package.json').dependencies.nw)); -} - -function get_release_filename_base(platform) { - return 'INAV-Configurator_' + platform; -} - -function get_release_filename(platform, ext, addition = '') { - var pkg = require('./package.json'); - return get_release_filename_base(platform) + addition + '_' + pkg.version + '.' + ext; -} - -function build_win_zip(arch) { - return function build_win_zip_proc(done) { - var pkg = require('./package.json'); - - // Create ZIP - console.log(`Creating ${arch} ZIP file...`); - var src = path.join(appsDir, pkg.name, arch); - var output = fs.createWriteStream(path.join(appsDir, get_release_filename(arch, 'zip'))); - var archive = archiver('zip', { - zlib: { level: 9 } - }); - archive.on('warning', function(err) { throw err; }); - archive.on('error', function(err) { throw err; }); - archive.pipe(output); - archive.directory(src, 'INAV Configurator'); - return archive.finalize(); - } -} - -function build_win_iss(arch) { - return function build_win_iss_proc(done) { - if (!getArguments().installer) { - done(); - return null; - } - - // Create Installer - console.log(`Creating ${arch} Installer...`); - const innoSetup = require('@quanle94/innosetup'); - - const APPS_DIR = './apps/'; - const pkg = require('./package.json'); - - // Parameters passed to the installer script - const parameters = []; - - // Extra parameters to replace inside the iss file - parameters.push(`/Dversion=${pkg.version}`); - parameters.push(`/DarchName=${arch}`); - parameters.push(`/DarchAllowed=${(arch === 'win32') ? 'x86 x64' : 'x64'}`); - parameters.push(`/DarchInstallIn64bit=${(arch === 'win32') ? '' : 'x64'}`); - parameters.push(`/DsourceFolder=${APPS_DIR}`); - parameters.push(`/DtargetFolder=${APPS_DIR}`); - - // Show only errors in console - parameters.push(`/Q`); - - // Script file to execute - parameters.push("assets/windows/installer.iss"); - - innoSetup(parameters, {}, - function(error) { - if (error != null) { - console.error(`Installer for platform ${arch} finished with error ${error}`); - } else { - console.log(`Installer for platform ${arch} finished`); - } - done(); - }); - } -} - -gulp.task('release-win32', gulp.series(build_win_zip('win32'), build_win_iss('win32'))); -gulp.task('release-win64', gulp.series(build_win_zip('win64'), build_win_iss('win64'))); - -gulp.task('release-osx64', function(done) { - var pkg = require('./package.json'); - var src = path.join(appsDir, pkg.name, 'osx64', pkg.name + '.app'); - // Check if we want to sign the .app bundle - if (getArguments().codesign) { - // macapptool can be downloaded from - // https://github.com/fiam/macapptool - // - // Make sure the bundle is well formed - execSync('macapptool', '-v', '1', 'fix', src); - // Sign - const codesignArgs = ['macapptool', '-v', '1', 'sign']; - const codesignIdentity = getArguments()['codesign-identity']; - if (codesignIdentity) { - codesignArgs.push('-i', codesignIdentity); - } - codesignArgs.push('-e', 'entitlements.plist'); - codesignArgs.push(src) - execSync.apply(this, codesignArgs); - - // Check if the bundle is signed - const codesignCheckArgs = [ 'codesign', '-vvv', '--deep', '--strict', src ]; - execSync.apply(this, codesignCheckArgs); - } - - // 'old' .zip mode - if (!getArguments().installer) { - const zipFilename = path.join(appsDir, get_release_filename('macOS', 'zip')); - console.log('Creating ZIP file: ' + zipFilename); - var output = fs.createWriteStream(zipFilename); - var archive = archiver('zip', { - zlib: { level: 9 } - }); - archive.on('warning', function(err) { throw err; }); - archive.on('error', function(err) { throw err; }); - archive.pipe(output); - archive.directory(src, 'INAV Configurator.app'); - output.on('close', function() { - if (getArguments().notarize) { - console.log('Notarizing DMG file: ' + zipFilename); - const notarizeArgs = ['macapptool', '-v', '1', 'notarize']; - const notarizationUsername = getArguments()['notarization-username']; - if (notarizationUsername) { - notarizeArgs.push('-u', notarizationUsername) - } - const notarizationPassword = getArguments()['notarization-password']; - if (notarizationPassword) { - notarizeArgs.push('-p', notarizationPassword) - } - notarizeArgs.push(zipFilename) - execSync.apply(this, notarizeArgs); - } - done(); - }); - archive.finalize(); - } - // 'new' .dmg mode - else { - const appdmg = require('appdmg'); - - var target = path.join(appsDir, get_release_filename('macOS', 'dmg')); - console.log('Creating DMG file: ' + target); - var basepath = path.join(appsDir, pkg.name, 'osx64'); - console.log('Base path: ' + basepath); - - if (fs.existsSync(target)) { - fs.unlinkSync(target); - } - - var specs = {}; - - specs["title"] = "INAV Configurator"; - specs["contents"] = [ - { "x": 448, "y": 342, "type": "link", "path": "/Applications" }, - { "x": 192, "y": 344, "type": "file", "path": pkg.name + ".app", "name": "INAV Configurator.app" }, - ]; - specs["background"] = path.join(__dirname, 'assets/osx/dmg-background.png'); - specs["format"] = "UDZO"; - specs["window"] = { - "size": { - "width": 638, - "height": 479, - } - }; - - const codesignIdentity = getArguments()['codesign-identity']; - if (getArguments().codesign) { - specs['code-sign'] = { - 'signing-identity': codesignIdentity, - } - } - - const ee = appdmg({ - target: target, - basepath: basepath, - specification: specs, - }); - - ee.on('progress', function(info) { - //console.log(info); - }); - - ee.on('error', function(err) { - console.log(err); - }); - - ee.on('finish', function() { - if (getArguments().codesign) { - // Check if the bundle is signed - const codesignCheckArgs = [ 'codesign', '-vvv', '--deep', '--strict', target ]; - execSync.apply(this, codesignCheckArgs); - } - if (getArguments().notarize) { - console.log('Notarizing DMG file: ' + target); - const notarizeArgs = ['xcrun', 'notarytool', 'submit']; - notarizeArgs.push(target); - const notarizationUsername = getArguments()['notarization-username']; - if (notarizationUsername) { - notarizeArgs.push('--apple-id', notarizationUsername) - } else { - throw new Error('Missing notarization username'); - } - const notarizationPassword = getArguments()['notarization-password']; - if (notarizationPassword) { - notarizeArgs.push('--password', notarizationPassword) - } else { - throw new Error('Missing notarization password'); - } - const notarizationTeamId = getArguments()['notarization-team-id']; - if (notarizationTeamId) { - notarizeArgs.push('--team-id', notarizationTeamId) - } else { - throw new Error('Missing notarization Team ID'); - } - notarizeArgs.push('--wait'); - - const notarizationWebhook = getArguments()['notarization-webhook']; - if (notarizationWebhook) { - notarizeArgs.push('--webhook', notarizationWebhook); - } - execSync.apply(this, notarizeArgs); - - console.log('Stapling DMG file: ' + target); - const stapleArgs = ['xcrun', 'stapler', 'staple']; - stapleArgs.push(target); - execSync.apply(this, stapleArgs); - - console.log('Checking DMG file: ' + target); - const checkArgs = ['spctl', '-vvv', '--assess', '--type', 'install', target]; - execSync.apply(this, checkArgs); - } - done(); - }); - } -}); - -function post_build(arch, folder) { - return function post_build_linux(done) { - if ((arch === 'linux32') || (arch === 'linux64')) { - const metadata = require('./package.json'); - // Copy Ubuntu launcher scripts to destination dir - const launcherDir = path.join(folder, metadata.name, arch); - console.log(`Copy Ubuntu launcher scripts to ${launcherDir}`); - return gulp.src('assets/linux/**') - .pipe(gulp.dest(launcherDir)); - } - - return done(); - } -} - -// Create the dir directory, with write permissions -function createDirIfNotExists(dir) { - fs.mkdir(dir, '0775', function(err) { - if (err && err.code !== 'EEXIST') { - throw err; - } - }); -} - -function release_deb(arch) { - return function release_deb_proc(done) { - if (!getArguments().installer) { - done(); - return null; - } - - // Check if dpkg-deb exists - if (!commandExistsSync('dpkg-deb')) { - console.warn(`dpkg-deb command not found, not generating deb package for ${arch}`); - done(); - return null; - } - - const deb = require('gulp-debian'); - const LINUX_INSTALL_DIR = '/opt/inav'; - const metadata = require('./package.json'); - - console.log(`Generating deb package for ${arch}`); - - return gulp.src([path.join(appsDir, metadata.name, arch, '*')]) - .pipe(deb({ - package: metadata.name, - version: metadata.version, - section: 'base', - priority: 'optional', - architecture: getLinuxPackageArch('deb', arch), - maintainer: metadata.author, - description: metadata.description, - preinst: [`rm -rf ${LINUX_INSTALL_DIR}/${metadata.name}`], - postinst: [ - `chown root:root ${LINUX_INSTALL_DIR}`, - `chown -R root:root ${LINUX_INSTALL_DIR}/${metadata.name}`, - `xdg-desktop-menu install ${LINUX_INSTALL_DIR}/${metadata.name}/${metadata.name}.desktop`, - ], - prerm: [`xdg-desktop-menu uninstall ${metadata.name}.desktop`], - depends: ['libgconf-2-4', 'libatomic1'], - changelog: [], - _target: `${LINUX_INSTALL_DIR}/${metadata.name}`, - _out: appsDir, - _copyright: 'assets/linux/copyright', - _clean: true, - })); - } -} - -function post_release_deb(arch) { - return function post_release_linux_deb(done) { - if (!getArguments().installer) { - done(); - return null; - } - if ((arch === 'linux32') || (arch === 'linux64')) { - var rename = require("gulp-rename"); - const metadata = require('./package.json'); - const renameFrom = path.join(appsDir, metadata.name + '_' + metadata.version + '_' + getLinuxPackageArch('.deb', arch) + '.deb'); - const renameTo = path.join(appsDir, get_release_filename_base(arch) + '_' + metadata.version + '.deb'); - // Rename .deb build to common naming - console.log(`Renaming .deb installer ${renameFrom} to ${renameTo}`); - return gulp.src(renameFrom) - .pipe(rename(renameTo)) - .pipe(gulp.dest(".")); - } - - return done(); - } -} - -function release_rpm(arch) { - return function release_rpm_proc(done) { - if (!getArguments().installer) { - done(); - return null; - } - - // Check if rpmbuild exists - if (!commandExistsSync('rpmbuild')) { - console.warn(`rpmbuild command not found, not generating rpm package for ${arch}`); - done(); - return; - } - - const buildRpm = require('rpm-builder'); - const NAME_REGEX = /-/g; - const LINUX_INSTALL_DIR = '/opt/inav'; - const metadata = require('./package.json'); - - console.log(`Generating rpm package for ${arch}`); - - // The buildRpm does not generate the folder correctly, manually - createDirIfNotExists(appsDir); - - const options = { - name: get_release_filename_base(arch), // metadata.name, - version: metadata.version.replace(NAME_REGEX, '_'), // RPM does not like release candidate versions - buildArch: getLinuxPackageArch('rpm', arch), - vendor: metadata.author, - summary: metadata.description, - license: 'GNU General Public License v3.0', - requires: ['libatomic1'], - prefix: '/opt', - files: [{ - cwd: path.join(appsDir, metadata.name, arch), - src: '*', - dest: `${LINUX_INSTALL_DIR}/${metadata.name}`, - }], - postInstallScript: [`xdg-desktop-menu install ${LINUX_INSTALL_DIR}/${metadata.name}/${metadata.name}.desktop`], - preUninstallScript: [`xdg-desktop-menu uninstall ${metadata.name}.desktop`], - tempDir: path.join(appsDir, `tmp-rpm-build-${arch}`), - keepTemp: false, - verbose: false, - rpmDest: appsDir, - execOpts: { maxBuffer: 1024 * 1024 * 16 }, - }; - - buildRpm(options, function(err) { - if (err) { - console.error(`Error generating rpm package: ${err}`); - } - done(); - }); - } -} - -function getLinuxPackageArch(type, arch) { - let packArch; - - switch (arch) { - case 'linux32': - packArch = 'i386'; - break; - case 'linux64': - if (type === 'rpm') { - packArch = 'x86_64'; - } else { - packArch = 'amd64'; - } - break; - default: - console.error(`Package error, arch: ${arch}`); - process.exit(1); - break; - } - - return packArch; -} - -function releaseLinux(bits) { - return function() { - console.log(`Generating zip package for linux${bits}`); - var dirname = 'linux' + bits; - var pkg = require('./package.json'); - var src = path.join(appsDir, pkg.name, dirname); - var output = fs.createWriteStream(path.join(appsDir, get_release_filename(dirname, 'tar.gz'))); - var archive = archiver('tar', { - zlib: { level: 9 }, - gzip: true - }); - archive.on('warning', function(err) { throw err; }); - archive.on('error', function(err) { throw err; }); - archive.pipe(output); - archive.directory(src, 'INAV Configurator'); - return archive.finalize(); - } -} - -gulp.task('release-linux32', gulp.series(releaseLinux(32), post_build('linux32', appsDir), release_deb('linux32'), post_release_deb('linux32'))); -gulp.task('release-linux64', gulp.series(releaseLinux(64), post_build('linux64', appsDir), release_deb('linux64'), post_release_deb('linux64'), release_rpm('linux64'))); - -// Create distributable .zip files in ./apps -gulp.task('release', gulp.series('apps', getPlatforms().map(function(v) { return 'release-' + v; }))); - -gulp.task('watch', function () { - for(var k in output) { - gulp.watch(sources[k], gulp.series(get_task_name(k))); - } -}); - -gulp.task('default', gulp.series('build')); diff --git a/images/icons/cf_icon_setup_white.svg b/images/icons/cf_icon_setup_white.svg index 7501a9f07..353d09025 100644 --- a/images/icons/cf_icon_setup_white.svg +++ b/images/icons/cf_icon_setup_white.svg @@ -1,16 +1,8 @@ - - - - - - - - - - + + + + + + + + diff --git a/images/inav-installing.gif b/images/inav-installing.gif new file mode 100644 index 000000000..7a8974410 Binary files /dev/null and b/images/inav-installing.gif differ diff --git a/images/inav.icns b/images/inav.icns index 9417e7cac..23e41e577 100644 Binary files a/images/inav.icns and b/images/inav.icns differ diff --git a/main.html b/index.html old mode 100755 new mode 100644 similarity index 71% rename from main.html rename to index.html index 92c89a58d..651930611 --- a/main.html +++ b/index.html @@ -3,9 +3,13 @@ - - - + + + + + + + INAV Configurator @@ -16,8 +20,12 @@

@@ -49,31 +57,31 @@

+ + +

+ +
@@ -87,6 +95,18 @@

+
+ +
-
- -
@@ -174,6 +182,49 @@

+
    @@ -181,13 +232,16 @@

  • - +
  • - +
  • - +
  • @@ -195,7 +249,7 @@

    • - +
    • @@ -215,14 +269,12 @@

    • -
    • - -
    • - +
    • - +
    • @@ -243,7 +295,8 @@

    • - +
    • @@ -295,9 +348,6 @@

-
- -
-
@@ -312,13 +362,15 @@

diff --git a/tabs/advanced_tuning.js b/tabs/advanced_tuning.js index 6c012fbcf..06c195b74 100644 --- a/tabs/advanced_tuning.js +++ b/tabs/advanced_tuning.js @@ -1,18 +1,40 @@ 'use strict'; +const path = require('path'); + +const MSPCodes = require('./../js/msp/MSPCodes'); +const MSP = require('./../js/msp'); +const { GUI, TABS } = require('./../js/gui'); +const FC = require('./../js/fc'); +const Settings = require('./../js/settings'); +const i18n = require('./../js/localization'); + TABS.advanced_tuning = {}; TABS.advanced_tuning.initialize = function (callback) { if (GUI.active_tab != 'advanced_tuning') { GUI.active_tab = 'advanced_tuning'; - googleAnalytics.sendAppView('AdvancedTuning'); } loadHtml(); + function save_to_eeprom() { + console.log('save_to_eeprom'); + MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function () { + GUI.log(i18n.getMessage('eepromSaved')); + + GUI.tab_switch_cleanup(function () { + MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, function () { + GUI.log(i18n.getMessage('deviceRebooting')); + GUI.handleReconnect($('.tab_advanced_tuning a')); + }); + }); + }); + } + function loadHtml() { - GUI.load("./tabs/advanced_tuning.html", Settings.processHtml(function () { + GUI.load(path.join(__dirname, "advanced_tuning.html"), Settings.processHtml(function () { if (FC.isAirplane()) { $('.airplaneTuning').show(); @@ -36,22 +58,22 @@ TABS.advanced_tuning.initialize = function (callback) { GUI.simpleBind(); - localize(); + i18n.localize();; // Set up required field warnings - $('#launchIdleThr').keyup(function() { + $('#launchIdleThr').on('keyup', () => { TABS.advanced_tuning.checkRequirements_IdleThrottle(); }); - $('#launchIdleDelay').keyup(function() { + $('#launchIdleDelay').on('keyup', () => { TABS.advanced_tuning.checkRequirements_IdleThrottle(); }); - $('#rthHomeAltitude').keyup(function() { + $('#rthHomeAltitude').on('keyup', () => { TABS.advanced_tuning.checkRequirements_LinearDescent(); }); - $('#rthUseLinearDescent').change(function() { + $('#rthUseLinearDescent').on('change', function () { TABS.advanced_tuning.checkRequirements_LinearDescent(); }); @@ -59,39 +81,15 @@ TABS.advanced_tuning.initialize = function (callback) { TABS.advanced_tuning.checkRequirements_IdleThrottle(); TABS.advanced_tuning.checkRequirements_LinearDescent(); - $('a.save').click(function () { - Settings.saveInputs().then(function () { - var self = this; - MSP.promise(MSPCodes.MSP_EEPROM_WRITE); - var oldText = $(this).text(); - $(this).html("Saved"); - setTimeout(function () { - $(self).html(oldText); - }, 2000); - reboot(); - }); + $('a.save').on('click', function () { + Settings.saveInputs(save_to_eeprom); }); GUI.content_ready(callback); })); } - - function reboot() { - //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); - GUI.tab_switch_cleanup(function () { - MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); - }); - } - - function reinitialize() { - //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('deviceRebooting')); - GUI.handleReconnect($('.tab_advanced_tuning a')); - } }; -$incLD = 0; TABS.advanced_tuning.checkRequirements_IdleThrottle = function() { let idleThrottle = $('#launchIdleThr'); diff --git a/tabs/auxiliary.js b/tabs/auxiliary.js index f04852420..8533c48f4 100644 --- a/tabs/auxiliary.js +++ b/tabs/auxiliary.js @@ -1,5 +1,20 @@ 'use strict'; + +const path = require('path'); +const wNumb = require('wnumb/wNumb') +const Store = require('electron-store'); +const store = new Store(); + +const mspHelper = require('./../js/msp/MSPHelper'); +const MSPCodes = require('./../js/msp/MSPCodes'); +const MSP = require('./../js/msp'); +const { GUI, TABS } = require('./../js/gui'); +const FC = require('./../js/fc'); +const adjustBoxNameIfPeripheralWithModeID = require('./../js/peripherals'); +const i18n = require('./../js/localization'); +const interval = require('./../js/intervals'); + var ORIG_AUX_CONFIG_IDS = []; TABS.auxiliary = {}; @@ -7,46 +22,47 @@ TABS.auxiliary = {}; TABS.auxiliary.initialize = function (callback) { GUI.active_tab_ref = this; GUI.active_tab = 'auxiliary'; - googleAnalytics.sendAppView('Auxiliary'); - function get_mode_ranges() { - MSP.send_message(MSPCodes.MSP_MODE_RANGES, false, false, get_box_ids); - } + let LOCAL_AUX_CONFIG = []; + let LOCAL_AUX_CONFIG_IDS = []; + + MSP.send_message(MSPCodes.MSP_MODE_RANGES, false, false, get_box_ids); function get_box_ids() { - MSP.send_message(MSPCodes.MSP_BOXIDS, false, false, get_rc_data); - } + MSP.send_message(MSPCodes.MSP_BOXIDS, false, false, function () { + FC.generateAuxConfig(); - function get_rc_data() { - if (SERIAL_CONFIG.ports.length == 0) { - MSP.send_message(MSPCodes.MSP_RC, false, false, get_ports_data); - } else { - MSP.send_message(MSPCodes.MSP_RC, false, false, load_html); - } + //Copy global settings into local ones + LOCAL_AUX_CONFIG = Array.from(FC.AUX_CONFIG); + LOCAL_AUX_CONFIG_IDS = Array.from(FC.AUX_CONFIG_IDS); + + get_rc_data(); + }); } - function get_ports_data() { - MSP.send_message(MSPCodes.MSP2_CF_SERIAL_CONFIG, false, false, load_html); + function get_rc_data() { + MSP.send_message(MSPCodes.MSP_RC, false, false, load_html); } function load_html() { sort_modes_for_display(); - GUI.load("./tabs/auxiliary.html", process_html); + GUI.load(path.join(__dirname, "auxiliary.html"), process_html); } - MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false, get_mode_ranges); - // This object separates out the dividers. This is also used to order the modes const modeSections = {}; modeSections["Arming"] = ["ARM", "PREARM"]; - modeSections["Flight Modes"] = ["ANGLE", "HORIZON", "MANUAL"]; + modeSections["Flight Modes"] = ["ANGLE", "HORIZON", "MANUAL", "ANGLE HOLD"]; modeSections["Navigation Modes"] = ["NAV COURSE HOLD", "NAV CRUISE", "NAV POSHOLD", "NAV RTH", "NAV WP", "GCS NAV"]; modeSections["Flight Mode Modifiers"] = ["NAV ALTHOLD", "HEADING HOLD", "AIR MODE", "SOARING", "SURFACE", "TURN ASSIST"]; modeSections["Fixed Wing"] = ["AUTO TUNE", "SERVO AUTOTRIM", "AUTO LEVEL TRIM", "NAV LAUNCH", "LOITER CHANGE", "FLAPERON"]; modeSections["Multi-rotor"] = ["FPV ANGLE MIX", "TURTLE", "MC BRAKING", "HEADFREE", "HEADADJ"]; modeSections["OSD Modes"] = ["OSD OFF", "OSD ALT 1", "OSD ALT 2", "OSD ALT 3"]; modeSections["FPV Camera Modes"] = ["CAMSTAB", "CAMERA CONTROL 1", "CAMERA CONTROL 2", "CAMERA CONTROL 3"]; - modeSections["Misc Modes"] = ["BEEPER", "LEDS OFF", "LIGHTS", "HOME RESET", "WP PLANNER", "MISSION CHANGE", "BLACKBOX", "FAILSAFE", "KILLSWITCH", "TELEMETRY", "MSP RC OVERRIDE", "USER1", "USER2", "USER3", "USER4"]; + modeSections["VTOL"] = ["MIXER PROFILE 2", "MIXER TRANSITION"]; + modeSections["Beeper"] = ["BEEPER", "BEEPER MUTE"]; + modeSections["Gimbal"] = ["GIMBAL LEVEL TILT", "GIMBAL LEVEL ROLL", "GIMBAL LEVEL PAN", "GIMBAL HEADTRACKER", "GIMBAL CENTER"]; + modeSections["Misc Modes"] = ["LEDS OFF", "LIGHTS", "HOME RESET", "WP PLANNER", "MISSION CHANGE", "BLACKBOX", "FAILSAFE", "KILLSWITCH", "TELEMETRY", "MSP RC OVERRIDE", "USER1", "USER2", "USER3", "USER4"]; function sort_modes_for_display() { // Sort the modes @@ -55,21 +71,21 @@ TABS.auxiliary.initialize = function (callback) { var found = false; var sortedID = 0; - for (i=0; i AUX_CONFIG.length) { - for (i=0; i LOCAL_AUX_CONFIG.length) { + for (let i=0; i diff --git a/tabs/calibration.html b/tabs/calibration.html index b0414cbaa..3305944e1 100755 --- a/tabs/calibration.html +++ b/tabs/calibration.html @@ -3,7 +3,7 @@
Calibration
-
+
@@ -71,20 +71,7 @@
-
- +
@@ -189,4 +176,4 @@

-
\ No newline at end of file +
diff --git a/tabs/calibration.js b/tabs/calibration.js index 05edcc398..4566fc416 100755 --- a/tabs/calibration.js +++ b/tabs/calibration.js @@ -1,6 +1,18 @@ -/*global chrome */ 'use strict'; +const path = require('path'); + +const MSPChainerClass = require('./../js/msp/MSPchainer'); +const mspHelper = require('./../js/msp/MSPHelper'); +const MSPCodes = require('./../js/msp/MSPCodes'); +const MSP = require('./../js/msp'); +const { GUI, TABS } = require('./../js/gui'); +const FC = require('./../js/fc'); +const timeout = require('./../js/timeouts'); +const interval = require('./../js/intervals'); +const i18n = require('./../js/localization'); +const jBox = require('./../js/libraries/jBox/jBox.min'); + TABS.calibration = {}; TABS.calibration.model = (function () { @@ -16,7 +28,7 @@ TABS.calibration.model = (function () { } else { var count = 0; for (var i = 0; i < 6; i++) { - if (CALIBRATION_DATA.acc['Pos' + i] === 1) { + if (FC.CALIBRATION_DATA.acc['Pos' + i] === 1) { count++; } } @@ -50,7 +62,6 @@ TABS.calibration.initialize = function (callback) { if (GUI.active_tab != 'calibration') { GUI.active_tab = 'calibration'; - googleAnalytics.sendAppView('Calibration'); } loadChainer.setChain([ mspHelper.queryFcStatus, @@ -68,7 +79,7 @@ TABS.calibration.initialize = function (callback) { function reboot() { //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); + GUI.log(i18n.getMessage('configurationEepromSaved')); GUI.tab_switch_cleanup(function() { MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); @@ -77,19 +88,19 @@ TABS.calibration.initialize = function (callback) { function reinitialize() { //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('deviceRebooting')); + GUI.log(i18n.getMessage('deviceRebooting')); GUI.handleReconnect($('.tab_calibration a')); } function loadHtml() { - GUI.load("./tabs/calibration.html", processHtml); + GUI.load(path.join(__dirname, "calibration.html"), processHtml); } function updateCalibrationSteps() { for (var i = 0; i < 6; i++) { var $element = $('[data-step="' + (i + 1) + '"]'); - if (CALIBRATION_DATA.acc['Pos' + i] === 0) { + if (FC.CALIBRATION_DATA.acc['Pos' + i] === 0) { $element.removeClass('finished').removeClass('active'); } else { $element.addClass("finished").removeClass('active'); @@ -100,12 +111,12 @@ TABS.calibration.initialize = function (callback) { function updateSensorData() { var pos = ['X', 'Y', 'Z']; pos.forEach(function (item) { - $('[name=accGain' + item + ']').val(CALIBRATION_DATA.accGain[item]); - $('[name=accZero' + item + ']').val(CALIBRATION_DATA.accZero[item]); - $('[name=Mag' + item + ']').val(CALIBRATION_DATA.magZero[item]); - $('[name=MagGain' + item + ']').val(CALIBRATION_DATA.magGain[item]); + $('[name=accGain' + item + ']').val(FC.CALIBRATION_DATA.accGain[item]); + $('[name=accZero' + item + ']').val(FC.CALIBRATION_DATA.accZero[item]); + $('[name=Mag' + item + ']').val(FC.CALIBRATION_DATA.magZero[item]); + $('[name=MagGain' + item + ']').val(FC.CALIBRATION_DATA.magGain[item]); }); - $('[name=OpflowScale]').val(CALIBRATION_DATA.opflow.Scale); + $('[name=OpflowScale]').val(FC.CALIBRATION_DATA.opflow.Scale); updateCalibrationSteps(); } @@ -129,8 +140,8 @@ TABS.calibration.initialize = function (callback) { if (TABS.calibration.model.getStep() === null) { for (var i = 0; i < 6; i++) { - if (CALIBRATION_DATA.acc['Pos' + i] === 1) { - CALIBRATION_DATA.acc['Pos' + i] = 0; + if (FC.CALIBRATION_DATA.acc['Pos' + i] === 1) { + FC.CALIBRATION_DATA.acc['Pos' + i] = 0; } } updateCalibrationSteps(); @@ -154,7 +165,7 @@ TABS.calibration.initialize = function (callback) { modalProcessing = new jBox('Modal', { width: 400, - height: 100, + height: 120, animation: false, closeOnClick: false, closeOnEsc: false, @@ -162,28 +173,28 @@ TABS.calibration.initialize = function (callback) { }).open(); MSP.send_message(MSPCodes.MSP_ACC_CALIBRATION, false, false, function () { - GUI.log(chrome.i18n.getMessage('initialSetupAccelCalibStarted')); + GUI.log(i18n.getMessage('initialSetupAccelCalibStarted')); }); - helper.timeout.add('acc_calibration_timeout', function () { + timeout.add('acc_calibration_timeout', function () { $button.removeClass('disabled'); modalProcessing.close(); MSP.send_message(MSPCodes.MSP_CALIBRATION_DATA, false, false, checkFinishAccCalibrate); - GUI.log(chrome.i18n.getMessage('initialSetupAccelCalibEnded')); + GUI.log(i18n.getMessage('initialSetupAccelCalibEnded')); }, 2000); } } function setupCalibrationButton(callback) { if (FC.getAccelerometerCalibrated()) { - $('#calibrate-start-button').html(chrome.i18n.getMessage("AccResetBtn")); - $('#calibrate-start-button').prop("title", chrome.i18n.getMessage("AccResetBtn")); + $('#calibrate-start-button').html(i18n.getMessage("AccResetBtn")); + $('#calibrate-start-button').prop("title", i18n.getMessage("AccResetBtn")); $('#calibrate-start-button').removeClass("calibrate"); $('#calibrate-start-button').addClass("resetCalibration"); } else { - $('#calibrate-start-button').html(chrome.i18n.getMessage("AccBtn")); - $('#calibrate-start-button').prop("title", chrome.i18n.getMessage("AccBtn")); + $('#calibrate-start-button').html(i18n.getMessage("AccBtn")); + $('#calibrate-start-button').prop("title", i18n.getMessage("AccBtn")); $('#calibrate-start-button').addClass("calibrate"); $('#calibrate-start-button').removeClass("resetCalibration"); } @@ -198,14 +209,14 @@ TABS.calibration.initialize = function (callback) { calibrateNew(); } - if (callback) callback(); + } function resetAccCalibration() { var pos = ['X', 'Y', 'Z']; pos.forEach(function (item) { - CALIBRATION_DATA.accGain[item] = 4096; - CALIBRATION_DATA.accZero[item] = 0; + FC.CALIBRATION_DATA.accGain[item] = 4096; + FC.CALIBRATION_DATA.accZero[item] = 0; }); saveChainer.execute(); @@ -213,23 +224,23 @@ TABS.calibration.initialize = function (callback) { function processHtml() { $('#calibrateButtonSave').on('click', function () { - CALIBRATION_DATA.opflow.Scale = parseFloat($('[name=OpflowScale]').val()); + FC.CALIBRATION_DATA.opflow.Scale = parseFloat($('[name=OpflowScale]').val()); saveChainer.execute(); }); - if (SENSOR_CONFIG.magnetometer === 0) { + if (FC.SENSOR_CONFIG.magnetometer === 0) { //Comment for test $('#mag_btn, #mag-calibrated-data').css('pointer-events', 'none').css('opacity', '0.4'); } - if (SENSOR_CONFIG.opflow === 0) { + if (FC.SENSOR_CONFIG.opflow === 0) { //Comment for test $('#opflow_btn, #opflow-calibrated-data').css('pointer-events', 'none').css('opacity', '0.4'); } $('#mag_btn').on('click', function () { MSP.send_message(MSPCodes.MSP_MAG_CALIBRATION, false, false, function () { - GUI.log(chrome.i18n.getMessage('initialSetupMagCalibStarted')); + GUI.log(i18n.getMessage('initialSetupMagCalibStarted')); }); var button = $(this); @@ -238,7 +249,7 @@ TABS.calibration.initialize = function (callback) { let modalProcessing = new jBox('Modal', { width: 400, - height: 100, + height: 120, animation: false, closeOnClick: false, closeOnEsc: false, @@ -246,20 +257,20 @@ TABS.calibration.initialize = function (callback) { }).open(); var countdown = 30; - helper.interval.add('compass_calibration_interval', function () { + interval.add('compass_calibration_interval', function () { countdown--; if (countdown === 0) { setTimeout(function () { $(button).removeClass('disabled'); modalProcessing.close(); - GUI.log(chrome.i18n.getMessage('initialSetupMagCalibEnded')); + GUI.log(i18n.getMessage('initialSetupMagCalibEnded')); MSP.send_message(MSPCodes.MSP_CALIBRATION_DATA, false, false, updateSensorData); - helper.interval.remove('compass_calibration_interval'); + interval.remove('compass_calibration_interval'); //Cleanup - delete modalProcessing; + //delete modalProcessing; $('.jBox-wrapper').remove(); }, 1000); } else { @@ -271,7 +282,7 @@ TABS.calibration.initialize = function (callback) { $('#opflow_btn').on('click', function () { MSP.send_message(MSPCodes.MSP2_INAV_OPFLOW_CALIBRATION, false, false, function () { - GUI.log(chrome.i18n.getMessage('initialSetupOpflowCalibStarted')); + GUI.log(i18n.getMessage('initialSetupOpflowCalibStarted')); }); var button = $(this); @@ -280,7 +291,7 @@ TABS.calibration.initialize = function (callback) { modalProcessing = new jBox('Modal', { width: 400, - height: 100, + height: 120, animation: false, closeOnClick: false, closeOnEsc: false, @@ -288,31 +299,31 @@ TABS.calibration.initialize = function (callback) { }).open(); var countdown = 30; - helper.interval.add('opflow_calibration_interval', function () { + interval.add('opflow_calibration_interval', function () { countdown--; $('#modal-opflow-countdown').text(countdown); if (countdown === 0) { $(button).removeClass('disabled'); modalProcessing.close(); - GUI.log(chrome.i18n.getMessage('initialSetupOpflowCalibEnded')); + GUI.log(i18n.getMessage('initialSetupOpflowCalibEnded')); MSP.send_message(MSPCodes.MSP_CALIBRATION_DATA, false, false, updateSensorData); - helper.interval.remove('opflow_calibration_interval'); + interval.remove('opflow_calibration_interval'); } }, 1000); }); - $('#modal-start-button').click(function () { + $('#modal-start-button').on('click', function () { modalStart.close(); TABS.calibration.model.next(); }); - $('#modal-stop-button').click(function () { + $('#modal-stop-button').on('click', function () { modalStop.close(); }); // translate to user-selected language - localize(); + i18n.localize(); setupCalibrationButton(); $('#calibrate-start-button').on('click', actionCalibrateButton); diff --git a/tabs/cli.js b/tabs/cli.js index 166de5787..3e86f00e2 100644 --- a/tabs/cli.js +++ b/tabs/cli.js @@ -1,5 +1,23 @@ 'use strict'; -/*global chrome,GUI,TABS,nwdialog,$*/ + +const path = require('path'); +const fs = require('fs'); +const { dialog } = require("@electron/remote"); + +const MSP = require('./../js/msp'); +const mspQueue = require('./../js/serial_queue'); +const { GUI, TABS } = require('./../js/gui'); +const CONFIGURATOR = require('./../js/data_storage'); +var timeout = require('./../js/timeouts'); +const i18n = require('./../js/localization'); +const { globalSettings } = require('./../js/globalSettings'); +const CliAutoComplete = require('./../js/CliAutoComplete'); +const { ConnectionType } = require('./../js/connection/connection'); +const jBox = require('./../js/libraries/jBox/jBox.min'); +const mspDeduplicationQueue = require('./../js/msp/mspDeduplicationQueue'); +const FC = require('./../js/fc'); +const { generateFilename } = require('./../js/helpers'); + TABS.cli = { lineDelayMs: 50, profileSwitchDelayMs: 100, @@ -49,7 +67,7 @@ function copyToClipboard(text) { const button = $('.tab-cli .copy'); const origText = button.text(); const origWidth = button.css("width"); - button.text(chrome.i18n.getMessage("cliCopySuccessful")); + button.text(i18n.getMessage("cliCopySuccessful")); button.css({ width: origWidth, textAlign: "center", @@ -67,49 +85,8 @@ function copyToClipboard(text) { console.warn(ex); } - function nwCopy(text) { - try { - let clipboard = require('nw.gui').Clipboard.get(); - clipboard.set(text, "text"); - onCopySuccessful(); - } catch (ex) { - onCopyFailed(ex); - } - } - - function webCopy(text) { - navigator.clipboard.writeText(text) - .then(onCopySuccessful, onCopyFailed); - } - - let copyFunc; - try { - let nwGui = require('nw.gui'); - copyFunc = nwCopy; - } catch (e) { - copyFunc = webCopy; - } - copyFunc(text); -} - -function sendLinesWithDelay(outputArray) { - return (delay, line, index) => { - return new Promise((resolve) => { - helper.timeout.add('CLI_send_slowly', () => { - let processingDelay = TABS.cli.lineDelayMs; - if (line.toLowerCase().startsWith('profile')) { - processingDelay = TABS.cli.profileSwitchDelayMs; - } - const isLastCommand = outputArray.length === index + 1; - if (isLastCommand && TABS.cli.cliBuffer) { - line = getCliCommand(line, TABS.cli.cliBuffer); - } - TABS.cli.sendLine(line, () => { - resolve(processingDelay); - }); - }, delay); - }); - }; + navigator.clipboard.writeText(text) + .then(onCopySuccessful, onCopyFailed); } TABS.cli.initialize = function (callback) { @@ -117,11 +94,11 @@ TABS.cli.initialize = function (callback) { if (GUI.active_tab != 'cli') { GUI.active_tab = 'cli'; - googleAnalytics.sendAppView('CLI'); } // Flush MSP queue as well as all MSP registered callbacks - helper.mspQueue.flush(); + mspQueue.flush(); + mspDeduplicationQueue.flush(); MSP.callbacks_cleanup(); self.outputHistory = ""; @@ -135,16 +112,35 @@ TABS.cli.initialize = function (callback) { return !(nwGui == null && !navigator.clipboard) })(); + function executeCommands(out_string) { self.history.add(out_string.trim()); var outputArray = out_string.split("\n"); - Promise.reduce(outputArray, sendLinesWithDelay(outputArray), 0); + return outputArray.reduce((p, line, index) => + p.then((delay) => + new Promise((resolve) => { + timeout.add('CLI_send_slowly', () => { + let processingDelay = TABS.cli.lineDelayMs; + if (line.toLowerCase().startsWith('profile')) { + processingDelay = TABS.cli.profileSwitchDelayMs; + } + const isLastCommand = outputArray.length === index + 1; + if (isLastCommand && TABS.cli.cliBuffer) { + line = getCliCommand(line, TABS.cli.cliBuffer); + } + TABS.cli.sendLine(line, () => { + resolve(processingDelay); + }); + }, delay); + }) + ), Promise.resolve(0), + ); } - GUI.load("./tabs/cli.html", function () { + GUI.load(path.join(__dirname, "cli.html"), function () { // translate to user-selected language - localize(); + i18n.localize(); $('.cliDocsBtn').attr('href', globalSettings.docsTreeLocation + 'Settings.md'); @@ -155,80 +151,86 @@ TABS.cli.initialize = function (callback) { $(CliAutoComplete).on('build:start', function() { textarea .val('') - .attr('placeholder', chrome.i18n.getMessage('cliInputPlaceholderBuilding')) + .attr('placeholder', i18n.getMessage('cliInputPlaceholderBuilding')) .prop('disabled', true); }); $(CliAutoComplete).on('build:stop', function() { textarea - .attr('placeholder', chrome.i18n.getMessage('cliInputPlaceholder')) + .attr('placeholder', i18n.getMessage('cliInputPlaceholder')) .prop('disabled', false) .focus(); }); - $('.tab-cli .save').click(function() { + $('.tab-cli .save').on('click', function () { var prefix = 'cli'; var suffix = 'txt'; - - var filename = generateFilename(prefix, suffix); - - var accepts = [{ - description: suffix.toUpperCase() + ' files', extensions: [suffix], - }]; - - nwdialog.setContext(document); - nwdialog.saveFileDialog(filename, accepts, '', function(result) { - if (!result) { - GUI.log(chrome.i18n.getMessage('cliSaveToFileAborted')); + var filename = generateFilename(FC.CONFIG, prefix, suffix); + var options = { + defaultPath: filename, + filters: [ + { name: suffix.toUpperCase(), extensions: [suffix] }, + { name: prefix.toUpperCase(), extensions: [prefix] } + ], + }; + dialog.showSaveDialog(options).then(result => { + if (result.canceled) { + GUI.log(i18n.getMessage('cliSaveToFileAborted')); return; } - const fs = require('fs'); - fs.writeFile(result, self.outputHistory, (err) => { + fs.writeFile(result.filePath, self.outputHistory, (err) => { if (err) { - GUI.log(chrome.i18n.getMessage('ErrorWritingFile')); + GUI.log(i18n.getMessage('ErrorWritingFile')); return console.error(err); } - GUI.log(chrome.i18n.getMessage('FileSaved')); + GUI.log(i18n.getMessage('FileSaved')); }); + }).catch (err => { + console.log(err); }); }); - $('.tab-cli .exit').click(function() { + $('.tab-cli .exit').on('click', function () { self.send(getCliCommand('exit\n', TABS.cli.cliBuffer)); }); - $('.tab-cli .savecmd').click(function() { + $('.tab-cli .savecmd').on('click', function () { self.send(getCliCommand('save\n', TABS.cli.cliBuffer)); }); - $('.tab-cli .msc').click(function() { + $('.tab-cli .msc').on('click', function () { self.send(getCliCommand('msc\n', TABS.cli.cliBuffer)); }); - $('.tab-cli .diffall').click(function() { + $('.tab-cli .diffall').on('click', function () { self.outputHistory = ""; $('.tab-cli .window .wrapper').empty(); self.send(getCliCommand('diff all\n', TABS.cli.cliBuffer)); }); - $('.tab-cli .clear').click(function() { + $('.tab-cli .clear').on('click', function () { self.outputHistory = ""; $('.tab-cli .window .wrapper').empty(); }); if (clipboardCopySupport) { - $('.tab-cli .copy').click(function() { + $('.tab-cli .copy').on('click', function () { copyToClipboard(self.outputHistory); }); } else { $('.tab-cli .copy').hide(); } - $('.tab-cli .load').click(function() { - nwdialog.setContext(document); - nwdialog.openFileDialog(".txt", false, '', function(result) { - if (!result) { + $('.tab-cli .load').on('click', function () { + var options = { + filters: [ + { name: 'CLI/TXT', extensions: ['cli', 'txt'] }, + { name: 'ALL', extensions: ['*'] } + ], + }; + dialog.showOpenDialog(options).then( result => { + if (result.canceled) { console.log('No file selected'); return; } @@ -250,36 +252,37 @@ TABS.cli.initialize = function (callback) { closeButton: 'title', animation: false, isolateScroll: false, - title: chrome.i18n.getMessage("cliConfirmSnippetDialogTitle"), + title: i18n.getMessage("cliConfirmSnippetDialogTitle"), content: $('#snippetpreviewcontent'), - onCreated: () => $("#snippetpreviewcontent a.confirm").click(() => executeSnippet()), + onCreated: () => $("#snippetpreviewcontent a.confirm").on('click', executeSnippet), }); } previewArea.val(result); self.GUI.snippetPreviewWindow.open(); } - const fs = require('fs'); + if (result.filePaths.length == 1) { + fs.readFile(result.filePaths[0], (err, data) => { + if (err) { + GUI.log(i18n.getMessage('ErrorReadingFile')); + return console.error(err); + } - fs.readFile(result, (err, data) => { - if (err) { - GUI.log(chrome.i18n.getMessage('ErrorReadingFile')); - return console.error(err); - } - - previewCommands(data); - }); + previewCommands(data); + }); + } + }).catch (err => { + console.log(err); }); }); // Tab key detection must be on keydown, // `keypress`/`keyup` happens too late, as `textarea` will have already lost focus. - textarea.keydown(function (event) { + textarea.on('keydown', function (event) { const tabKeyCode = 9; if (event.which == tabKeyCode) { // prevent default tabbing behaviour event.preventDefault(); - if (!CliAutoComplete.isEnabled()) { const outString = textarea.val(); const lastCommand = outString.split("\n").pop(); @@ -296,7 +299,7 @@ TABS.cli.initialize = function (callback) { } }); - textarea.keypress(function (event) { + textarea.on('keypress', function (event) { const enterKeyCode = 13; if (event.which == enterKeyCode) { event.preventDefault(); // prevent the adding of new line @@ -312,21 +315,20 @@ TABS.cli.initialize = function (callback) { self.outputHistory = ""; $('.tab-cli .window .wrapper').empty(); } else { - var outputArray = out_string.split("\n"); - Promise.reduce(outputArray, sendLinesWithDelay(outputArray), 0); + executeCommands(out_string); } textarea.val(''); } }); - textarea.keyup(function (event) { + textarea.on('keyup', function (event) { var keyUp = {38: true}, keyDown = {40: true}; - if (CliAutoComplete.isOpen()) { - return; // disable history keys if autocomplete is open - } + if (CliAutoComplete.isOpen()) { + return; // disable history keys if autocomplete is open + } if (event.keyCode in keyUp) { textarea.val(self.history.prev()); @@ -340,7 +342,7 @@ TABS.cli.initialize = function (callback) { // give input element user focus textarea.focus(); - helper.timeout.add('enter_cli', function enter_cli() { + timeout.add('enter_cli', function enter_cli() { // Enter CLI mode var bufferOut = new ArrayBuffer(1); var bufView = new Uint8Array(bufferOut); @@ -356,12 +358,12 @@ TABS.cli.initialize = function (callback) { if (CONFIGURATOR.connection.type == ConnectionType.BLE) { let delay = CONFIGURATOR.connection.deviceDescription.delay; - if (delay > 0) { - helper.timeout.add('cli_delay', () => { + if (delay > 0) { + timeout.add('cli_delay', () => { self.send(getCliCommand("cli_delay " + delay + '\n', TABS.cli.cliBuffer)); - self.send(getCliCommand('# ' + chrome.i18n.getMessage('connectionBleCliEnter') + '\n', TABS.cli.cliBuffer)); + self.send(getCliCommand('# ' + i18n.getMessage('connectionBleCliEnter') + '\n', TABS.cli.cliBuffer)); }, 400); - } + } } GUI.content_ready(callback); @@ -485,15 +487,15 @@ TABS.cli.read = function (readInfo) { if (this.cliBuffer == 'Rebooting') { CONFIGURATOR.cliActive = false; CONFIGURATOR.cliValid = false; - GUI.log(chrome.i18n.getMessage('cliReboot')); - GUI.log(chrome.i18n.getMessage('deviceRebooting')); + GUI.log(i18n.getMessage('cliReboot')); + GUI.log(i18n.getMessage('deviceRebooting')); GUI.handleReconnect(); } } if (!CONFIGURATOR.cliValid && validateText.indexOf('CLI') !== -1) { - GUI.log(chrome.i18n.getMessage('cliEnter')); + GUI.log(i18n.getMessage('cliEnter')); CONFIGURATOR.cliValid = true; if (CliAutoComplete.isEnabled() && !CliAutoComplete.isBuilding()) { @@ -539,7 +541,7 @@ TABS.cli.cleanup = function (callback) { // (another approach is however much more complicated): // we can setup an interval asking for data lets say every 200ms, when data arrives, callback will be triggered and tab switched // we could probably implement this someday - helper.timeout.add('waiting_for_bootup', function waiting_for_bootup() { + timeout.add('waiting_for_bootup', function waiting_for_bootup() { if (callback) callback(); }, 1000); // if we dont allow enough time to reboot, CRC of "first" command sent will fail, keep an eye for this one CONFIGURATOR.cliActive = false; diff --git a/tabs/configuration.html b/tabs/configuration.html index e6d0fd885..7dd52f9c0 100644 --- a/tabs/configuration.html +++ b/tabs/configuration.html @@ -19,7 +19,7 @@
-
@@ -45,7 +45,7 @@
- @@ -78,7 +78,7 @@
- @@ -108,7 +108,7 @@
- @@ -234,6 +234,68 @@
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+
+
diff --git a/tabs/configuration.js b/tabs/configuration.js index 07d05d30f..713b8b76e 100644 --- a/tabs/configuration.js +++ b/tabs/configuration.js @@ -1,20 +1,32 @@ -/*global chrome,GUI,FC_CONFIG,$,mspHelper,googleAnalytics,ADVANCED_CONFIG,VTX_CONFIG,CONFIG,MSPChainerClass,BOARD_ALIGNMENT,TABS,MISC*/ 'use strict'; +const path = require('path'); + +const MSPChainerClass = require('./../js/msp/MSPchainer'); +const mspHelper = require('./../js/msp/MSPHelper'); +const MSPCodes = require('./../js/msp/MSPCodes'); +const MSP = require('./../js/msp'); +const { GUI, TABS } = require('./../js/gui'); +const FC = require('./../js/fc'); +const interval = require('./../js/intervals'); +const VTX = require('./../js/vtx'); +const i18n = require('./../js/localization'); +const Settings = require('./../js/settings'); +const features = require('./../js/feature_framework'); + TABS.configuration = {}; TABS.configuration.initialize = function (callback, scrollPosition) { if (GUI.active_tab != 'configuration') { GUI.active_tab = 'configuration'; - googleAnalytics.sendAppView('Configuration'); + } var loadChainer = new MSPChainerClass(); var loadChain = [ mspHelper.loadFeatures, - mspHelper.loadArmingConfig, mspHelper.loadSensorAlignment, mspHelper.loadAdvancedConfig, mspHelper.loadVTXConfig, @@ -31,7 +43,6 @@ TABS.configuration.initialize = function (callback, scrollPosition) { var saveChain = [ mspHelper.saveAccTrim, - mspHelper.saveArmingConfig, mspHelper.saveAdvancedConfig, mspHelper.saveVTXConfig, mspHelper.saveCurrentMeterConfig, @@ -41,7 +52,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) { ]; function saveSettings(onComplete) { - Settings.saveInputs().then(onComplete); + Settings.saveInputs(onComplete); } saveChainer.setChain(saveChain); @@ -49,7 +60,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) { function reboot() { //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); + GUI.log(i18n.getMessage('configurationEepromSaved')); GUI.tab_switch_cleanup(function () { MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); @@ -58,12 +69,12 @@ TABS.configuration.initialize = function (callback, scrollPosition) { function reinitialize() { //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('deviceRebooting')); + GUI.log(i18n.getMessage('deviceRebooting')); GUI.handleReconnect($('.tab_configuration a')); } function load_html() { - GUI.load("./tabs/configuration.html", Settings.processHtml(process_html)); + GUI.load(path.join(__dirname, "configuration.html"), Settings.processHtml(process_html)); } function process_html() { @@ -71,20 +82,20 @@ TABS.configuration.initialize = function (callback, scrollPosition) { let i; // generate features - var features = FC.getFeatures(); + var fcFeatures = FC.getFeatures(); var features_e = $('.features'); - for (i = 0; i < features.length; i++) { + for (let i = 0; i < fcFeatures.length; i++) { var row_e, tips = [], feature_tip_html = ''; - if (features[i].showNameInTip) { - tips.push(chrome.i18n.getMessage("manualEnablingTemplate").replace("{name}", features[i].name)); + if (fcFeatures[i].showNameInTip) { + tips.push(i18n.getMessage("manualEnablingTemplate").replace("{name}", fcFeatures[i].name)); } - if (features[i].haveTip) { - tips.push(chrome.i18n.getMessage("feature" + features[i].name + "Tip")); + if (fcFeatures[i].haveTip) { + tips.push(i18n.getMessage("feature" + fcFeatures[i].name + "Tip")); } if (tips.length > 0) { @@ -92,36 +103,36 @@ TABS.configuration.initialize = function (callback, scrollPosition) { } row_e = $('
' + - '' + - '
'); features_e.each(function () { - if ($(this).hasClass(features[i].group)) { + if ($(this).hasClass(fcFeatures[i].group)) { $(this).after(row_e); } }); } - helper.features.updateUI($('.tab-configuration'), FEATURES); + features.updateUI($('.tab-configuration'), FC.FEATURES); // translate to user-selected language - localize(); + i18n.localize();; // VTX var config_vtx = $('.config-vtx'); - if (VTX_CONFIG.device_type != VTX.DEV_UNKNOWN) { + if (FC.VTX_CONFIG.device_type != VTX.DEV_UNKNOWN) { var vtx_band = $('#vtx_band'); vtx_band.empty(); var vtx_no_band_note = $('#vtx_no_band'); - if (VTX_CONFIG.band < VTX.BAND_MIN || VTX_CONFIG.band > VTX.BAND_MAX) { - var noBandName = chrome.i18n.getMessage("configurationNoBand"); + if (FC.VTX_CONFIG.band < VTX.BAND_MIN || FC.VTX_CONFIG.band > VTX.BAND_MAX) { + var noBandName = i18n.getMessage("configurationNoBand"); $('').appendTo(vtx_band); vtx_no_band_note.show(); } else { @@ -130,58 +141,58 @@ TABS.configuration.initialize = function (callback, scrollPosition) { for (var ii = 0; ii < VTX.BANDS.length; ii++) { var band_name = VTX.BANDS[ii].name; var option = $(''); - if (VTX.BANDS[ii].code == VTX_CONFIG.band) { + if (VTX.BANDS[ii].code == FC.VTX_CONFIG.band) { option.prop('selected', true); } option.appendTo(vtx_band); } - vtx_band.change(function () { - VTX_CONFIG.band = parseInt($(this).val()); + vtx_band.on('change', function () { + FC.VTX_CONFIG.band = parseInt($(this).val()); }); var vtx_channel = $('#vtx_channel'); vtx_channel.empty(); for (var ii = VTX.CHANNEL_MIN; ii <= VTX.CHANNEL_MAX; ii++) { var option = $(''); - if (ii == VTX_CONFIG.channel) { + if (ii == FC.VTX_CONFIG.channel) { option.prop('selected', true); } option.appendTo(vtx_channel); } - vtx_channel.change(function () { - VTX_CONFIG.channel = parseInt($(this).val()); + vtx_channel.on('change', function () { + FC.VTX_CONFIG.channel = parseInt($(this).val()); }); var vtx_power = $('#vtx_power'); vtx_power.empty(); - var minPower = VTX.getMinPower(VTX_CONFIG.device_type); - var maxPower = VTX.getMaxPower(VTX_CONFIG.device_type); + var minPower = VTX.getMinPower(FC.VTX_CONFIG.device_type); + var maxPower = VTX.getMaxPower(FC.VTX_CONFIG.device_type); for (var ii = minPower; ii <= maxPower; ii++) { var option = $(''); - if (ii == VTX_CONFIG.power) { + if (ii == FC.VTX_CONFIG.power) { option.prop('selected', true); } option.appendTo(vtx_power); } - vtx_power.change(function () { - VTX_CONFIG.power = parseInt($(this).val()); + vtx_power.on('change', function () { + FC.FC.VTX_CONFIG.power = parseInt($(this).val()); }); var vtx_low_power_disarm = $('#vtx_low_power_disarm'); vtx_low_power_disarm.empty(); for (var ii = VTX.LOW_POWER_DISARM_MIN; ii <= VTX.LOW_POWER_DISARM_MAX; ii++) { - var name = chrome.i18n.getMessage("configurationVTXLowPowerDisarmValue_" + ii); + var name = i18n.getMessage("configurationVTXLowPowerDisarmValue_" + ii); if (!name) { name = ii; } var option = $(''); - if (ii == VTX_CONFIG.low_power_disarm) { + if (ii == FC.VTX_CONFIG.low_power_disarm) { option.prop('selected', true); } option.appendTo(vtx_low_power_disarm); } - vtx_low_power_disarm.change(function () { - VTX_CONFIG.low_power_disarm = parseInt($(this).val()); + vtx_low_power_disarm.on('change', function () { + FC.VTX_CONFIG.low_power_disarm = parseInt($(this).val()); }); config_vtx.show(); @@ -196,35 +207,37 @@ TABS.configuration.initialize = function (callback, scrollPosition) { $('#content').scrollTop((scrollPosition) ? scrollPosition : 0); // fill board alignment - $('input[name="board_align_yaw"]').val((BOARD_ALIGNMENT.yaw / 10.0).toFixed(1)); + $('input[name="board_align_yaw"]').val((FC.BOARD_ALIGNMENT.yaw / 10.0).toFixed(1)); // fill magnetometer //UPDATE: moved to GPS tab and hidden - //$('#mag_declination').val(MISC.mag_declination); + //$('#mag_declination').val(FC.MISC.mag_declination); // fill battery voltage - $('#voltagesource').val(MISC.voltage_source); - $('#cells').val(MISC.battery_cells); - $('#celldetectvoltage').val(MISC.vbatdetectcellvoltage); - $('#mincellvoltage').val(MISC.vbatmincellvoltage); - $('#maxcellvoltage').val(MISC.vbatmaxcellvoltage); - $('#warningcellvoltage').val(MISC.vbatwarningcellvoltage); - $('#voltagescale').val(MISC.vbatscale); + $('#voltagesource').val(FC.MISC.voltage_source); + $('#cells').val(FC.MISC.battery_cells); + $('#celldetectvoltage').val(FC.MISC.vbatdetectcellvoltage); + $('#mincellvoltage').val(FC.MISC.vbatmincellvoltage); + $('#maxcellvoltage').val(FC.MISC.vbatmaxcellvoltage); + $('#warningcellvoltage').val(FC.MISC.vbatwarningcellvoltage); + $('#voltagescale').val(FC.MISC.vbatscale); // fill current - $('#currentscale').val(CURRENT_METER_CONFIG.scale); - $('#currentoffset').val(CURRENT_METER_CONFIG.offset / 10); + $('#currentscale').val(FC.CURRENT_METER_CONFIG.scale); + $('#currentoffset').val(FC.CURRENT_METER_CONFIG.offset / 10); // fill battery capacity - $('#battery_capacity').val(MISC.battery_capacity); - $('#battery_capacity_warning').val(Math.round(MISC.battery_capacity_warning * 100 / MISC.battery_capacity)); - $('#battery_capacity_critical').val(Math.round(MISC.battery_capacity_critical * 100 / MISC.battery_capacity)); - $('#battery_capacity_unit').val(MISC.battery_capacity_unit); + $('#battery_capacity').val(FC.MISC.battery_capacity); + let batCapWarn = Math.round(FC.MISC.battery_capacity_warning * 100 / FC.MISC.battery_capacity); + $('#battery_capacity_warning').val(isNaN(batCapWarn) ? "" : batCapWarn); + let batCapWarnCrit = Math.round(FC.MISC.battery_capacity_critical * 100 / FC.MISC.battery_capacity); + $('#battery_capacity_critical').val(isNaN(batCapWarnCrit) ? "" : batCapWarnCrit); + $('#battery_capacity_unit').val(FC.MISC.battery_capacity_unit); let $i2cSpeed = $('#i2c_speed'), $i2cSpeedInfo = $('#i2c_speed-info'); - $i2cSpeed.change(function () { + $i2cSpeed.on('change', function () { let $this = $(this), value = $this.children("option:selected").text(); @@ -234,7 +247,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) { $i2cSpeedInfo.addClass('info-box'); $i2cSpeedInfo.removeClass('warning-box'); - $i2cSpeedInfo.html(chrome.i18n.getMessage('i2cSpeedSuggested800khz')); + $i2cSpeedInfo.html(i18n.getMessage('i2cSpeedSuggested800khz')); $i2cSpeedInfo.show(); } else if (value == "800KHZ") { @@ -246,59 +259,42 @@ TABS.configuration.initialize = function (callback, scrollPosition) { $i2cSpeedInfo.removeClass('ok-box'); $i2cSpeedInfo.removeClass('info-box'); $i2cSpeedInfo.addClass('warning-box'); - $i2cSpeedInfo.html(chrome.i18n.getMessage('i2cSpeedTooLow')); + $i2cSpeedInfo.html(i18n.getMessage('i2cSpeedTooLow')); $i2cSpeedInfo.show(); } }); - $i2cSpeed.change(); + $i2cSpeed.trigger('change'); - $('a.save').click(function () { + $('a.save').on('click', function () { //UPDATE: moved to GPS tab and hidden - //MISC.mag_declination = parseFloat($('#mag_declination').val()); - - ARMING_CONFIG.auto_disarm_delay = parseInt($('input[name="autodisarmdelay"]').val()); - - MISC.battery_cells = parseInt($('#cells').val()); - MISC.voltage_source = parseInt($('#voltagesource').val()); - MISC.vbatdetectcellvoltage = parseFloat($('#celldetectvoltage').val()); - MISC.vbatmincellvoltage = parseFloat($('#mincellvoltage').val()); - MISC.vbatmaxcellvoltage = parseFloat($('#maxcellvoltage').val()); - MISC.vbatwarningcellvoltage = parseFloat($('#warningcellvoltage').val()); - MISC.vbatscale = parseInt($('#voltagescale').val()); - - MISC.battery_capacity = parseInt($('#battery_capacity').val()); - MISC.battery_capacity_warning = parseInt($('#battery_capacity_warning').val() * MISC.battery_capacity / 100); - MISC.battery_capacity_critical = parseInt($('#battery_capacity_critical').val() * MISC.battery_capacity / 100); - MISC.battery_capacity_unit = $('#battery_capacity_unit').val(); - - googleAnalytics.sendEvent('Setting', 'I2CSpeed', $('#i2c_speed').children("option:selected").text()); - - googleAnalytics.sendEvent('Board', 'Accelerometer', $('#sensor-acc').children("option:selected").text()); - googleAnalytics.sendEvent('Board', 'Magnetometer', $('#sensor-mag').children("option:selected").text()); - googleAnalytics.sendEvent('Board', 'Barometer', $('#sensor-baro').children("option:selected").text()); - googleAnalytics.sendEvent('Board', 'Pitot', $('#sensor-pitot').children("option:selected").text()); - - for (var i = 0; i < features.length; i++) { - var featureName = features[i].name; - if (FC.isFeatureEnabled(featureName, features)) { - googleAnalytics.sendEvent('Setting', 'Feature', featureName); - } - } - - helper.features.reset(); - helper.features.fromUI($('.tab-configuration')); - helper.features.execute(function () { - CURRENT_METER_CONFIG.scale = parseInt($('#currentscale').val()); - CURRENT_METER_CONFIG.offset = Math.round(parseFloat($('#currentoffset').val()) * 10); + //FC.MISC.mag_declination = parseFloat($('#mag_declination').val()); + + FC.MISC.battery_cells = parseInt($('#cells').val()); + FC.MISC.voltage_source = parseInt($('#voltagesource').val()); + FC.MISC.vbatdetectcellvoltage = parseFloat($('#celldetectvoltage').val()); + FC.MISC.vbatmincellvoltage = parseFloat($('#mincellvoltage').val()); + FC.MISC.vbatmaxcellvoltage = parseFloat($('#maxcellvoltage').val()); + FC.MISC.vbatwarningcellvoltage = parseFloat($('#warningcellvoltage').val()); + FC.MISC.vbatscale = parseInt($('#voltagescale').val()); + + FC.MISC.battery_capacity = parseInt($('#battery_capacity').val()); + FC.MISC.battery_capacity_warning = parseInt($('#battery_capacity_warning').val() * FC.MISC.battery_capacity / 100); + FC.MISC.battery_capacity_critical = parseInt($('#battery_capacity_critical').val() * FC.MISC.battery_capacity / 100); + FC.MISC.battery_capacity_unit = $('#battery_capacity_unit').val(); + + features.reset(); + features.fromUI($('.tab-configuration')); + features.execute(function () { + FC.CURRENT_METER_CONFIG.scale = parseInt($('#currentscale').val()); + FC.CURRENT_METER_CONFIG.offset = Math.round(parseFloat($('#currentoffset').val()) * 10); saveChainer.execute(); }); }); - - helper.interval.add('config_load_analog', function () { - $('#batteryvoltage').val([ANALOG.voltage.toFixed(2)]); - $('#batterycurrent').val([ANALOG.amperage.toFixed(2)]); + interval.add('config_load_analog', function () { + $('#batteryvoltage').val([FC.ANALOG.voltage.toFixed(2)]); + $('#batterycurrent').val([FC.ANALOG.amperage.toFixed(2)]); }, 100, true); // 10 fps GUI.content_ready(callback); diff --git a/tabs/debug_trace.html b/tabs/debug_trace.html index a1d031f12..f46969c29 100644 --- a/tabs/debug_trace.html +++ b/tabs/debug_trace.html @@ -1,7 +1,7 @@ Debug Trace - +
diff --git a/tabs/ez_tune.html b/tabs/ez_tune.html deleted file mode 100644 index 6545bbfb7..000000000 --- a/tabs/ez_tune.html +++ /dev/null @@ -1,175 +0,0 @@ - -
-
-
-
-
-
-

-
-
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
- -
- -
-
-
-
- -
-
-
- - -
- -
-
-
- -
-
- -
- -
-
-
- -
-
- -
- -
-
-
- -
-
- -
- -
-
-
- -
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
- -
- -
-
-
-
- -
- -
-

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 PIDFF
-

- - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
-
-
-
- -
-
-
\ No newline at end of file diff --git a/tabs/ez_tune.js b/tabs/ez_tune.js deleted file mode 100644 index 8128deb5e..000000000 --- a/tabs/ez_tune.js +++ /dev/null @@ -1,161 +0,0 @@ -/*global chrome,helper,mspHelper*/ -'use strict'; - -TABS.ez_tune = { - -}; - -TABS.ez_tune.initialize = function (callback) { - - let loadChainer = new MSPChainerClass(); - - let loadChain = [ - mspHelper.loadEzTune, - ]; - - let EZ_TUNE_PID_RP_DEFAULT = [40, 75, 23, 100]; - let EZ_TUNE_PID_YAW_DEFAULT = [45, 80, 0, 100]; - - loadChain.push(mspHelper.loadRateProfileData); - - loadChainer.setChain(loadChain); - loadChainer.setExitPoint(load_html); - loadChainer.execute(); - - var saveChainer = new MSPChainerClass(); - - var saveChain = [ - mspHelper.saveEzTune, - mspHelper.saveToEeprom - ]; - - saveChainer.setChain(saveChain); - saveChainer.setExitPoint(reboot); - - function reboot() { - //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); - GUI.tab_switch_cleanup(function () { - MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); - }); - } - - function reinitialize() { - GUI.log(chrome.i18n.getMessage('deviceRebooting')); - GUI.handleReconnect($('.tab_ez_tune a')); - } - - if (GUI.active_tab != 'ez_tune') { - GUI.active_tab = 'ez_tune'; - googleAnalytics.sendAppView('Ez Tune'); - } - - function load_html() { - GUI.load("./tabs/ez_tune.html", Settings.processHtml(process_html)); - } - - function getYawPidScale(input) { - const normalized = (input - 100) * 0.01; - - return 1.0 + (normalized * 0.5); - } - - function scaleRange(x, srcMin, srcMax, destMin, destMax) { - let a = (destMax - destMin) * (x - srcMin); - let b = srcMax - srcMin; - return ((a / b) + destMin); - } - - function updatePreview() { - - let axisRatio = $('#ez_tune_axis_ratio').val() / 100; - let response = $('#ez_tune_response').val(); - let damping = $('#ez_tune_damping').val(); - let stability = $('#ez_tune_stability').val(); - let aggressiveness = $('#ez_tune_aggressiveness').val(); - let rate = $('#ez_tune_rate').val(); - let expo = $('#ez_tune_expo').val(); - - $('#preview-roll-p').html(Math.floor(EZ_TUNE_PID_RP_DEFAULT[0] * response / 100)); - $('#preview-roll-i').html(Math.floor(EZ_TUNE_PID_RP_DEFAULT[1] * stability / 100)); - $('#preview-roll-d').html(Math.floor(EZ_TUNE_PID_RP_DEFAULT[2] * damping / 100)); - $('#preview-roll-ff').html(Math.floor(EZ_TUNE_PID_RP_DEFAULT[3] * aggressiveness / 100)); - - $('#preview-pitch-p').html(Math.floor(axisRatio * EZ_TUNE_PID_RP_DEFAULT[0] * response / 100)); - $('#preview-pitch-i').html(Math.floor(axisRatio * EZ_TUNE_PID_RP_DEFAULT[1] * stability / 100)); - $('#preview-pitch-d').html(Math.floor(axisRatio * EZ_TUNE_PID_RP_DEFAULT[2] * damping / 100)); - $('#preview-pitch-ff').html(Math.floor(axisRatio * EZ_TUNE_PID_RP_DEFAULT[3] * aggressiveness / 100)); - - $('#preview-yaw-p').html(Math.floor(EZ_TUNE_PID_YAW_DEFAULT[0] * getYawPidScale(response))); - $('#preview-yaw-i').html(Math.floor(EZ_TUNE_PID_YAW_DEFAULT[1] * getYawPidScale(stability))); - $('#preview-yaw-d').html(Math.floor(EZ_TUNE_PID_YAW_DEFAULT[2] * getYawPidScale(damping))); - $('#preview-yaw-ff').html(Math.floor(EZ_TUNE_PID_YAW_DEFAULT[3] * getYawPidScale(aggressiveness))); - - $('#preview-roll-rate').html(Math.floor(scaleRange(rate, 0, 200, 30, 90)) * 10 + " dps"); - $('#preview-pitch-rate').html(Math.floor(scaleRange(rate, 0, 200, 30, 90)) * 10 + " dps"); - $('#preview-yaw-rate').html((Math.floor(scaleRange(rate, 0, 200, 30, 90)) - 10) * 10 + " dps"); - - $('#preview-roll-expo').html(Math.floor(scaleRange(expo, 0, 200, 40, 100)) + "%"); - $('#preview-pitch-expo').html(Math.floor(scaleRange(expo, 0, 200, 40, 100)) + "%"); - $('#preview-yaw-expo').html(Math.floor(scaleRange(expo, 0, 200, 40, 100)) + "%"); - - } - - function process_html() { - localize(); - - helper.tabs.init($('.tab-ez_tune')); - helper.features.updateUI($('.tab-ez_tune'), FEATURES); - - $("#ez_tune_enabled").prop('checked', EZ_TUNE.enabled); - - GUI.sliderize($('#ez_tune_filter_hz'), EZ_TUNE.filterHz, 10, 300); - GUI.sliderize($('#ez_tune_axis_ratio'), EZ_TUNE.axisRatio, 25, 175); - GUI.sliderize($('#ez_tune_response'), EZ_TUNE.response, 0, 200); - GUI.sliderize($('#ez_tune_damping'), EZ_TUNE.damping, 0, 200); - GUI.sliderize($('#ez_tune_stability'), EZ_TUNE.stability, 0, 200); - GUI.sliderize($('#ez_tune_aggressiveness'), EZ_TUNE.aggressiveness, 0, 200); - - GUI.sliderize($('#ez_tune_rate'), EZ_TUNE.rate, 0, 200); - GUI.sliderize($('#ez_tune_expo'), EZ_TUNE.expo, 0, 200); - - - $('.ez-element').on('updated', function () { - updatePreview(); - }); - - updatePreview(); - - GUI.simpleBind(); - - GUI.content_ready(callback); - - $('a.update').on('click', function () { - - if ($("#ez_tune_enabled").is(":checked")) { - EZ_TUNE.enabled = 1; - } else { - EZ_TUNE.enabled = 0; - } - - EZ_TUNE.filterHz = $('#ez_tune_filter_hz').val(); - EZ_TUNE.axisRatio = $('#ez_tune_axis_ratio').val(); - EZ_TUNE.response = $('#ez_tune_response').val(); - EZ_TUNE.damping = $('#ez_tune_damping').val(); - EZ_TUNE.stability = $('#ez_tune_stability').val(); - EZ_TUNE.aggressiveness = $('#ez_tune_aggressiveness').val(); - EZ_TUNE.rate = $('#ez_tune_rate').val(); - EZ_TUNE.expo = $('#ez_tune_expo').val(); - - saveChainer.execute(); - }); - - } - -}; - -TABS.ez_tune.cleanup = function (callback) { - if (callback) { - callback(); - } -}; \ No newline at end of file diff --git a/tabs/failsafe.js b/tabs/failsafe.js index 8afd2d214..05c02b0f9 100644 --- a/tabs/failsafe.js +++ b/tabs/failsafe.js @@ -1,12 +1,21 @@ 'use strict'; +const path = require('path'); + +const mspHelper = require('./../js/msp/MSPHelper'); +const MSPCodes = require('./../js/msp/MSPCodes'); +const MSP = require('./../js/msp'); +const { GUI, TABS } = require('./../js/gui'); +const FC = require('./../js/fc'); +const Settings = require('./../js/settings'); +const i18n = require('./../js/localization'); + TABS.failsafe = {}; TABS.failsafe.initialize = function (callback, scrollPosition) { if (GUI.active_tab != 'failsafe') { GUI.active_tab = 'failsafe'; - googleAnalytics.sendAppView('Failsafe'); } // Can get rid of this when MSPHelper supports strings (fixed in #7734, awaiting merge) @@ -15,11 +24,11 @@ TABS.failsafe.initialize = function (callback, scrollPosition) { } function load_html() { - GUI.load("./tabs/failsafe.html", Settings.processHtml(function() { + GUI.load(path.join(__dirname, "failsafe.html"), Settings.processHtml(function() { GUI.simpleBind(); // translate to user-selected language - localize(); + i18n.localize();; // for some odd reason chrome 38+ changes scroll according to the touched select element // i am guessing this is a bug, since this wasn't happening on 37 @@ -27,7 +36,7 @@ TABS.failsafe.initialize = function (callback, scrollPosition) { $('#content').scrollTop((scrollPosition) ? scrollPosition : 0); // set stage 2 failsafe procedure - $('input[type="radio"].procedure').change(function () { + $('input[type="radio"].procedure').on('change', function () { var element = $(this), checked = element.is(':checked'), id = element.attr('id'); @@ -49,32 +58,32 @@ TABS.failsafe.initialize = function (callback, scrollPosition) { }); // switch (MSPHelper.getSetting('failsafe_procedure')) { // Use once #7734 is merged - switch (FAILSAFE_CONFIG.failsafe_procedure) { + switch (FC.FAILSAFE_CONFIG.failsafe_procedure) { default: case 0: - element = $('input[id="land"]'); + var element = $('input[id="land"]'); element.prop('checked', true); - element.change(); + element.trigger('change'); break; case 1: - element = $('input[id="drop"]'); + var element = $('input[id="drop"]'); element.prop('checked', true); - element.change(); + element.trigger('change'); break; case 2: - element = $('input[id="rth"]'); + var element = $('input[id="rth"]'); element.prop('checked', true); - element.change(); + element.trigger('change'); break; case 3: - element = $('input[id="nothing"]'); + var element = $('input[id="nothing"]'); element.prop('checked', true); - element.change(); + element.trigger('change'); break; } // Adjust Minimum Distance values when checkbox is checked/unchecked - $('#failsafe_use_minimum_distance').change(function() { + $('#failsafe_use_minimum_distance').on('change', function () { if ($(this).is(':checked')) { // No default distance added due to conversions $('#failsafe_min_distance_elements').show(); @@ -98,15 +107,15 @@ TABS.failsafe.initialize = function (callback, scrollPosition) { $('#failsafe_min_distance_procedure_elements').hide(); } - $('a.save').click(function () { + $('a.save').on('click', function () { if ($('input[id="land"]').is(':checked')) { - FAILSAFE_CONFIG.failsafe_procedure = 0; + FC.FAILSAFE_CONFIG.failsafe_procedure = 0; } else if ($('input[id="drop"]').is(':checked')) { - FAILSAFE_CONFIG.failsafe_procedure = 1; + FC.FAILSAFE_CONFIG.failsafe_procedure = 1; } else if ($('input[id="rth"]').is(':checked')) { - FAILSAFE_CONFIG.failsafe_procedure = 2; + FC.FAILSAFE_CONFIG.failsafe_procedure = 2; } else if ($('input[id="nothing"]').is(':checked')) { - FAILSAFE_CONFIG.failsafe_procedure = 3; + FC.FAILSAFE_CONFIG.failsafe_procedure = 3; } MSP.send_message(MSPCodes.MSP_SET_FAILSAFE_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FAILSAFE_CONFIG), false, savePhaseTwo); @@ -118,29 +127,22 @@ TABS.failsafe.initialize = function (callback, scrollPosition) { load_failssafe_config(); - function savePhaseTwo() { - Settings.saveInputs().then(function () { - var self = this; - MSP.promise(MSPCodes.MSP_EEPROM_WRITE); - setTimeout(function () { - $(self).html(oldText); - }, 2000); - reboot(); - }); - } + function save_to_eeprom() { + console.log('save_to_eeprom'); + MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function () { + GUI.log(i18n.getMessage('eepromSaved')); - function reboot() { - //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); - GUI.tab_switch_cleanup(function () { - MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); + GUI.tab_switch_cleanup(function () { + MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, function () { + GUI.log(i18n.getMessage('deviceRebooting')); + GUI.handleReconnect($('.tab_failsafe a')); + }); + }); }); } - function reinitialize() { - //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('deviceRebooting')); - GUI.handleReconnect($('.tab_failsafe a')); + function savePhaseTwo() { + Settings.saveInputs(save_to_eeprom); } }; diff --git a/tabs/firmware_flasher.js b/tabs/firmware_flasher.js index f5b2cc5df..c95746d5f 100755 --- a/tabs/firmware_flasher.js +++ b/tabs/firmware_flasher.js @@ -1,29 +1,50 @@ -/*global $,nwdialog*/ 'use strict'; +const { marked } = require('marked'); +const fs = require('fs'); +const path = require('path'); +const semver = require('semver'); +const { dialog } = require('@electron/remote'); +const Store = require('electron-store'); +const store = new Store(); + +const i18n = require('./../js/localization'); +const { GUI, TABS } = require('./../js/gui'); +const MSP = require('./../js/msp'); +const MSPCodes = require('./../js/msp/MSPCodes') +const FC = require('./../js/fc'); +const { usbDevices, PortHandler } = require('./../js/port_handler'); +const CONFIGURATOR = require('./../js/data_storage'); +const SerialBackend = require('./../js/serial_backend'); +const timeout = require('./../js/timeouts'); +const interval = require('./../js/intervals'); +const mspQueue = require('./../js/serial_queue'); +const mspHelper = require('./../js/msp/MSPHelper'); +const STM32 = require('./../js/protocols/stm32'); +const STM32DFU = require('./../js/protocols/stm32usbdfu'); +const mspDeduplicationQueue = require('./../js/msp/mspDeduplicationQueue'); + TABS.firmware_flasher = {}; TABS.firmware_flasher.initialize = function (callback) { if (GUI.active_tab != 'firmware_flasher') { GUI.active_tab = 'firmware_flasher'; - googleAnalytics.sendAppView('Firmware Flasher'); } - var intel_hex = false, // standard intel hex in string format parsed_hex = false; // parsed raw hex in array format - GUI.load("./tabs/firmware_flasher.html", function () { + GUI.load(path.join(__dirname, "firmware_flasher.html"), function () { // translate to user-selected language - localize(); + i18n.localize(); function enable_load_online_button() { - $(".load_remote_file").text(chrome.i18n.getMessage('firmwareFlasherButtonLoadOnline')).removeClass('disabled'); + $(".load_remote_file").text(i18n.getMessage('firmwareFlasherButtonLoadOnline')).removeClass('disabled'); } function parse_hex(str, callback) { // parsing hex in different thread - var worker = new Worker('./build/hex_parser.js'); + var worker = new Worker('./js/workers/hex_parser.js'); // "callback" worker.onmessage = function (event) { @@ -34,6 +55,23 @@ TABS.firmware_flasher.initialize = function (callback) { worker.postMessage(str); } + function parseDevFilename(filename) { + //var targetFromFilenameExpression = /inav_([\d.]+)?_?([^.]+)\.(.*)/; + // inav_8.0.0_TUNERCF405_dev-20240617-88fb1d0.hex + var targetFromFilenameExpression = /^inav_([\d.]+)_([A-Z0-9_]+)_dev-(\d{4})(\d{2})(\d{2})-(\w+)\.(hex)$/; + var match = targetFromFilenameExpression.exec(filename); + + if (!match) { + return null; + } + + return { + raw_target: match[2], + target: match[2].replace("_", " "), + format: match[7], + }; + } + function parseFilename(filename) { //var targetFromFilenameExpression = /inav_([\d.]+)?_?([^.]+)\.(.*)/; var targetFromFilenameExpression = /inav_([\d.]+(?:-rc\d+)?)?_?([^.]+)\.(.*)/; @@ -43,6 +81,8 @@ TABS.firmware_flasher.initialize = function (callback) { return null; } + //GUI.log("non dev: match[2]: " + match[2] + " match[3]: " + match[3]); + return { raw_target: match[2], target: match[2].replace("_", " "), @@ -50,16 +90,16 @@ TABS.firmware_flasher.initialize = function (callback) { }; } - $('input.show_development_releases').click(function() { + $('input.show_development_releases').on('click', function () { let selectedTarget = String($('select[name="board"]').val()); - GUI.log(chrome.i18n.getMessage('selectedTarget') + selectedTarget); + GUI.log(i18n.getMessage('selectedTarget') + selectedTarget); buildBoardOptions(); - GUI.log(chrome.i18n.getMessage('toggledRCs')); + GUI.log(i18n.getMessage('toggledRCs')); if (selectedTarget === "0") { TABS.firmware_flasher.getTarget(); } else { $('select[name="board"] option[value=' + selectedTarget + ']').attr("selected", "selected"); - $('select[name="board"]').change(); + $('select[name="board"]').trigger('change'); } }); @@ -81,17 +121,19 @@ TABS.firmware_flasher.initialize = function (callback) { }); }); - var buildBoardOptions = function(){ + var buildBoardOptions = function(releasesData) { + const start = performance.now(); var boards_e = $('select[name="board"]').empty(); var versions_e = $('select[name="firmware_version"]').empty(); var showDevReleases = ($('input.show_development_releases').is(':checked')); - boards_e.append($("".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectBoard')))); - versions_e.append($("".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersion')))); + boards_e.append($("".format(i18n.getMessage('firmwareFlasherOptionLabelSelectBoard')))); + versions_e.append($("".format(i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersion')))); var releases = {}; var sortedTargets = []; var unsortedTargets = []; + TABS.firmware_flasher.releasesData.forEach(function(release){ release.assets.forEach(function(asset){ var result = parseFilename(asset.name); @@ -103,8 +145,25 @@ TABS.firmware_flasher.initialize = function (callback) { unsortedTargets.push(result.target); } }); - sortedTargets = unsortedTargets.sort(); }); + + if (showDevReleases) { + TABS.firmware_flasher.devReleasesData.forEach(function (release) { + release.assets.forEach(function (asset) { + var result = parseDevFilename(asset.name); + + if ((!showDevReleases && release.prerelease) || !result) { + return; + } + if ($.inArray(result.target, unsortedTargets) == -1) { + unsortedTargets.push(result.target); + } + }); + }); + } + + sortedTargets = unsortedTargets.sort(); + sortedTargets.forEach(function(release) { releases[release] = []; }); @@ -133,7 +192,7 @@ TABS.firmware_flasher.initialize = function (callback) { date.getUTCHours(), date.getMinutes() ); - + var descriptor = { "releaseUrl": release.html_url, "name" : semver.clean(release.name), @@ -149,6 +208,49 @@ TABS.firmware_flasher.initialize = function (callback) { releases[result.target].push(descriptor); }); }); + + if(showDevReleases) { + TABS.firmware_flasher.devReleasesData.forEach(function(release){ + + var versionFromTagExpression = /v?(.*)/; + var matchVersionFromTag = versionFromTagExpression.exec(release.tag_name); + var version = matchVersionFromTag[1]; + + release.assets.forEach(function(asset){ + var result = parseDevFilename(asset.name); + if ((!showDevReleases && release.prerelease) || !result) { + return; + } + + if (result.format != 'hex') { + return; + } + + var date = new Date(release.published_at); + var formattedDate = "{0}-{1}-{2} {3}:{4}".format( + date.getFullYear(), + date.getMonth() + 1, + date.getDate(), + date.getUTCHours(), + date.getMinutes() + ); + + var descriptor = { + "releaseUrl": release.html_url, + "name" : semver.clean(release.name), + "version" : release.tag_name, + "url" : asset.browser_download_url, + "file" : asset.name, + "raw_target": result.raw_target, + "target" : result.target, + "date" : formattedDate, + "notes" : release.body, + "status" : release.prerelease ? "nightly" : "stable" + }; + releases[result.target].push(descriptor); + }); + }); + } var selectTargets = []; Object.keys(releases) @@ -168,31 +270,46 @@ TABS.firmware_flasher.initialize = function (callback) { }); }); TABS.firmware_flasher.releases = releases; + const end = performance.now(); + console.log(`buildBoardOptions: ${end - start} ms`) return; }; + $.get('https://api.github.com/repos/iNavFlight/inav-nightly/releases?per_page=10', function(releasesData) { + TABS.firmware_flasher.devReleasesData = releasesData; + }).fail(function (data){ + TABS.firmware_flasher.devReleasesData = {}; + if (data["responseJSON"]){ + GUI.log("GITHUB Query Failed: {0}".format(data["responseJSON"].message)); + } + $('select[name="board"]').empty().append(''); + $('select[name="firmware_version"]').empty().append(''); + $('a.auto_select_target').addClass('disabled'); + }); + + $.get('https://api.github.com/repos/iNavFlight/inav/releases?per_page=10', function (releasesData){ TABS.firmware_flasher.releasesData = releasesData; - buildBoardOptions(); + buildBoardOptions(releasesData); // bind events - $('select[name="board"]').change(function() { + $('select[name="board"]').on('change', function () { $("a.load_remote_file").addClass('disabled'); var target = $(this).children("option:selected").text(); if (!GUI.connect_lock) { $('.progress').val(0).removeClass('valid invalid'); - $('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherLoadFirmwareFile')); + $('span.progressLabel').text(i18n.getMessage('firmwareFlasherLoadFirmwareFile')); $('div.git_info').slideUp(); $('div.release_info').slideUp(); $('a.flash_firmware').addClass('disabled'); var versions_e = $('select[name="firmware_version"]').empty(); if(target == 0) { - versions_e.append($("".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersion')))); + versions_e.append($("".format(i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersion')))); } else { - versions_e.append($("".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersionFor'), target))); + versions_e.append($("".format(i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersionFor'), target))); } TABS.firmware_flasher.releases[target].forEach(function(descriptor) { @@ -222,9 +339,18 @@ TABS.firmware_flasher.initialize = function (callback) { $('a.load_file').on('click', function () { - nwdialog.setContext(document); - nwdialog.openFileDialog('.hex', function(filename) { - const fs = require('fs'); + var options = { + filters: [ { name: "HEX file", extensions: ['hex'] } ] + }; + dialog.showOpenDialog(options).then(result => { + if (result.canceled) { + return; + } + + let filename; + if (result.filePaths.length == 1) { + filename = result.filePaths[0]; + } $('div.git_info').slideUp(); @@ -243,12 +369,11 @@ TABS.firmware_flasher.initialize = function (callback) { parsed_hex = data; if (parsed_hex) { - googleAnalytics.sendEvent('Flashing', 'Firmware', 'local'); $('a.flash_firmware').removeClass('disabled'); $('span.progressLabel').text('Loaded Local Firmware: (' + parsed_hex.bytes_total + ' bytes)'); } else { - $('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherHexCorrupted')); + $('span.progressLabel').text(i18n.getMessage('firmwareFlasherHexCorrupted')); } }); }); @@ -260,7 +385,7 @@ TABS.firmware_flasher.initialize = function (callback) { /** * Lock / Unlock the firmware download button according to the firmware selection dropdown. */ - $('select[name="firmware_version"]').change(function(evt){ + $('select[name="firmware_version"]').on('change', function(evt){ $('div.release_info').slideUp(); $('a.flash_firmware').addClass('disabled'); if (evt.target.value=="0") { @@ -271,10 +396,10 @@ TABS.firmware_flasher.initialize = function (callback) { } }); - $('a.load_remote_file').click(function (evt) { + $('a.load_remote_file').on('click', function () { if ($('select[name="firmware_version"]').val() == "0") { - GUI.log(chrome.i18n.getMessage('noFirmwareSelectedToLoad')); + GUI.log(i18n.getMessage('noFirmwareSelectedToLoad')); return; } @@ -287,7 +412,6 @@ TABS.firmware_flasher.initialize = function (callback) { if (parsed_hex) { var url; - googleAnalytics.sendEvent('Flashing', 'Firmware', 'online'); $('span.progressLabel').html('Loaded Online Firmware: (' + parsed_hex.bytes_total + ' bytes)'); $('a.flash_firmware').removeClass('disabled'); @@ -317,7 +441,7 @@ TABS.firmware_flasher.initialize = function (callback) { var status_e = $('div.release_info .status'); if (summary.status == 'release-candidate') { - $('div.release_info .status').html(chrome.i18n.getMessage('firmwareFlasherReleaseStatusReleaseCandidate')).show(); + $('div.release_info .status').html(i18n.getMessage('firmwareFlasherReleaseStatusReleaseCandidate')).show(); } else { status_e.hide(); } @@ -327,7 +451,7 @@ TABS.firmware_flasher.initialize = function (callback) { $('div.release_info .status').text(summary.status); $('div.release_info .file').text(summary.file).prop('href', summary.url); - var formattedNotes = marked(summary.notes); + var formattedNotes = marked.parse(summary.notes); $('div.release_info .notes').html(formattedNotes); // Make links in the release notes open in a new window $('div.release_info .notes a').each(function () { @@ -337,30 +461,30 @@ TABS.firmware_flasher.initialize = function (callback) { $('div.release_info').slideDown(); } else { - $('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherHexCorrupted')); + $('span.progressLabel').text(i18n.getMessage('firmwareFlasherHexCorrupted')); } }); } function failed_to_load() { - $('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherFailedToLoadOnlineFirmware')); + $('span.progressLabel').text(i18n.getMessage('firmwareFlasherFailedToLoadOnlineFirmware')); $('a.flash_firmware').addClass('disabled'); enable_load_online_button(); } var summary = $('select[name="firmware_version"] option:selected').data('summary'); if (summary) { // undefined while list is loading or while running offline - $(".load_remote_file").text(chrome.i18n.getMessage('firmwareFlasherButtonLoading')).addClass('disabled'); + $(".load_remote_file").text(i18n.getMessage('firmwareFlasherButtonLoading')).addClass('disabled'); $.get(summary.url, function (data) { enable_load_online_button(); process_hex(data, summary); }).fail(failed_to_load); } else { - $('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherFailedToLoadOnlineFirmware')); + $('span.progressLabel').text(i18n.getMessage('firmwareFlasherFailedToLoadOnlineFirmware')); } }); - $('a.flash_firmware').click(function () { + $('a.flash_firmware').on('click', function () { if (!$(this).hasClass('disabled')) { if (!GUI.connect_lock) { // button disabled while flashing is in progress if (parsed_hex != false) { @@ -402,13 +526,13 @@ TABS.firmware_flasher.initialize = function (callback) { STM32.connect(port, baud, parsed_hex, options); } else { console.log('Please select valid serial port'); - GUI.log(chrome.i18n.getMessage('selectValidSerialPort')); + GUI.log(i18n.getMessage('selectValidSerialPort')); } } else { STM32DFU.connect(usbDevices, parsed_hex, options); } } else { - $('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherFirmwareNotLoaded')); + $('span.progressLabel').text(i18n.getMessage('firmwareFlasherFirmwareNotLoaded')); } } } @@ -452,39 +576,39 @@ TABS.firmware_flasher.initialize = function (callback) { }); } else { console.log('You don\'t have write permissions for this file, sorry.'); - GUI.log(chrome.i18n.getMessage('writePermissionsForFile')); + GUI.log(i18n.getMessage('writePermissionsForFile')); } }); }); }); }); - chrome.storage.local.get('no_reboot_sequence', function (result) { - if (result.no_reboot_sequence) { - $('input.updating').prop('checked', true); + + if (store.get('no_reboot_sequence', false)) { + $('input.updating').prop('checked', true); + $('.flash_on_connect_wrapper').show(); + } else { + $('input.updating').prop('checked', false); + } + + // bind UI hook so the status is saved on change + $('input.updating').on('change', function () { + var status = $(this).is(':checked'); + + if (status) { $('.flash_on_connect_wrapper').show(); } else { - $('input.updating').prop('checked', false); + $('input.flash_on_connect').prop('checked', false).trigger('change'); + $('.flash_on_connect_wrapper').hide(); } - // bind UI hook so the status is saved on change - $('input.updating').change(function() { - var status = $(this).is(':checked'); - - if (status) { - $('.flash_on_connect_wrapper').show(); - } else { - $('input.flash_on_connect').prop('checked', false).change(); - $('.flash_on_connect_wrapper').hide(); - } - - chrome.storage.local.set({'no_reboot_sequence': status}); - }); - - $('input.updating').change(); + store.set('no_reboot_sequence', status); }); - chrome.storage.local.get('flash_manual_baud', function (result) { + $('input.updating').trigger('change'); + + + store.get('flash_manual_baud', function (result) { if (result.flash_manual_baud) { $('input.flash_manual_baud').prop('checked', true); } else { @@ -492,91 +616,90 @@ TABS.firmware_flasher.initialize = function (callback) { } // bind UI hook so the status is saved on change - $('input.flash_manual_baud').change(function() { + $('input.flash_manual_baud').on('change', function () { var status = $(this).is(':checked'); - chrome.storage.local.set({'flash_manual_baud': status}); + store.set('flash_manual_baud', status); }); - $('input.flash_manual_baud').change(); + $('input.flash_manual_baud').trigger('change'); }); - chrome.storage.local.get('flash_manual_baud_rate', function (result) { - $('#flash_manual_baud_rate').val(result.flash_manual_baud_rate); - - // bind UI hook so the status is saved on change - $('#flash_manual_baud_rate').change(function() { - var baud = parseInt($('#flash_manual_baud_rate').val()); - chrome.storage.local.set({'flash_manual_baud_rate': baud}); - }); + var flash_manual_baud_rate = store.get('flash_manual_baud_rate', ''); + $('#flash_manual_baud_rate').val(flash_manual_baud_rate); - $('input.flash_manual_baud_rate').change(); + // bind UI hook so the status is saved on change + $('#flash_manual_baud_rate').on('change', function () { + var baud = parseInt($('#flash_manual_baud_rate').val()); + store.set('flash_manual_baud_rate', baud); }); - chrome.storage.local.get('flash_on_connect', function (result) { - if (result.flash_on_connect) { - $('input.flash_on_connect').prop('checked', true); - } else { - $('input.flash_on_connect').prop('checked', false); - } + $('input.flash_manual_baud_rate').trigger('change'); - $('input.flash_on_connect').change(function () { - var status = $(this).is(':checked'); - - if (status) { - var catch_new_port = function () { - PortHandler.port_detected('flash_detected_device', function (result) { - var port = result[0]; + + if (store.get('flash_on_connect', false)) { + $('input.flash_on_connect').prop('checked', true); + } else { + $('input.flash_on_connect').prop('checked', false); + } - if (!GUI.connect_lock) { - GUI.log('Detected: ' + port + ' - triggering flash on connect'); - console.log('Detected: ' + port + ' - triggering flash on connect'); + $('input.flash_on_connect').on('change', function () { + var status = $(this).is(':checked'); - // Trigger regular Flashing sequence - helper.timeout.add('initialization_timeout', function () { - $('a.flash_firmware').click(); - }, 100); // timeout so bus have time to initialize after being detected by the system - } else { - GUI.log('Detected ' + port + ' - previous device still flashing, please replug to try again'); - } + if (status) { + var catch_new_port = function () { + PortHandler.port_detected('flash_detected_device', function (result) { + var port = result[0]; - // Since current port_detected request was consumed, create new one - catch_new_port(); - }, false, true); - }; + if (!GUI.connect_lock) { + GUI.log('Detected: ' + port + ' - triggering flash on connect'); + console.log('Detected: ' + port + ' - triggering flash on connect'); - catch_new_port(); - } else { - PortHandler.flush_callbacks(); - } + // Trigger regular Flashing sequence + timeout.add('initialization_timeout', function () { + $('a.flash_firmware').trigger( "click" ); + }, 100); // timeout so bus have time to initialize after being detected by the system + } else { + GUI.log('Detected ' + port + ' - previous device still flashing, please replug to try again'); + } - chrome.storage.local.set({'flash_on_connect': status}); - }).change(); - }); + // Since current port_detected request was consumed, create new one + catch_new_port(); + }, false, true); + }; - chrome.storage.local.get('erase_chip', function (result) { - if (result.erase_chip) { - $('input.erase_chip').prop('checked', true); + catch_new_port(); } else { - $('input.erase_chip').prop('checked', false); + PortHandler.flush_callbacks(); } - // bind UI hook so the status is saved on change - $('input.erase_chip').change(function () { - chrome.storage.local.set({'erase_chip': $(this).is(':checked')}); - }); + store.set('flash_on_connect', status); + }).trigger('change'); + - $('input.erase_chip').change(); + + if (store.get('erase_chip', false)) { + $('input.erase_chip').prop('checked', true); + } else { + $('input.erase_chip').prop('checked', false); + } + // bind UI hook so the status is saved on change + $('input.erase_chip').on('change', function () { + store.set('erase_chip', $(this).is(':checked')); }); + $('input.erase_chip').trigger('change'); + + + $(document).keypress(function (e) { if (e.which == 13) { // enter // Trigger regular Flashing sequence - $('a.flash_firmware').click(); + $('a.flash_firmware').trigger( "click" ); } }); - $('a.auto_select_target').click(function () { + $('a.auto_select_target').on('click', function () { TABS.firmware_flasher.getTarget(); }); @@ -635,14 +758,14 @@ TABS.firmware_flasher.cleanup = function (callback) { }; TABS.firmware_flasher.getTarget = function() { - GUI.log(chrome.i18n.getMessage('automaticTargetSelect')); + GUI.log(i18n.getMessage('automaticTargetSelect')); var selected_baud = parseInt($('#baud').val()); var selected_port = $('#port').find('option:selected').data().isManual ? $('#port-override').val() : String($('#port').val()); if (selected_port !== 'DFU') { - if (selected_port == '0') { - GUI.log(chrome.i18n.getMessage('targetPrefetchFailNoPort')); + if (!selected_port || selected_port == '0') { + GUI.log(i18n.getMessage('targetPrefetchFailNoPort')); } else { console.log('Connecting to: ' + selected_port); GUI.connecting_to = selected_port; @@ -654,7 +777,7 @@ TABS.firmware_flasher.getTarget = function() { } } } else { - GUI.log(chrome.i18n.getMessage('targetPrefetchFailDFU')); + GUI.log(i18n.getMessage('targetPrefetchFailDFU')); } }; @@ -666,27 +789,27 @@ TABS.firmware_flasher.onOpen = function(openInfo) { GUI.connecting_to = false; // save selected port with chrome.storage if the port differs - chrome.storage.local.get('last_used_port', function (result) { - if (result.last_used_port) { - if (result.last_used_port != GUI.connected_to) { - // last used port doesn't match the one found in local db, we will store the new one - chrome.storage.local.set({'last_used_port': GUI.connected_to}); - } - } else { - // variable isn't stored yet, saving - chrome.storage.local.set({'last_used_port': GUI.connected_to}); + var last_used_port = store.get('last_used_port', ''); + if (last_used_port) { + if (last_used_port != GUI.connected_to) { + // last used port doesn't match the one found in local db, we will store the new one + store.set('last_used_port', GUI.connected_to); } - }); + } else { + // variable isn't stored yet, saving + store.set('last_used_port', GUI.connected_to); + } + - chrome.storage.local.set({last_used_bps: CONFIGURATOR.connection.bitrate}); - chrome.storage.local.set({wireless_mode_enabled: $('#wireless-mode').is(":checked")}); + store.set('last_used_bps', CONFIGURATOR.connection.bitrate); + store.set('wireless_mode_enabled', $('#wireless-mode').is(":checked")); - CONFIGURATOR.connection.addOnReceiveListener(read_serial); + CONFIGURATOR.connection.addOnReceiveListener(SerialBackend.read_serial); // disconnect after 10 seconds with error if we don't get IDENT data - helper.timeout.add('connecting', function () { + timeout.add('connecting', function () { if (!CONFIGURATOR.connectionValid) { - GUI.log(chrome.i18n.getMessage('targetPrefetchFail') + chrome.i18n.getMessage('noConfigurationReceived')); + GUI.log(i18n.getMessage('targetPrefetchFail') + i18n.getMessage('noConfigurationReceived')); TABS.firmware_flasher.closeTempConnection(); } @@ -699,35 +822,35 @@ TABS.firmware_flasher.onOpen = function(openInfo) { MSP.protocolVersion = MSP.constants.PROTOCOL_V2; MSP.send_message(MSPCodes.MSP_API_VERSION, false, false, function () { - if (CONFIG.apiVersion === "0.0.0") { - GUI_control.prototype.log("Cannot prefetch target: " + chrome.i18n.getMessage("illegalStateRestartRequired") + ""); + if (FC.CONFIG.apiVersion === "0.0.0") { + GUI_control.prototype.log("Cannot prefetch target: " + i18n.getMessage("illegalStateRestartRequired") + ""); FC.restartRequired = true; return; } MSP.send_message(MSPCodes.MSP_FC_VARIANT, false, false, function () { - if (CONFIG.flightControllerIdentifier == 'INAV') { + if (FC.CONFIG.flightControllerIdentifier == 'INAV') { MSP.send_message(MSPCodes.MSP_FC_VERSION, false, false, function () { - if (semver.lt(CONFIG.flightControllerVersion, "5.0.0")) { - GUI.log(chrome.i18n.getMessage('targetPrefetchFailOld')); + if (semver.lt(FC.CONFIG.flightControllerVersion, "5.0.0")) { + GUI.log(i18n.getMessage('targetPrefetchFailOld')); TABS.firmware_flasher.closeTempConnection(); } else { mspHelper.getCraftName(function(name) { if (name) { - CONFIG.name = name; + FC.CONFIG.name = name; } TABS.firmware_flasher.onValidFirmware(); }); } }); } else { - GUI.log(chrome.i18n.getMessage('targetPrefetchFailNonINAV')); + GUI.log(i18n.getMessage('targetPrefetchFailNonINAV')); TABS.firmware_flasher.closeTempConnection(); } }); }); } else { - GUI.log(chrome.i18n.getMessage('targetPrefetchFail') + chrome.i18n.getMessage('serialPortOpenFail')); + GUI.log(i18n.getMessage('targetPrefetchFail') + i18n.getMessage('serialPortOpenFail')); return; } }; @@ -735,28 +858,28 @@ TABS.firmware_flasher.onOpen = function(openInfo) { TABS.firmware_flasher.onValidFirmware = function() { MSP.send_message(MSPCodes.MSP_BUILD_INFO, false, false, function () { MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, function () { - $('select[name="board"] option[value=' + CONFIG.target + ']').attr("selected", "selected"); - GUI.log(chrome.i18n.getMessage('targetPrefetchsuccessful') + CONFIG.target); + $('select[name="board"] option[value=' + FC.CONFIG.target + ']').attr("selected", "selected"); + GUI.log(i18n.getMessage('targetPrefetchsuccessful') + FC.CONFIG.target); TABS.firmware_flasher.closeTempConnection(); - $('select[name="board"]').change(); + $('select[name="board"]').trigger('change'); }); }); }; TABS.firmware_flasher.closeTempConnection = function() { - helper.timeout.killAll(); - helper.interval.killAll(['global_data_refresh', 'msp-load-update']); - helper.mspBalancedInterval.flush(); + timeout.killAll(); + interval.killAll(['global_data_refresh', 'msp-load-update', 'ltm-connection-check']); - helper.mspQueue.flush(); - helper.mspQueue.freeHardLock(); - helper.mspQueue.freeSoftLock(); + mspQueue.flush(); + mspQueue.freeHardLock(); + mspQueue.freeSoftLock(); + mspDeduplicationQueue.flush(); CONFIGURATOR.connection.emptyOutputBuffer(); CONFIGURATOR.connectionValid = false; GUI.connected_to = false; - CONFIGURATOR.connection.disconnect(onClosed); + CONFIGURATOR.connection.disconnect(); MSP.disconnect_cleanup(); }; \ No newline at end of file diff --git a/tabs/gps.html b/tabs/gps.html index d9c5c27e4..c217b7b9c 100644 --- a/tabs/gps.html +++ b/tabs/gps.html @@ -9,17 +9,19 @@
-
-
-

-
-
-
+
+ + +
+
+ + +
+
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -125,7 +156,7 @@

- + align_mag_roll, align_mag_pitch, align_mag_yaw

@@ -172,6 +203,13 @@ +
+ CLI:  +
+
+ + +
diff --git a/tabs/magnetometer.js b/tabs/magnetometer.js index 77e3391cf..af8e6a8ba 100644 --- a/tabs/magnetometer.js +++ b/tabs/magnetometer.js @@ -1,5 +1,16 @@ 'use strict'; -/*global chrome,GUI,BOARD_ALIGNMENT,TABS,nwdialog,helper,$*/ + +const path = require('path'); + +const MSPChainerClass = require('./../js/msp/MSPchainer'); +const MSP = require('./../js/msp'); +const MSPCodes = require('./../js/msp/MSPCodes'); +const mspHelper = require('./../js/msp/MSPHelper'); +const FC = require('./../js/fc'); +const { GUI, TABS } = require('./../js/gui'); +const i18n = require('./../js/localization'); +const { mixer } = require('./../js/model'); +const interval = require('./../js/intervals'); TABS.magnetometer = {}; @@ -9,7 +20,6 @@ TABS.magnetometer.initialize = function (callback) { if (GUI.active_tab != 'magnetometer') { GUI.active_tab = 'magnetometer'; - googleAnalytics.sendAppView('MAGNETOMETER'); } self.alignmentConfig = { @@ -26,7 +36,7 @@ TABS.magnetometer.initialize = function (callback) { self.pageElements = {}; self.isSavePreset = true; - self.showMagnetometer = true; + self.elementToShow = 0; //======================== // Load chain // ======================= @@ -36,9 +46,9 @@ TABS.magnetometer.initialize = function (callback) { mspHelper.loadMixerConfig, mspHelper.loadBoardAlignment, function (callback) { - self.boardAlignmentConfig.pitch = Math.round(BOARD_ALIGNMENT.pitch / 10); - self.boardAlignmentConfig.roll = Math.round(BOARD_ALIGNMENT.roll / 10); - self.boardAlignmentConfig.yaw = Math.round(BOARD_ALIGNMENT.yaw / 10); + self.boardAlignmentConfig.pitch = Math.round(FC.BOARD_ALIGNMENT.pitch / 10); + self.boardAlignmentConfig.roll = Math.round(FC.BOARD_ALIGNMENT.roll / 10); + self.boardAlignmentConfig.yaw = Math.round(FC.BOARD_ALIGNMENT.yaw / 10); callback(); }, mspHelper.loadSensorAlignment, @@ -79,16 +89,16 @@ TABS.magnetometer.initialize = function (callback) { var saveChain = [ function (callback) { - BOARD_ALIGNMENT.pitch = self.boardAlignmentConfig.pitch * 10; - BOARD_ALIGNMENT.roll = self.boardAlignmentConfig.roll * 10; - BOARD_ALIGNMENT.yaw = self.boardAlignmentConfig.yaw * 10; + FC.BOARD_ALIGNMENT.pitch = self.boardAlignmentConfig.pitch * 10; + FC.BOARD_ALIGNMENT.roll = self.boardAlignmentConfig.roll * 10; + FC.BOARD_ALIGNMENT.yaw = self.boardAlignmentConfig.yaw * 10; callback(); }, mspHelper.saveBoardAlignment, // Magnetometer alignment function (callback) { let orientation_mag_e = $('select.magalign'); - SENSOR_ALIGNMENT.align_mag = parseInt(orientation_mag_e.val()); + FC.SENSOR_ALIGNMENT.align_mag = parseInt(orientation_mag_e.val()); callback(); }, mspHelper.saveSensorAlignment, @@ -126,7 +136,7 @@ TABS.magnetometer.initialize = function (callback) { function reboot() { //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); + GUI.log(i18n.getMessage('configurationEepromSaved')); GUI.tab_switch_cleanup(function () { MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); @@ -134,12 +144,12 @@ TABS.magnetometer.initialize = function (callback) { } function reinitialize() { - GUI.log(chrome.i18n.getMessage('deviceRebooting')); + GUI.log(i18n.getMessage('deviceRebooting')); GUI.handleReconnect($('.tab_magnetometer a')); } function load_html() { - GUI.load("./tabs/magnetometer.html", process_html); + GUI.load(path.join(__dirname, "magnetometer.html"), process_html); } function generateRange(min, max, step) { @@ -151,6 +161,7 @@ TABS.magnetometer.initialize = function (callback) { } function toUpperRange(input, max) { + if (!Number.isFinite(input)) return 0; while (input > max) input -= 360; while (input + 360 <= max) input += 360; return input; @@ -179,7 +190,7 @@ TABS.magnetometer.initialize = function (callback) { return [180, 0, 180]; case 0: //ALIGN_DEFAULT = 0 case 8: //CW270_DEG_FLIP = 5 - default://If not recognized, returns defualt + default://If not recognized, returns default return [180, 0, 270]; } } @@ -198,7 +209,7 @@ TABS.magnetometer.initialize = function (callback) { var magRotation = new THREE.Euler(-THREE.Math.degToRad(degree[0]-180), THREE.Math.degToRad(-180 - degree[2]), THREE.Math.degToRad(degree[1]), 'YXZ'); var matrix = (new THREE.Matrix4()).makeRotationFromEuler(magRotation); - var boardRotation = new THREE.Euler( THREE.Math.degToRad( -self.boardAlignmentConfig.pitch ), THREE.Math.degToRad( -self.boardAlignmentConfig.yaw ), THREE.Math.degToRad( -self.boardAlignmentConfig.roll ), 'YXZ'); + var boardRotation = new THREE.Euler( THREE.Math.degToRad( self.boardAlignmentConfig.pitch ), THREE.Math.degToRad( -self.boardAlignmentConfig.yaw ), THREE.Math.degToRad( self.boardAlignmentConfig.roll ), 'YXZ'); var matrix1 = (new THREE.Matrix4()).makeRotationFromEuler(boardRotation); matrix.premultiply(matrix1); @@ -215,16 +226,24 @@ TABS.magnetometer.initialize = function (callback) { function updateMagOrientationWithPreset() { if (self.isSavePreset) { - const degrees = getAxisDegreeWithPresetAndBoardOrientation(SENSOR_ALIGNMENT.align_mag); + const degrees = getAxisDegreeWithPresetAndBoardOrientation(FC.SENSOR_ALIGNMENT.align_mag); presetUpdated(degrees); } } + function updateFCCliString() { + var s = " align_board_roll=" + (self.boardAlignmentConfig.roll * 10) + + " align_board_pitch=" + (self.boardAlignmentConfig.pitch * 10) + + " align_board_yaw=" + (self.boardAlignmentConfig.yaw * 10); + self.pageElements.cli_settings_fc.text(s); + } + function updateBoardRollAxis(value) { self.boardAlignmentConfig.roll = Number(value); self.pageElements.board_roll_slider.val(self.boardAlignmentConfig.roll); self.pageElements.orientation_board_roll.val(self.boardAlignmentConfig.roll); updateMagOrientationWithPreset(); + updateFCCliString(); self.render3D(); } @@ -233,6 +252,7 @@ TABS.magnetometer.initialize = function (callback) { self.pageElements.board_pitch_slider.val(self.boardAlignmentConfig.pitch); self.pageElements.orientation_board_pitch.val(self.boardAlignmentConfig.pitch); updateMagOrientationWithPreset(); + updateFCCliString(); self.render3D(); } @@ -241,14 +261,31 @@ TABS.magnetometer.initialize = function (callback) { self.pageElements.board_yaw_slider.val(self.boardAlignmentConfig.yaw); self.pageElements.orientation_board_yaw.val(self.boardAlignmentConfig.yaw); updateMagOrientationWithPreset(); + updateFCCliString(); self.render3D(); } + + function updateMagCliString() { + var fix = 0; + if ( areAnglesZero() ) { + fix = 1; //if all angles are 0, then we have to save yaw = 1 (0.1 deg) to enforce usage of angles, not a usage of preset + } + var names = ['DEFAULT', 'CW0', 'CW90', 'CW180', 'CW270', 'CW0FLIP', 'CW90FLIP', 'CW180FLIP', 'CW270FLIP']; + var s = "align_mag=" + names[FC.SENSOR_ALIGNMENT.align_mag] + + " align_mag_roll=" + (self.isSavePreset ? 0 : self.alignmentConfig.roll * 10) + + " align_mag_pitch=" + (self.isSavePreset ? 0 : self.alignmentConfig.pitch * 10) + + " align_mag_yaw=" + (self.isSavePreset ? 0 : self.alignmentConfig.yaw * 10 + fix); + self.pageElements.cli_settings_mag.text(s); + self.pageElements.comment_sensor_mag_preset.css("display", !self.isSavePreset ? "none" : ""); + self.pageElements.comment_sensor_mag_angles.css("display", self.isSavePreset ? "none" : ""); + } //Called when roll values change function updateRollAxis(value) { self.alignmentConfig.roll = Number(value); self.pageElements.roll_slider.val(self.alignmentConfig.roll); self.pageElements.orientation_mag_roll.val(self.alignmentConfig.roll); + updateMagCliString(); self.render3D(); } @@ -257,6 +294,7 @@ TABS.magnetometer.initialize = function (callback) { self.alignmentConfig.pitch = Number(value); self.pageElements.pitch_slider.val(self.alignmentConfig.pitch); self.pageElements.orientation_mag_pitch.val(self.alignmentConfig.pitch); + updateMagCliString(); self.render3D(); } @@ -265,6 +303,7 @@ TABS.magnetometer.initialize = function (callback) { self.alignmentConfig.yaw = Number(value); self.pageElements.yaw_slider.val(self.alignmentConfig.yaw); self.pageElements.orientation_mag_yaw.val(self.alignmentConfig.yaw); + updateMagCliString(); self.render3D(); } @@ -272,12 +311,16 @@ TABS.magnetometer.initialize = function (callback) { self.isSavePreset = true; self.pageElements.orientation_mag_e.css("opacity", 1); self.pageElements.orientation_mag_e.css("text-decoration", ""); + self.pageElements.align_mag_xxx_e.css("opacity", "0.65"); + self.pageElements.align_mag_xxx_e.css("text-decoration", "line-through"); } function disableSavePreset() { self.isSavePreset = false; self.pageElements.orientation_mag_e.css("opacity", 0.5); self.pageElements.orientation_mag_e.css("text-decoration", "line-through"); + self.pageElements.align_mag_xxx_e.css("opacity", "1"); + self.pageElements.align_mag_xxx_e.css("text-decoration", ""); } @@ -287,12 +330,13 @@ TABS.magnetometer.initialize = function (callback) { updatePitchAxis(degrees[0]); updateRollAxis(degrees[1]); updateYawAxis(degrees[2]); + updateMagCliString(); } function process_html() { - localize(); + i18n.localize();; // initialize 3D self.initialize3D(); @@ -314,19 +358,27 @@ TABS.magnetometer.initialize = function (callback) { self.pageElements.pitch_slider = $('#pitch_slider'); self.pageElements.yaw_slider = $('#yaw_slider'); + self.pageElements.align_mag_xxx_e = $('#align_mag_xxx'); + + self.pageElements.cli_settings_fc = $('#cli_settings_fc'); + self.pageElements.cli_settings_mag = $('#cli_settings_mag'); + + self.pageElements.comment_sensor_mag_preset = $('#comment_sensor_mag_preset'); + self.pageElements.comment_sensor_mag_angles = $('#comment_sensor_mag_angles'); + self.roll_e = $('dd.roll'), self.pitch_e = $('dd.pitch'), self.heading_e = $('dd.heading'); - for (i = 0; i < alignments.length; i++) { + for (let i = 0; i < alignments.length; i++) { self.pageElements.orientation_mag_e.append(''); } - self.pageElements.orientation_mag_e.val(SENSOR_ALIGNMENT.align_mag); + self.pageElements.orientation_mag_e.val(FC.SENSOR_ALIGNMENT.align_mag); if (areAnglesZero()) { //If using a preset, checking if custom values are equal to 0 //Update the slider, but don't save the value until they will be not modified. - const degrees = getAxisDegreeWithPresetAndBoardOrientation(SENSOR_ALIGNMENT.align_mag); + const degrees = getAxisDegreeWithPresetAndBoardOrientation(FC.SENSOR_ALIGNMENT.align_mag); presetUpdated(degrees); } else { @@ -337,15 +389,15 @@ TABS.magnetometer.initialize = function (callback) { } - self.pageElements.orientation_board_roll.change(function () { + self.pageElements.orientation_board_roll.on('change', function () { updateBoardRollAxis(clamp(this, -180, 360)); }); - self.pageElements.orientation_board_pitch.change(function () { + self.pageElements.orientation_board_pitch.on('change', function () { updateBoardPitchAxis(clamp(this, -180, 360)); }); - self.pageElements.orientation_board_yaw.change(function () { + self.pageElements.orientation_board_yaw.on('change', function () { updateBoardYawAxis(clamp(this, -180, 360)); }); @@ -406,9 +458,9 @@ TABS.magnetometer.initialize = function (callback) { }); const elementToShow = $("#element_to_show"); - elementToShow.change(function () { + elementToShow.on('change', function () { const value = parseInt($(this).val()); - self.showMagnetometer = (value == 0); + self.elementToShow = value; self.render3D(); }); @@ -416,33 +468,33 @@ TABS.magnetometer.initialize = function (callback) { return Math.min(Math.max(parseInt($(input).val()), min), max); } - self.pageElements.orientation_mag_e.change(function () { - SENSOR_ALIGNMENT.align_mag = parseInt($(this).val()); - const degrees = getAxisDegreeWithPresetAndBoardOrientation(SENSOR_ALIGNMENT.align_mag); + self.pageElements.orientation_mag_e.on('change', function () { + FC.SENSOR_ALIGNMENT.align_mag = parseInt($(this).val()); + const degrees = getAxisDegreeWithPresetAndBoardOrientation(FC.SENSOR_ALIGNMENT.align_mag); presetUpdated(degrees); }); self.pageElements.orientation_mag_e.on('mousedown', function () { - const degrees = getAxisDegreeWithPresetAndBoardOrientation(SENSOR_ALIGNMENT.align_mag); + const degrees = getAxisDegreeWithPresetAndBoardOrientation(FC.SENSOR_ALIGNMENT.align_mag); presetUpdated(degrees); }); - self.pageElements.orientation_mag_roll.change(function () { + self.pageElements.orientation_mag_roll.on('change', function () { disableSavePreset(); updateRollAxis(clamp(this, -180, 360)); }); - self.pageElements.orientation_mag_pitch.change(function () { + self.pageElements.orientation_mag_pitch.on('change', function () { disableSavePreset(); updatePitchAxis(clamp(this, -180, 360)); }); - self.pageElements.orientation_mag_yaw.change(function () { + self.pageElements.orientation_mag_yaw.on('change', function () { disableSavePreset(); updateYawAxis(clamp(this, -180, 360)); }); - $('a.save').click(function () { + $('a.save').on('click', function () { saveChainer.execute() }); @@ -513,19 +565,16 @@ TABS.magnetometer.initialize = function (callback) { }); function get_fast_data() { - if (helper.mspQueue.shouldDrop()) { - return; - } MSP.send_message(MSPCodes.MSP_ATTITUDE, false, false, function () { - self.roll_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[0]])); - self.pitch_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[1]])); - self.heading_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[2]])); + self.roll_e.text(i18n.getMessage('initialSetupAttitude', [FC.SENSOR_DATA.kinematics[0]])); + self.pitch_e.text(i18n.getMessage('initialSetupAttitude', [FC.SENSOR_DATA.kinematics[1]])); + self.heading_e.text(i18n.getMessage('initialSetupAttitude', [FC.SENSOR_DATA.kinematics[2]])); self.render3D(); }); } - helper.mspBalancedInterval.add('setup_data_pull_fast', 40, 1, get_fast_data); + interval.add('setup_data_pull_fast', get_fast_data, 40); GUI.content_ready(callback); } @@ -543,8 +592,7 @@ TABS.magnetometer.initialize3D = function () { model_file, camera, scene, - gps, - xyz, + magModels, fc, useWebGlRenderer = false; @@ -571,12 +619,12 @@ TABS.magnetometer.initialize3D = function () { // load the model including materials if (useWebGlRenderer) { - if (MIXER_CONFIG.appliedMixerPreset === -1) { + if (FC.MIXER_CONFIG.appliedMixerPreset === -1) { model_file = 'custom'; - GUI_control.prototype.log("" + chrome.i18n.getMessage("mixerNotConfigured") + ""); + GUI_control.prototype.log("" + i18n.getMessage("mixerNotConfigured") + ""); } else { - model_file = helper.mixer.getById(MIXER_CONFIG.appliedMixerPreset).model; + model_file = mixer.getById(FC.MIXER_CONFIG.appliedMixerPreset).model; } } else { @@ -590,17 +638,16 @@ TABS.magnetometer.initialize3D = function () { this.render3D = function () { - if (!gps || !xyz || !fc) + if (!magModels || !fc) return; - gps.visible = self.showMagnetometer; - xyz.visible = !self.showMagnetometer; + magModels.forEach( (m,i) => m.visible = i == self.elementToShow ); fc.visible = true; var magRotation = new THREE.Euler(-THREE.Math.degToRad(self.alignmentConfig.pitch-180), THREE.Math.degToRad(-180 - self.alignmentConfig.yaw), THREE.Math.degToRad(self.alignmentConfig.roll), 'YXZ'); var matrix = (new THREE.Matrix4()).makeRotationFromEuler(magRotation); - var boardRotation = new THREE.Euler( THREE.Math.degToRad( -self.boardAlignmentConfig.pitch ), THREE.Math.degToRad( -self.boardAlignmentConfig.yaw ), THREE.Math.degToRad( -self.boardAlignmentConfig.roll ), 'YXZ'); + var boardRotation = new THREE.Euler( THREE.Math.degToRad( self.boardAlignmentConfig.pitch), THREE.Math.degToRad( -self.boardAlignmentConfig.yaw ), THREE.Math.degToRad( self.boardAlignmentConfig.roll ), 'YXZ'); var matrix1 = (new THREE.Matrix4()).makeRotationFromEuler(boardRotation); /* @@ -608,8 +655,7 @@ TABS.magnetometer.initialize3D = function () { matrix.premultiply(matrix1); //preset specifies orientation relative to FC, align_max_xxx specify absolute orientation } */ - gps.rotation.setFromRotationMatrix(matrix); - xyz.rotation.setFromRotationMatrix(matrix); + magModels.forEach( (m,i) => m.rotation.setFromRotationMatrix(matrix) ); fc.rotation.setFromRotationMatrix(matrix1); // draw @@ -623,7 +669,7 @@ TABS.magnetometer.initialize3D = function () { camera.aspect = wrapper.width() / wrapper.height(); camera.updateProjectionMatrix(); - this.render3D(); + self.render3D(); }; $(window).on('resize', this.resize3D); @@ -684,6 +730,11 @@ TABS.magnetometer.initialize3D = function () { const manager = new THREE.LoadingManager(); const loader = new THREE.GLTFLoader(manager); + const magModelNames = ['xyz', 'ak8963c', 'ak8963n', 'ak8975', 'ak8975c', 'bn_880', 'diatone_mamba_m10_pro', 'flywoo_goku_m10_pro_v3', 'foxeer_m10q_120', 'foxeer_m10q_180', 'foxeer_m10q_250', + 'geprc_gep_m10_dq', 'gy271', 'gy273', 'hglrc_m100', 'qmc5883', 'holybro_m9n_micro', 'holybro_m9n_micro', 'ist8308', 'ist8310', 'lis3mdl', + 'mag3110', 'matek_m8q', 'matek_m9n', 'matek_m10q', 'mlx90393', 'mp9250', 'qmc5883', 'flywoo_goku_m10_pro_v3', 'ws_m181']; + magModels = []; + //Load the UAV model loader.load('./resources/models/' + model_file + '.gltf', (obj) => { const model = obj.scene; @@ -693,30 +744,22 @@ TABS.magnetometer.initialize3D = function () { const gpsOffset = getDistanceByModelName(model_file); - //Load the GPS model - loader.load('./resources/models/gps.gltf', (obj) => { - gps = obj.scene; - const scaleFactor = 0.04; - gps.scale.set(scaleFactor, scaleFactor, scaleFactor); - gps.position.set(gpsOffset[0], gpsOffset[1] + 0.5, gpsOffset[2]); - gps.traverse(child => { - if (child.material) child.material.metalness = 0; + magModelNames.forEach( (name, i) => + { + loader.load('./resources/models/' + name + '.gltf', (obj) => { + const gps = obj.scene; + const scaleFactor = i==0 ? 0.03 : 0.04; + gps.scale.set(scaleFactor, scaleFactor, scaleFactor); + gps.position.set(gpsOffset[0], gpsOffset[1] + 0.5, gpsOffset[2]); + gps.traverse(child => { + if (child.material) child.material.metalness = 0; + }); + gps.rotation.y = 3 * Math.PI / 2; + model.add(gps); + magModels[i]=gps; + this.resize3D(); }); - gps.rotation.y = 3 * Math.PI / 2; - model.add(gps); - this.resize3D(); - }); - - //Load the XYZ model - loader.load('./resources/models/xyz.gltf', (obj) => { - xyz = obj.scene; - const scaleFactor = 0.04; - xyz.scale.set(scaleFactor, scaleFactor, scaleFactor); - xyz.position.set(gpsOffset[0], gpsOffset[1] + 0.5, gpsOffset[2]); - xyz.rotation.y = 3 * Math.PI / 2; - model.add(xyz); - this.render3D(); - }); + }); //Load the FC model loader.load('./resources/models/fc.gltf', (obj) => { diff --git a/tabs/mission_control.html b/tabs/mission_control.html index eb0ec50c3..94c0a9138 100644 --- a/tabs/mission_control.html +++ b/tabs/mission_control.html @@ -19,8 +19,8 @@ @@ -86,6 +86,14 @@ +
+ + +
+
+ + +
@@ -167,7 +175,7 @@ @@ -178,41 +186,98 @@
- + +
-
- - - - - - - - - - - - -
+
+ + +
+
- + - +
- + - + +
+
+ + +
-
@@ -234,7 +299,7 @@
- +
- +
- +
- +
- + + -
diff --git a/tabs/mission_control.js b/tabs/mission_control.js index 01e46b96f..dced15bf3 100644 --- a/tabs/mission_control.js +++ b/tabs/mission_control.js @@ -1,45 +1,36 @@ 'use strict'; -//////////////////////////////////// -// -// global Parameters definition -// -//////////////////////////////////// - - -// MultiWii NAV Protocol -var MWNP = MWNP || {}; - -// WayPoint type -MWNP.WPTYPE = { - WAYPOINT: 1, - POSHOLD_UNLIM: 2, - POSHOLD_TIME: 3, - RTH: 4, - SET_POI: 5, - JUMP: 6, - SET_HEAD: 7, - LAND: 8 -}; - -MWNP.P3 = { - ALT_TYPE: 0, // Altitude (alt) : Relative (to home altitude) (0) or Absolute (AMSL) (1). - USER_ACTION_1: 1, // WP Action 1 - USER_ACTION_2: 2, // WP Action 2 - USER_ACTION_3: 3, // WP Action 3 - USER_ACTION_4: 4, // WP Action 4 -} - -// Reverse WayPoint type dictionary -function swap(dict) { - let rev_dict = {}; - for (let key in dict) { - rev_dict[dict[key]] = key; - } - return rev_dict; -} - -MWNP.WPTYPE.REV = swap(MWNP.WPTYPE); +const path = require('path'); +const fs = require('fs'); +const ol = require('openlayers'); +const xml2js = require('xml2js'); +const Store = require('electron-store'); +const store = new Store(); +const { dialog } = require("@electron/remote"); + +const MSPChainerClass = require('./../js/msp/MSPchainer'); +const mspHelper = require('./../js/msp/MSPHelper'); +const MSPCodes = require('./../js/msp/MSPCodes'); +const MSP = require('./../js/msp'); +const mspQueue = require('./../js/serial_queue'); +const { GUI, TABS } = require('./../js/gui'); +const FC = require('./../js/fc'); +const CONFIGURATOR = require('./../js/data_storage'); +const i18n = require('./../js/localization'); +const { globalSettings } = require('./../js/globalSettings'); +const MWNP = require('./../js/mwnp'); +const Waypoint = require('./../js/waypoint') +const WaypointCollection = require('./../js/waypointCollection'); +const Safehome = require('./../js/safehome'); +const SafehomeCollection = require('./../js/safehomeCollection'); +const { ApproachDirection, FwApproach } = require('./../js/fwApproach'); +const FwApproachCollection = require('./../js/fwApproachCollection'); +const SerialBackend = require('./../js/serial_backend'); +const { distanceOnLine, wrap_360, calculate_new_cooridatnes } = require('./../js/helpers'); +const Plotly = require('./../js/libraries/plotly-latest.min'); +const interval = require('./../js/intervals'); + +var MAX_NEG_FW_LAND_ALT = -2000; // cm // Dictionary of Parameter 1,2,3 definition depending on type of action selected (refer to MWNP.WPTYPE) var dictOfLabelParameterPoint = { @@ -78,11 +69,13 @@ TABS.mission_control.initialize = function (callback) { let textFeature; var textGeom; let isOffline = false; - let rthUpdateInterval = 0; + let selectedSafehome; + let $safehomeContentBox; + let $waypointOptionsTableBody; + let settings = {speed: 0, alt: 5000, safeRadiusSH: 50, fwApproachAlt: 60, fwLandAlt: 5, maxDistSH: 0, fwApproachLength: 0, fwLoiterRadius: 0, bingDemModel: false}; if (GUI.active_tab != 'mission_control') { GUI.active_tab = 'mission_control'; - googleAnalytics.sendAppView('Mission Control'); } if (CONFIGURATOR.connectionValid) { @@ -90,7 +83,23 @@ TABS.mission_control.initialize = function (callback) { loadChainer.setChain([ mspHelper.getMissionInfo, //mspHelper.loadWaypoints, - //mspHelper.loadSafehomes + mspHelper.loadSafehomes, + mspHelper.loadFwApproach, + function (callback) { + mspHelper.getSetting("nav_fw_land_approach_length").then((data) => { + settings.fwApproachLength = parseInt(data.value); + }).then(callback); + }, + function (callback) { + mspHelper.getSetting("safehome_max_distance").then((data) => { + settings.maxDistSH = parseInt(data.value) / 100; + }).then(callback); + }, + function (callback) { + mspHelper.getSetting(("nav_fw_loiter_radius")).then((data) => { + settings.fwLoiterRadius = parseInt(data.value); + }).then(callback); + } ]); loadChainer.setExitPoint(loadHtml); loadChainer.execute(); @@ -98,10 +107,19 @@ TABS.mission_control.initialize = function (callback) { // FC not connected, load page anyway loadHtml(); + if (!FC.FW_APPROACH) { + FC.FW_APPROACH = new FwApproachCollection(); + } + if (!FC.SAFEHOMES) { + FC.SAFEHOMES = new SafehomeCollection(); + } + for (let i = 0; i < FC.FW_APPROACH.getMaxFwApproachCount(); i++){ + FC.FW_APPROACH.put(new FwApproach(i)); + } } function loadHtml() { - GUI.load("./tabs/mission_control.html", process_html); + GUI.load(path.join(__dirname, "mission_control.html"), process_html); } function process_html() { @@ -117,20 +135,30 @@ TABS.mission_control.initialize = function (callback) { isOffline = true; } - $safehomesTable = $('.safehomesTable'); - $safehomesTableBody = $('#safehomesTableBody'); - $waypointOptionsTable = $('.waypointOptionsTable'); + $safehomeContentBox = $('#SafehomeContentBox'); $waypointOptionsTableBody = $('#waypointOptionsTableBody'); if (typeof require !== "undefined") { loadSettings(); // let the dom load finish, avoiding the resizing of the map setTimeout(initMap, 200); + if (!isOffline) { + setTimeout(() => { + if (FC.SAFEHOMES.safehomeCount() >= 1) { + updateSelectedShAndFwAp(0); + } else { + selectedSafehome = null; + selectedFwApproachSh = null; + } + renderSafehomesOnMap(); + updateSafehomeInfo(); + }, 500); + } } else { $('#missionMap, #missionControls').hide(); $('#notLoadMap').show(); } - localize(); + i18n.localize(); function get_raw_gps_data() { MSP.send_message(MSPCodes.MSP_RAW_GPS, false, false, get_comp_gps_data); @@ -151,11 +179,11 @@ TABS.mission_control.initialize = function (callback) { function update_gpsTrack() { - let lat = GPS_DATA.lat / 10000000; - let lon = GPS_DATA.lon / 10000000; + let lat = FC.GPS_DATA.lat / 10000000; + let lon = FC.GPS_DATA.lon / 10000000; //Update map - if (GPS_DATA.fix >= 2) { + if (FC.GPS_DATA.fix >= 2) { if (!cursorInitialized) { cursorInitialized = true; @@ -167,7 +195,7 @@ TABS.mission_control.initialize = function (callback) { anchor: [0.5, 0.5], opacity: 1, scale: 0.6, - src: '../images/icons/icon_mission_airplane.png' + src: './images/icons/icon_mission_airplane.png' })) }); @@ -194,7 +222,7 @@ TABS.mission_control.initialize = function (callback) { anchor: [0.5, 1.0], opacity: 1, scale: 0.5, - src: '../images/icons/cf_icon_RTH.png' + src: './images/icons/cf_icon_RTH.png' })) }); @@ -289,28 +317,16 @@ TABS.mission_control.initialize = function (callback) { breadCrumbLS.setCoordinates(coords); } - curPosStyle.getImage().setRotation((SENSOR_DATA.kinematics[2]/360.0) * 6.28318); + curPosStyle.getImage().setRotation((FC.SENSOR_DATA.kinematics[2]/360.0) * 6.28318); //update data text textGeom.setCoordinates(map.getCoordinateFromPixel([0,0])); let tmpText = textStyle.getText(); tmpText.setText(' \n' + - 'H: ' + SENSOR_DATA.kinematics[2] + - '\nAlt: ' + SENSOR_DATA.altitude + - 'm\nSpeed: ' + GPS_DATA.speed + 'cm/s\n' + - 'Dist: ' + GPS_DATA.distanceToHome + 'm'); - - //update RTH every 5th GPS update since it really shouldn't change - if(rthUpdateInterval >= 5) - { - MISSION_PLANNER.bufferPoint.number = -1; //needed to get point 0 which id RTH - MSP.send_message(MSPCodes.MSP_WP, mspHelper.crunch(MSPCodes.MSP_WP), false, function rth_update() { - var coord = ol.proj.fromLonLat([MISSION_PLANNER.bufferPoint.lon, MISSION_PLANNER.bufferPoint.lat]); - rthGeo.setCoordinates(coord); - }); - rthUpdateInterval = 0; - } - rthUpdateInterval++; + 'H: ' + FC.SENSOR_DATA.kinematics[2] + + '\nAlt: ' + FC.SENSOR_DATA.altitude + + 'm\nSpeed: ' + FC.GPS_DATA.speed + 'cm/s\n' + + 'Dist: ' + FC.GPS_DATA.distanceToHome + 'm'); } } @@ -320,19 +336,15 @@ TABS.mission_control.initialize = function (callback) { */ if(!isOffline) { - helper.mspBalancedInterval.add('gps_pull', 200, 3, function gps_update() { + interval.add('gps_pull', function gps_update() { // avoid usage of the GPS commands until a GPS sensor is detected for targets that are compiled without GPS support. - if (!have_sensor(CONFIG.activeSensors, 'gps')) { + if (!SerialBackend.have_sensor(FC.CONFIG.activeSensors, 'gps')) { update_gpsTrack(); return; } - if (helper.mspQueue.shouldDrop()) { - return; - } - get_raw_gps_data(); - }); + }, 200); } GUI.content_ready(callback); @@ -349,7 +361,11 @@ TABS.mission_control.initialize = function (callback) { ////////////////////////////////////////////////////////////////////////////////////////////// var markers = []; // Layer for Waypoints var lines = []; // Layer for lines between waypoints - var safehomeMarkers = []; // layer for Safehome points + var safehomeMarkers = []; // layer for Safehome points + var safehomeMarkers = []; // layer for Safehome points + var approachLayers = [] // Layers for FW approach + var safehomeMarkers = []; // layer for Safehome points + var approachLayers = [] // Layers for FW approach var map; @@ -360,19 +376,16 @@ TABS.mission_control.initialize = function (callback) { var selectedFeature = null; var tempMarker = null; var disableMarkerEdit = false; + var selectedFwApproachWp = null; + var selectedFwApproachSh = null; + var lockShExclHeading = false; + ////////////////////////////////////////////////////////////////////////////////////////////// // define & init parameters for default Settings ////////////////////////////////////////////////////////////////////////////////////////////// - var settings = {speed: 0, alt: 5000, safeRadiusSH : 50, maxDistSH : 0, bingDemModel : false}; - if (CONFIGURATOR.connectionValid) { - mspHelper.getSetting("safehome_max_distance").then(function (s) { - if (s) { - settings.maxDistSH = Number(s.value)/100; - } - }); - } + ////////////////////////////////////////////////////////////////////////////////////////////// // define & init Waypoints parameters @@ -395,8 +408,8 @@ TABS.mission_control.initialize = function (callback) { ////////////////////////////////////////////////////////////////////////////////////////////// // define & init Safehome parameters ////////////////////////////////////////////////////////////////////////////////////////////// - //var SAFEHOMES = new SafehomeCollection(); // TO COMMENT FOR RELEASE : DECOMMENT FOR DEBUG - //SAFEHOMES.inflate(); // TO COMMENT FOR RELEASE : DECOMMENT FOR DEBUG + //var FC.SAFEHOMES = new SafehomeCollection(); // TO COMMENT FOR RELEASE : DECOMMENT FOR DEBUG + //FC.SAFEHOMES.inflate(); // TO COMMENT FOR RELEASE : DECOMMENT FOR DEBUG //var safehomeRangeRadius = 200; //meters //var safehomeSafeRadius = 50; //meters @@ -426,22 +439,29 @@ TABS.mission_control.initialize = function (callback) { // ///////////////////////////////////////////// function loadSettings() { - chrome.storage.local.get('missionPlannerSettings', function (result) { - if (result.missionPlannerSettings) { - settings = result.missionPlannerSettings; + var missionPlannerSettings = store.get('missionPlannerSettings', false); + if (missionPlannerSettings) { + if (!missionPlannerSettings.fwApproachLength && settings.fwApproachLength) { + missionPlannerSettings.fwApproachLength = settings.fwApproachLength; + missionPlannerSettings.maxDistSH = settings.maxDistSH; + missionPlannerSettings.fwLoiterRadius = settings.fwLoiterRadius; } - refreshSettings(); - }); + saveSettings(); + settings = missionPlannerSettings; + } + refreshSettings(); } function saveSettings() { - chrome.storage.local.set({'missionPlannerSettings': settings}); + store.set('missionPlannerSettings', settings); } function refreshSettings() { $('#MPdefaultPointAlt').val(String(settings.alt)); $('#MPdefaultPointSpeed').val(String(settings.speed)); $('#MPdefaultSafeRangeSH').val(String(settings.safeRadiusSH)); + $('#MPdefaultFwApproachAlt').val(String(settings.fwApproachAlt)); + $('#MPdefaultLandAlt').val(String(settings.fwLandAlt)); } function closeSettingsPanel() { @@ -458,59 +478,29 @@ TABS.mission_control.initialize = function (callback) { cleanSafehomeLayers(); } - function renderSafehomesTable() { - /* - * Process safehome table UI - */ - let safehomes = SAFEHOMES.get(); - $safehomesTableBody.find("*").remove(); - for (let safehomeIndex in safehomes) { - if (safehomes.hasOwnProperty(safehomeIndex)) { - const safehome = safehomes[safehomeIndex]; - - $safehomesTableBody.append('\ - \ -
\ - \ -
\ - \ - \ - \ - \ - \ - \ - '); - - const $row = $safehomesTableBody.find('tr:last'); - - $row.find(".safehome-number").text(safehome.getNumber()+1); - - $row.find(".safehome-enabled-value").prop('checked',safehome.isUsed()).change(function () { - safehome.setEnabled((($(this).prop('checked')) ? 1 : 0)); - SAFEHOMES.updateSafehome(safehome); - cleanSafehomeLayers(); - renderSafehomesOnMap(); - }); + function checkApproachAltitude(altitude, isSeaLevelRef, sealevel) { - $row.find(".safehome-lon").val(safehome.getLonMap()).change(function () { - safehome.setLon(Math.round(Number($(this).val()) * 10000000)); - SAFEHOMES.updateSafehome(safehome); - cleanSafehomeLayers(); - renderSafehomesOnMap(); - }); + if (altitude - (isSeaLevelRef ? sealevel * 100 : 0 ) < 0) { + GUI.alert(i18n.getMessage('MissionPlannerAltitudeChangeReset')); + return false; + } - $row.find(".safehome-lat").val(safehome.getLatMap()).change(function () { - safehome.setLat(Math.round(Number($(this).val()) * 10000000)); - SAFEHOMES.updateSafehome(safehome); - cleanSafehomeLayers(); - renderSafehomesOnMap(); - }); + return true; + } - $row.find("[data-role='safehome-center']").attr("data-index", safehomeIndex); - } + function checkLandingAltitude(altitude, isSeaLevelRef, sealevel) { + + if (altitude - (isSeaLevelRef ? sealevel * 100 : 0 ) < MAX_NEG_FW_LAND_ALT) { + GUI.alert(i18n.getMessage('MissionPlannerFwLAndingAltitudeChangeReset')); + return false; } - GUI.switchery(); - localize(); + + return true; + } + + function updateSafehomeInfo(){ + let freeSamehomes = FC.SAFEHOMES.getMaxSafehomeCount() - FC.SAFEHOMES.safehomeCount() + $('#availableSafehomes').text(freeSamehomes + '/' + FC.SAFEHOMES.getMaxSafehomeCount()); } @@ -518,8 +508,12 @@ TABS.mission_control.initialize = function (callback) { /* * Process safehome on Map */ - SAFEHOMES.get().forEach(function (safehome) { - map.addLayer(addSafeHomeMarker(safehome)); + FC.SAFEHOMES.get().forEach(safehome => { + addFwApproach(safehome.getLonMap(), safehome.getLatMap(), FC.FW_APPROACH.get()[safehome.getNumber()], safehomeMarkers); + }); + FC.SAFEHOMES.get().forEach(safehome => { + addSafehomeCircles(safehome); + addSafeHomeMarker(safehome); }); } @@ -539,7 +533,7 @@ TABS.mission_control.initialize = function (callback) { anchor: [0.5, 1], opacity: 1, scale: 0.5, - src: '../images/icons/cf_icon_safehome' + (safehome.isUsed() ? '_used' : '')+ '.png' + src: './images/icons/cf_icon_safehome' + (safehome.isUsed() ? '_used' : '')+ '.png' })), text: new ol.style.Text(({ text: String(Number(safehome.getNumber())+1), @@ -556,7 +550,107 @@ TABS.mission_control.initialize = function (callback) { }); } - function addSafeHomeMarker(safehome) { + function paintApproachLine(pos1, pos2, color, layers) + { + var line = new ol.geom.LineString([ol.proj.fromLonLat([pos1.lon, pos1.lat]), ol.proj.fromLonLat([pos2.lon, pos2.lat])]); + + var feature = new ol.Feature({ + geometry: line + }); + + var styles = [ new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: color, + width: 3, + }), + }) + ]; + + var geometry = feature.getGeometry(); + geometry.forEachSegment(function (start, end) { + var dx = end[0] - start[0]; + var dy = end[1] - start[1]; + var rotation = Math.atan2(dy, dx); + + styles.push(new ol.style.Style({ + geometry: new ol.geom.Point(distanceOnLine(start, end, -8)), + image: new ol.style.RegularShape({ + fill: new ol.style.Fill({color}), + points: 3, + radius: 8, + rotation: -rotation, + angle: Math.PI / 2 // rotate -90° + }) + })); + }); + + feature.setStyle(styles); + + var vectorSource = new ol.source.Vector({ + features: [feature] + }); + + + var vectorLayer = new ol.layer.Vector({ + source: vectorSource + }); + + + + vectorLayer.kind = "approachline"; + vectorLayer.selection = false; + + + approachLayers.push(vectorLayer); + + approachLayers.push(vectorLayer); + map.addLayer(vectorLayer); + layers.push(vectorLayer); + + return vectorLayer; + } + + function paintApproach(landCoord, approachLength, bearing, approachDirection, layers) { + + var pos1 = calculate_new_cooridatnes(landCoord, bearing, approachLength); + let direction; + if (approachDirection == ApproachDirection.LEFT) { + direction = wrap_360(bearing + 90); + } else { + direction = wrap_360(bearing - 90); + } + + var pos2 = calculate_new_cooridatnes(pos1, direction, Math.max(settings.fwLoiterRadius * 4, settings.fwApproachLength / 2)); + + paintApproachLine(landCoord, pos2, '#0025a1', layers); + paintApproachLine(pos2, pos1, '#0025a1', layers); + paintApproachLine(pos1, landCoord, '#f78a05', layers); + } + + function addFwApproach(lon, lat, fwApproach, layers) + { + if (fwApproach.getLandHeading1() != 0) { + let bearing = wrap_360(Math.abs(fwApproach.getLandHeading1()) + 180); + paintApproach({lat: lat, lon: lon}, settings.fwApproachLength, bearing, fwApproach.getApproachDirection(), layers); + } + + if (fwApproach.getLandHeading1() > 0) { + let direction = fwApproach.getApproachDirection() == ApproachDirection.LEFT ? ApproachDirection.RIGHT : ApproachDirection.LEFT; + paintApproach({lat: lat, lon: lon}, settings.fwApproachLength, fwApproach.getLandHeading1(), direction, layers); + } + + if (fwApproach.getLandHeading2() != 0) { + let bearing = wrap_360(Math.abs(fwApproach.getLandHeading2()) + 180); + paintApproach({lat: lat, lon: lon}, settings.fwApproachLength, bearing, fwApproach.getApproachDirection(), layers); + } + + if (fwApproach.getLandHeading2() > 0) { + let direction = fwApproach.getApproachDirection() == ApproachDirection.LEFT ? ApproachDirection.RIGHT : ApproachDirection.LEFT; + paintApproach({lat: lat, lon: lon}, settings.fwApproachLength, fwApproach.getLandHeading2(), direction, layers); + } + } + + function addSafehomeCircles(safehome) { /* * add safehome on Map */ @@ -611,8 +705,32 @@ TABS.mission_control.initialize = function (callback) { vectorLayer.selection = false; safehomeMarkers.push(vectorLayer); + map.addLayer(vectorLayer); + } - return vectorLayer; + function addSafeHomeMarker(safehome) { + + let coord = ol.proj.fromLonLat([safehome.getLonMap(), safehome.getLatMap()]); + var iconFeature = new ol.Feature({ + geometry: new ol.geom.Point(coord), + name: 'safehome' + }); + + var vectorLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: [iconFeature] + }), + style : function(iconFeature) { + return [getSafehomeIcon(safehome)]; + } + }); + + vectorLayer.kind = "safehome"; + vectorLayer.number = safehome.getNumber(); + vectorLayer.selection = true; + + safehomeMarkers.push(vectorLayer); + map.addLayer(vectorLayer); } function getProjectedRadius(radius) { @@ -646,13 +764,13 @@ TABS.mission_control.initialize = function (callback) { * Process home table UI */ - $(".home-lat").val(HOME.getLatMap()).change(function () { + $(".home-lat").val(HOME.getLatMap()).on('change', function () { HOME.setLat(Math.round(Number($(this).val()) * 10000000)); cleanHomeLayers(); renderHomeOnMap(); }); - $(".home-lon").val(HOME.getLonMap()).change(function () { + $(".home-lon").val(HOME.getLonMap()).on('change', function () { HOME.setLon(Math.round(Number($(this).val()) * 10000000)); cleanHomeLayers(); renderHomeOnMap(); @@ -724,7 +842,7 @@ TABS.mission_control.initialize = function (callback) { anchor: [0.5, 1], opacity: 1, scale: 0.5, - src: '../images/icons/cf_icon_home.png' + src: './images/icons/cf_icon_home.png' })), }); } @@ -875,7 +993,7 @@ TABS.mission_control.initialize = function (callback) { }); mission.reinit(); - tempMissionData = multimission.get().slice(startWPCount, endWPCount + 1); // copy selected single mission from MM + var tempMissionData = multimission.get().slice(startWPCount, endWPCount + 1); // copy selected single mission from MM let i = 0; tempMissionData.forEach(function (element) { // write mission copy to active map mission mission.put(element); @@ -905,7 +1023,7 @@ TABS.mission_control.initialize = function (callback) { MMCount ++; } }); - $('#multimissionOptionList').val(MMCount).change(); + $('#multimissionOptionList').val(MMCount).trigger('change'); } function deleteMultimission() { @@ -948,14 +1066,23 @@ TABS.mission_control.initialize = function (callback) { function fileLoadMultiMissionCheck() { if (singleMissionActive()) { return true; - } else if (confirm(chrome.i18n.getMessage('confirm_overwrite_multimission_file_load_option'))) { - nwdialog.setContext(document); - nwdialog.openFileDialog(function(result) { - loadMissionFile(result); - multimissionCount = 0; - multimission.flush(); - renderMultimissionTable(); - }) + } else if (confirm(i18n.getMessage('confirm_overwrite_multimission_file_load_option'))) { + var options = { + filters: [ { name: "Mission file", extensions: ['mission'] } ] + }; + dialog.showOpenDialog(options).then(result => { + if (result.canceled) { + console.log('No file selected'); + return; + } + + if (result.filePaths.length == 1) { + loadMissionFile(result.filePaths[0]); + multimissionCount = 0; + multimission.flush(); + renderMultimissionTable(); + } + }); } return false; } @@ -1029,7 +1156,7 @@ TABS.mission_control.initialize = function (callback) { anchor: [0.5, 1], opacity: 1, scale: 0.5, - src: '../images/icons/cf_icon_position' + (dictofPointIcon[waypoint.getAction()] != '' ? '_'+dictofPointIcon[waypoint.getAction()] : '') + (isEdit ? '_edit' : '')+ '.png' + src: './images/icons/cf_icon_position' + (dictofPointIcon[waypoint.getAction()] != '' ? '_'+dictofPointIcon[waypoint.getAction()] : '') + (isEdit ? '_edit' : '')+ '.png' })), text: new ol.style.Text(({ text: String(Number(waypoint.getLayerNumber()+1)), @@ -1073,15 +1200,15 @@ TABS.mission_control.initialize = function (callback) { // If one is POI, draw orange line in-between and modulate dashline each time a new POI is defined else if (typeof oldPos !== 'undefined' && activatePoi == true && activateHead != true) { if ((poiList.length % 2) == 0) { - paintLine(oldPos, coord, element.getNumber(), color='#ffb725', lineDash=5); + paintLine(oldPos, coord, element.getNumber(), '#ffb725', 5); } else { - paintLine(oldPos, coord, element.getNumber(), color='#ffb725'); + paintLine(oldPos, coord, element.getNumber(), '#ffb725'); } } // If one is SET_HEAD, draw labelled line in-between with heading value else if (typeof oldPos !== 'undefined' && activatePoi != true && activateHead == true) { - paintLine(oldPos, coord, element.getNumber(), color='#1497f1', lineDash=0, lineText=String(oldHeading)+"°"); + paintLine(oldPos, coord, element.getNumber(), '#1497f1', 0, String(oldHeading)+"°"); } if (element.getEndMission() == 0xA5) { @@ -1098,7 +1225,7 @@ TABS.mission_control.initialize = function (callback) { if (element.getAction() == MWNP.WPTYPE.JUMP) { let jumpWPIndex = multiMissionWPNum + element.getP1(); let coord = ol.proj.fromLonLat([mission.getWaypoint(jumpWPIndex).getLonMap(), mission.getWaypoint(jumpWPIndex).getLatMap()]); - paintLine(oldPos, coord, element.getNumber(), color='#e935d6', lineDash=5, lineText="Repeat x"+(element.getP2() == -1 ? " infinite" : String(element.getP2())), selection=false, arrow=true); + paintLine(oldPos, coord, element.getNumber(), '#e935d6', 5, "Repeat x"+(element.getP2() == -1 ? " infinite" : String(element.getP2())), false, true); } // If classic WPs is defined with a heading = -1, change Boolean for POI to false. If it is defined with a value different from -1, activate Heading boolean else if (element.getAction() == MWNP.WPTYPE.SET_HEAD) { @@ -1121,6 +1248,9 @@ TABS.mission_control.initialize = function (callback) { multiMissionWPNum = element.getNumber() + 1; } } + if (element.getAction() == MWNP.WPTYPE.LAND) { + addFwApproach(element.getLonMap(), element.getLatMap(), FC.FW_APPROACH.get()[FC.SAFEHOMES.getMaxSafehomeCount() + element.getMultiMissionIdx()], lines); + } }); //reset text position if (textGeom) { @@ -1131,7 +1261,11 @@ TABS.mission_control.initialize = function (callback) { if (disableMarkerEdit) { $('#missionDistance').text('N/A'); } else { - $('#missionDistance').text(lengthMission[lengthMission.length -1] != -1 ? lengthMission[lengthMission.length -1].toFixed(1) : 'infinite'); + if (lengthMission.length >= 1) { + $('#missionDistance').text(lengthMission[lengthMission.length -1].toFixed(1)); + } else { + $('#missionDistance').text('infinite'); + } } } @@ -1171,7 +1305,7 @@ TABS.mission_control.initialize = function (callback) { featureArrow.setStyle( new ol.style.Style({ image: new ol.style.Icon({ - src: '../images/icons/cf_icon_arrow.png', + src: './images/icons/cf_icon_arrow.png', scale: 0.3, anchor: [0.5, 0.5], rotateWithView: true, @@ -1234,20 +1368,65 @@ TABS.mission_control.initialize = function (callback) { function redrawLayers() { if (!mission.isEmpty()) { + repaintLine4Waypoints(mission); mission.get().forEach(function (element) { if (!element.isAttached()) { map.addLayer(addWaypointMarker(element)); } }); - repaintLine4Waypoints(mission); + } } function redrawLayer() { + repaintLine4Waypoints(mission); if (selectedFeature && selectedMarker) { selectedFeature.setStyle(getWaypointIcon(selectedMarker, true)); } - repaintLine4Waypoints(mission); + } + + function renderSafeHomeOptions() { + if (selectedSafehome && selectedFwApproachSh) { + + lockShExclHeading = true; + if (!$('#missionPlannerSafehome').is(':visible')) { + $('#missionPlannerSafehome').fadeIn(300); + } + + $('#SafehomeContentBox').show(); + + if (selectedFwApproachSh.getLandHeading1() == 0 && selectedFwApproachSh.getLandHeading1() == 0 && selectedFwApproachSh.getApproachAltAsl() == 0 && selectedFwApproachSh.getLandAltAsl() == 0) { + selectedFwApproachSh.setApproachAltAsl(settings.fwApproachAlt * 100); + selectedFwApproachSh.setLandAltAsl(settings.fwLandAlt * 100); + } + + if (selectedFwApproachSh.getElevation() == 0) { + (async () => { + const elevation = await selectedFwApproachSh.getElevationFromServer(selectedSafehome.getLonMap(), selectedSafehome.getLatMap(), globalSettings) * 100; + selectedFwApproachSh.setElevation(elevation); + $('#safehomeElevation').text(selectedFwApproachSh.getElevation() / 100 + " m"); + })(); + } + + const $safehomeBox = $safehomeContentBox.find('.missionPlannerSafehomeBox:last-child'); + $safehomeBox.find('.spacer_box_title').text(i18n.getMessage('safehomeEdit') + ' ' + (selectedSafehome.getNumber() + 1)); + + $('#safehomeLatitude').val(selectedSafehome.getLatMap()); + $('#safehomeLongitude').val(selectedSafehome.getLonMap()); + changeSwitchery($('#safehomeSeaLevelRef'), selectedFwApproachSh.getIsSeaLevelRef()); + $('#safehomeApproachAlt').val(selectedFwApproachSh.getApproachAltAsl()); + $('#safehomeLandAlt').val(selectedFwApproachSh.getLandAltAsl()); + $('#geozoneApproachDirection').val(selectedFwApproachSh.getApproachDirection()); + $('#safehomeLandHeading1').val(Math.abs(selectedFwApproachSh.getLandHeading1())); + changeSwitchery($('#safehomeLandHeading1Excl'), selectedFwApproachSh.getLandHeading1() < 0); + $('#safehomeLandHeading2').val(Math.abs(selectedFwApproachSh.getLandHeading2())); + changeSwitchery($('#safehomeLandHeading2Excl'), selectedFwApproachSh.getLandHeading2() < 0); + $('#safehomeLandAltM').text(selectedFwApproachSh.getLandAltAsl() / 100 + " m"); + $('#safehomeApproachAltM').text(selectedFwApproachSh.getApproachAltAsl() / 100 + " m"); + lockShExclHeading = false; + } else { + $('#SafehomeContentBox').hide(); + } } function renderWaypointOptionsTable(waypoint) { @@ -1284,7 +1463,7 @@ TABS.mission_control.initialize = function (callback) { GUI.fillSelect($row.find(".waypointOptions-action"), waypointOptions, waypointOptions.indexOf(MWNP.WPTYPE.REV[element.getAction()])); - $row.find(".waypointOptions-action").val(waypointOptions.indexOf(MWNP.WPTYPE.REV[element.getAction()])).change(function () { + $row.find(".waypointOptions-action").val(waypointOptions.indexOf(MWNP.WPTYPE.REV[element.getAction()])).on('change', function () { element.setAction(MWNP.WPTYPE[waypointOptions[$(this).val()]]); for (var i = 1; i <= 3; i++) { if (dictOfLabelParameterPoint[element.getAction()]['parameter'+String(i)] != '') { @@ -1303,31 +1482,31 @@ TABS.mission_control.initialize = function (callback) { $row.find(".waypointOptions-number").text(element.getAttachedNumber()+1); - $row.find(".waypointOptions-p1").val((MWNP.WPTYPE.REV[element.getAction()] == "JUMP" ? element.getP1()+1 : element.getP1())).change(function () { + $row.find(".waypointOptions-p1").val((MWNP.WPTYPE.REV[element.getAction()] == "JUMP" ? element.getP1()+1 : element.getP1())).on('change', function () { if (MWNP.WPTYPE.REV[element.getAction()] == "SET_HEAD") { if ($(this).val() >= 360 || ($(this).val() < 0 && $(this).val() != -1)) { $(this).val(-1); - alert(chrome.i18n.getMessage('MissionPlannerHeadSettingsCheck')); + GUI.alert(i18n.getMessage('MissionPlannerHeadSettingsCheck')); } } else if (MWNP.WPTYPE.REV[element.getAction()] == "RTH") { if ($(this).val() != 0 && $(this).val() != 1) { $(this).val(0); - alert(chrome.i18n.getMessage('MissionPlannerRTHSettingsCheck')); + GUI.alert(i18n.getMessage('MissionPlannerRTHSettingsCheck')); } } else if (MWNP.WPTYPE.REV[element.getAction()] == "JUMP") { if ($(this).val() > mission.getNonAttachedList().length || $(this).val() < 1) { $(this).val(1); - alert(chrome.i18n.getMessage('MissionPlannerJumpSettingsCheck')); + GUI.alert(i18n.getMessage('MissionPlannerJumpSettingsCheck')); } else if (mission.getPoiList().length != 0 && mission.getPoiList()) { if (mission.getPoiList().includes(mission.convertJumpNumberToWaypoint(Number($(this).val())-1))) { $(this).val(1); - alert(chrome.i18n.getMessage('MissionPlannerJump3SettingsCheck')); + GUI.alert(i18n.getMessage('MissionPlannerJump3SettingsCheck')); } } } @@ -1337,12 +1516,12 @@ TABS.mission_control.initialize = function (callback) { redrawLayer(); }); - $row.find(".waypointOptions-p2").val(element.getP2()).change(function () { + $row.find(".waypointOptions-p2").val(element.getP2()).on('change', function () { if (MWNP.WPTYPE.REV[element.getAction()] == "JUMP") { if ($(this).val() > 10 || ($(this).val() < 0 && $(this).val() != -1)) { $(this).val(0); - alert(chrome.i18n.getMessage('MissionPlannerJump2SettingsCheck')); + GUI.alert(i18n.getMessage('MissionPlannerJump2SettingsCheck')); } } element.setP2(Number($(this).val())); @@ -1355,7 +1534,7 @@ TABS.mission_control.initialize = function (callback) { }); GUI.switchery(); - localize(); + i18n.localize();; return waypoint; } @@ -1431,7 +1610,7 @@ TABS.mission_control.initialize = function (callback) { var button = document.createElement('button'); button.innerHTML = ' '; - button.style = 'background: url(\'../images/CF_settings_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; + button.style = 'background: url(\'./images/CF_settings_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; var handleShowSettings = function () { $('#missionPlannerSettings').fadeIn(300); @@ -1463,14 +1642,11 @@ TABS.mission_control.initialize = function (callback) { var button = document.createElement('button'); button.innerHTML = ' '; - button.style = 'background: url(\'../images/icons/cf_icon_safehome_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; + button.style = 'background: url(\'./images/icons/cf_icon_safehome_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; var handleShowSafehome = function () { $('#missionPlannerSafehome').fadeIn(300); - //SAFEHOMES.flush(); - //mspHelper.loadSafehomes(); cleanSafehomeLayers(); - renderSafehomesTable(); renderSafehomesOnMap(); $('#safeHomeMaxDistance').text(settings.maxDistSH); $('#SafeHomeSafeDistance').text(settings.safeRadiusSH); @@ -1501,7 +1677,7 @@ TABS.mission_control.initialize = function (callback) { var button = document.createElement('button'); button.innerHTML = ' '; - button.style = 'background: url(\'../images/icons/cf_icon_elevation_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; + button.style = 'background: url(\'./images/icons/cf_icon_elevation_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; var handleShowSettings = function () { $('#missionPlannerHome').fadeIn(300); @@ -1538,7 +1714,7 @@ TABS.mission_control.initialize = function (callback) { var button = document.createElement('button'); button.innerHTML = ' '; - button.style = 'background: url(\'../images/icons/cf_icon_multimission_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; + button.style = 'background: url(\'./images/icons/cf_icon_multimission_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; var handleShowSettings = function () { $('#missionPlannerMultiMission').fadeIn(300); @@ -1591,6 +1767,10 @@ TABS.mission_control.initialize = function (callback) { * @param {ol.MapBrowserEvent} evt Map browser event. */ app.Drag.prototype.handleDragEvent = function (evt) { + if (tempMarker.kind == "safehomecircle") { + return; + } + var map = evt.map; var feature = map.forEachFeatureAtPixel(evt.pixel, @@ -1622,12 +1802,16 @@ TABS.mission_control.initialize = function (callback) { repaintLine4Waypoints(mission); } else if (tempMarker.kind == "safehome") { - let tempSH = SAFEHOMES.getSafehome(tempMarker.number); - tempSH.setLon(Math.round(coord[0] * 10000000)); - tempSH.setLat(Math.round(coord[1] * 10000000)); - SAFEHOMES.updateSafehome(tempSH); - $safehomesTableBody.find('tr:nth-child('+String(tempMarker.number+1)+') > td > .safehome-lon').val(Math.round(coord[0] * 10000000) / 10000000); - $safehomesTableBody.find('tr:nth-child('+String(tempMarker.number+1)+') > td > .safehome-lat').val(Math.round(coord[1] * 10000000) / 10000000); + let tmpSafehome = FC.SAFEHOMES.get()[tempMarker.number]; + tmpSafehome.setLon(Math.round(coord[0] * 1e7)); + tmpSafehome.setLat(Math.round(coord[1] * 1e7)); + + $('#safeHomeLongitude').val(Math.round(coord[0] * 1e7)); + $('#safeHomeLatitude').val(Math.round(coord[1] * 1e7)); + updateSelectedShAndFwAp(tempMarker.number); + renderSafeHomeOptions(); + cleanSafehomeLayers(); + renderSafehomesOnMap(); } else if (tempMarker.kind == "home") { HOME.setLon(Math.round(coord[0] * 10000000)); @@ -1672,6 +1856,22 @@ TABS.mission_control.initialize = function (callback) { $('#elevationValueAtWP').text(elevationAtWP); const returnAltitude = checkAltElevSanity(false, mission.getWaypoint(tempMarker.number).getAlt(), elevationAtWP, mission.getWaypoint(tempMarker.number).getP3()); mission.getWaypoint(tempMarker.number).setAlt(returnAltitude); + + if (mission.getWaypoint(tempMarker.number).getAction() == MWNP.WPTYPE.LAND) { + let approach = FC.FW_APPROACH.get()[FC.SAFEHOMES.getMaxSafehomeCount() + mission.getWaypoint(tempMarker.number).getMultiMissionIdx()]; + if (approach.getIsSeaLevelRef()) { + if (approach.getElevation() != 0) { + approach.setApproachAltAsl(approach.getApproachAltAsl() - approach.getElevation() + elevationAtWP * 100); + approach.setLandAltAsl(approach.getLandAltAsl() - approach.getElevation() + elevationAtWP * 100); + } + approach.setElevation(elevationAtWP * 100); + $('#wpApproachAlt').val(approach.getApproachAltAsl()); + $('#wpLandAlt').val(approach.getLandAltAsl); + $('#wpLandAltM').text(approach.getLandAltAsl() / 100 + " m"); + $('#wpApproachAltM').text(approach.getApproachAltAsl() / 100 + " m"); + } + } + plotElevation(); })() } @@ -1684,15 +1884,32 @@ TABS.mission_control.initialize = function (callback) { plotElevation(); })() } + else if (tempMarker.kind == "safehome") { + (async () => { + let approach = FC.FW_APPROACH.get()[tempMarker.number]; + let safehome = FC.SAFEHOMES.get()[tempMarker.number]; + const elevation = await approach.getElevationFromServer(safehome.getLonMap(), safehome.getLatMap(), globalSettings) * 100; + $('#safehomeElevation').text(elevation / 100 + " m"); + if (approach.getIsSeaLevelRef()) { + if (approach.getElevation() != 0) { + approach.setApproachAltAsl(approach.getApproachAltAsl() - approach.getElevation() + elevation); + approach.setLandAltAsl(approach.getLandAltAsl() - approach.getElevation() + elevation); + } + approach.setElevation(elevation); + } + renderSafeHomeOptions(); + })() + } this.coordinate_ = null; this.feature_ = null; return false; }; - var lat = (GPS_DATA ? (GPS_DATA.lat / 10000000) : 0); - var lon = (GPS_DATA ? (GPS_DATA.lon / 10000000) : 0); + var lat = (FC.GPS_DATA ? (FC.GPS_DATA.lat / 10000000) : 0); + var lon = (FC.GPS_DATA ? (FC.GPS_DATA.lon / 10000000) : 0); let mapLayer; + let control_list; if (globalSettings.mapProviderType == 'bing') { mapLayer = new ol.source.BingMaps({ @@ -1759,20 +1976,20 @@ TABS.mission_control.initialize = function (callback) { // save map view settings when user moves it ////////////////////////////////////////////////////////////////////////// map.on('moveend', function (evt) { - chrome.storage.local.set({'missionPlannerLastValues': { + store.set('missionPlannerLastValues', { center: ol.proj.toLonLat(map.getView().getCenter()), zoom: map.getView().getZoom() - }}); + }); }); ////////////////////////////////////////////////////////////////////////// // load map view settings on startup ////////////////////////////////////////////////////////////////////////// - chrome.storage.local.get('missionPlannerLastValues', function (result) { - if (result.missionPlannerLastValues && result.missionPlannerLastValues.center) { - map.getView().setCenter(ol.proj.fromLonLat(result.missionPlannerLastValues.center)); - map.getView().setZoom(result.missionPlannerLastValues.zoom); - } - }); + var missionPlannerLastValues = store.get('missionPlannerLastValues', false); + if (missionPlannerLastValues && missionPlannerLastValues.zoom && missionPlannerLastValues.center) { + map.getView().setCenter(ol.proj.fromLonLat(missionPlannerLastValues.center)); + map.getView().setZoom(missionPlannerLastValues.zoom); + } + ////////////////////////////////////////////////////////////////////////// // Map on-click behavior definition @@ -1789,7 +2006,7 @@ TABS.mission_control.initialize = function (callback) { clearEditForm(); } catch (e) { console.log(e); - GUI.log(chrome.i18n.getMessage('notAWAYPOINT')); + GUI.log(i18n.getMessage('notAWAYPOINT')); } } selectedFeature = map.forEachFeatureAtPixel(evt.pixel, @@ -1803,6 +2020,14 @@ TABS.mission_control.initialize = function (callback) { if (selectedFeature && tempMarker.kind == "waypoint") { $("#editMission").hide(); selectedMarker = mission.getWaypoint(tempMarker.number); + + selectedFwApproachWp = FC.FW_APPROACH.get()[FC.SAFEHOMES.getMaxSafehomeCount() + selectedMarker.getMultiMissionIdx()]; + + if (selectedFwApproachWp.getLandHeading1() == 0 && selectedFwApproachWp.getLandHeading1() == 0 && selectedFwApproachWp.getApproachAltAsl() == 0 && selectedFwApproachWp.getLandAltAsl() == 0) { + selectedFwApproachWp.setApproachAltAsl(settings.fwApproachAlt * 100); + selectedFwApproachWp.setLandAltAsl(settings.fwLandAlt * 100); + } + var geometry = selectedFeature.getGeometry(); var coord = ol.proj.toLonLat(geometry.getCoordinates()); @@ -1818,12 +2043,34 @@ TABS.mission_control.initialize = function (callback) { var altitudeMeters = app.ConvertCentimetersToMeters(selectedMarker.getAlt()); + if (selectedMarker.getAction() == MWNP.WPTYPE.LAND) { + $('#wpFwLanding').fadeIn(300); + } else { + $('#wpFwLanding').fadeOut(300); + } + if (tempSelectedMarkerIndex == null || tempSelectedMarkerIndex != selectedMarker.getLayerNumber()) { (async () => { const elevationAtWP = await selectedMarker.getElevation(globalSettings); $('#elevationValueAtWP').text(elevationAtWP); const returnAltitude = checkAltElevSanity(false, selectedMarker.getAlt(), elevationAtWP, P3Value); selectedMarker.setAlt(returnAltitude); + + /* + if (TABS.mission_control.isBitSet(P3Value, MWNP.P3.ALT_TYPE)) { + if (!selectedFwApproachWp.getIsSeaLevelRef()) { + selectedFwApproachWp.setApproachDirection(selectedFwApproachWp.getApproachDirection() + elevationAtWP * 100); + selectedFwApproachWp.setLandAltAsl(selectedFwApproachWp.getLandAltAsl() + elevationAtWP * 100); + } + + } + */ + selectedFwApproachWp.setIsSeaLevelRef(TABS.mission_control.isBitSet(P3Value, MWNP.P3.ALT_TYPE) ? 1 : 0); + $('#wpApproachAlt').val(selectedFwApproachWp.getApproachAltAsl()); + $('#wpLandAlt').val(selectedFwApproachWp.getLandAltAsl); + $('#wpLandAltM').text(selectedFwApproachWp.getLandAltAsl() / 100 + " m"); + $('#wpApproachAltM').text(selectedFwApproachWp.getApproachAltAsl() / 100 + " m"); + plotElevation(); })() } @@ -1840,6 +2087,22 @@ TABS.mission_control.initialize = function (callback) { $('#pointP2').val(selectedMarker.getP2()); + + + $('#wpApproachDirection').val(selectedFwApproachWp.getApproachDirection()); + $('#wpLandHeading1').val(Math.abs(selectedFwApproachWp.getLandHeading1())); + changeSwitchery($('#wpLandHeading1Excl'), selectedFwApproachWp.getLandHeading1() < 0); + $('#wpLandHeading2').val(Math.abs(selectedFwApproachWp.getLandHeading2())); + changeSwitchery($('#wpLandHeading2Excl'), selectedFwApproachWp.getLandHeading2() < 0); + + + + $('#wpApproachDirection').val(selectedFwApproachWp.getApproachDirection()); + $('#wpLandHeading1').val(Math.abs(selectedFwApproachWp.getLandHeading1())); + changeSwitchery($('#wpLandHeading1Excl'), selectedFwApproachWp.getLandHeading1() < 0); + $('#wpLandHeading2').val(Math.abs(selectedFwApproachWp.getLandHeading2())); + changeSwitchery($('#wpLandHeading2Excl'), selectedFwApproachWp.getLandHeading2() < 0); + // Selection box update depending on choice of type of waypoint for (var j in dictOfLabelParameterPoint[selectedMarker.getAction()]) { if (dictOfLabelParameterPoint[selectedMarker.getAction()][j] != '') { @@ -1856,7 +2119,9 @@ TABS.mission_control.initialize = function (callback) { } else if (selectedFeature && tempMarker.kind == "line" && tempMarker.selection && !disableMarkerEdit) { let tempWpCoord = ol.proj.toLonLat(evt.coordinate); - let tempWp = new Waypoint(tempMarker.number, MWNP.WPTYPE.WAYPOINT, Math.round(tempWpCoord[1] * 10000000), Math.round(tempWpCoord[0] * 10000000), alt=Number(settings.alt), p1=Number(settings.speed)); + let tempWp = new Waypoint(tempMarker.number, MWNP.WPTYPE.WAYPOINT, Math.round(tempWpCoord[1] * 10000000), Math.round(tempWpCoord[0] * 10000000), Number(settings.alt), Number(settings.speed)); + tempWp.setMultiMissionIdx(mission.getWaypoint(0).getMultiMissionIdx()); + if (homeMarkers.length && HOME.getAlt() != "N/A") { (async () => { const elevationAtWP = await tempWp.getElevation(globalSettings); @@ -1875,12 +2140,8 @@ TABS.mission_control.initialize = function (callback) { } } else if (selectedFeature && tempMarker.kind == "safehome" && tempMarker.selection) { - selectedMarker = SAFEHOMES.getSafehome(tempMarker.number); - var geometry = selectedFeature.getGeometry(); - var coord = ol.proj.toLonLat(geometry.getCoordinates()); - $safehomesTableBody.find('tr:nth-child('+String(tempMarker.number+1)+') > td > .safehome-enabled-value').val(selectedMarker.isUsed()); - $safehomesTableBody.find('tr:nth-child('+String(tempMarker.number+1)+') > td > .safehome-lon').val(Math.round(coord[0] * 10000000) / 10000000); - $safehomesTableBody.find('tr:nth-child('+String(tempMarker.number+1)+') > td > .safehome-lat').val(Math.round(coord[1] * 10000000) / 10000000); + updateSelectedShAndFwAp(tempMarker.number); + //renderSafeHomeOptions(); } else if (selectedFeature && tempMarker.kind == "home" && tempMarker.selection) { selectedMarker = HOME; @@ -1891,7 +2152,15 @@ TABS.mission_control.initialize = function (callback) { } else if (!disableMarkerEdit) { let tempWpCoord = ol.proj.toLonLat(evt.coordinate); - let tempWp = new Waypoint(mission.get().length, MWNP.WPTYPE.WAYPOINT, Math.round(tempWpCoord[1] * 10000000), Math.round(tempWpCoord[0] * 10000000), alt=Number(settings.alt), p1=Number(settings.speed)); + let tempWp = new Waypoint(mission.get().length, MWNP.WPTYPE.WAYPOINT, Math.round(tempWpCoord[1] * 10000000), Math.round(tempWpCoord[0] * 10000000), Number(settings.alt), Number(settings.speed)); + + if (mission.get().length == 0) { + tempWp.setMultiMissionIdx(multimissionCount == 0 ? 0 : multimissionCount - 1); + FC.FW_APPROACH.clean(FC.SAFEHOMES.getMaxSafehomeCount() + tempWp.getMultiMissionIdx()); + } else { + tempWp.setMultiMissionIdx(mission.getWaypoint(mission.get().length - 1).getMultiMissionIdx()); + } + if (homeMarkers.length && HOME.getAlt() != "N/A") { (async () => { const elevationAtWP = await tempWp.getElevation(globalSettings); @@ -1918,10 +2187,14 @@ TABS.mission_control.initialize = function (callback) { ////////////////////////////////////////////////////////////////////////// $(map.getViewport()).on('mousemove', function (e) { var pixel = map.getEventPixel(e.originalEvent); + var name = ""; var hit = map.forEachFeatureAtPixel(pixel, function (feature, layer) { + if (feature) { + name = feature.getProperties().name; + } return true; }); - if (hit) { + if (hit && name != "safehomeDist" && name != "safehomeSafe") { map.getTarget().style.cursor = 'pointer'; } else { map.getTarget().style.cursor = ''; @@ -1940,8 +2213,8 @@ TABS.mission_control.initialize = function (callback) { ////////////////////////////////////////////////////////////////////////// // Update Alt display in meters on ALT field keypress up ////////////////////////////////////////////////////////////////////////// - $('#pointAlt').keyup(function(){ - let altitudeMeters = app.ConvertCentimetersToMeters($(this).val()); + $('#pointAlt').on('keyup', () => { + let altitudeMeters = app.ConvertCentimetersToMeters($('#pointAlt').val()); $('#altitudeInMeters').text(` ${altitudeMeters}m`); }); @@ -2029,8 +2302,26 @@ TABS.mission_control.initialize = function (callback) { ///////////////////////////////////////////// // Callback for Waypoint edition ///////////////////////////////////////////// - $('#pointType').change(function () { + $('#pointType').on('change', (event) => { if (selectedMarker) { + if (Number($('#pointType').val()) == MWNP.WPTYPE.LAND) { + let found = false; + mission.get().forEach(wp => { + if (wp.getAction() == MWNP.WPTYPE.LAND) { + GUI.alert(i18n.getMessage('MissionPlannerOnlyOneLandWp')); + found = true; + $(event.currentTarget).val(selectedMarker.getAction()); + } + }); + + if (!found) { + $('#wpFwLanding').fadeIn(300); + } + + } else { + $('#wpFwLanding').fadeOut(300); + } + selectedMarker.setAction(Number($('#pointType').val())); if ([MWNP.WPTYPE.SET_POI,MWNP.WPTYPE.POSHOLD_TIME,MWNP.WPTYPE.LAND].includes(selectedMarker.getAction())) { selectedMarker.setP1(0.0); @@ -2105,7 +2396,7 @@ TABS.mission_control.initialize = function (callback) { $('#pointP3Alt').on('change', function (event) { if (selectedMarker) { - P3Value = selectedMarker.getP3(); + var P3Value = selectedMarker.getP3(); if (disableMarkerEdit) { changeSwitchery($('#pointP3Alt'), TABS.mission_control.isBitSet(P3Value, MWNP.P3.ALT_TYPE)); @@ -2134,19 +2425,61 @@ TABS.mission_control.initialize = function (callback) { } selectedMarker.setAlt(groundClearance + 100 * (elevationAtWP - elevationAtHome)); } + + if (selectedMarker.getAction() == MWNP.WPTYPE.LAND && selectedFwApproachWp && selectedFwApproachWp.getIsSeaLevelRef() != $('#pointP3Alt').prop("checked")) { + + let oldElevation = 0; + if (selectedFwApproachWp.getIsSeaLevelRef()) { + oldElevation = selectedFwApproachWp.getElevation(); + } + + if ($('#pointP3Alt').prop("checked")) { + selectedFwApproachWp.setApproachAltAsl(selectedFwApproachWp.getApproachAltAsl() - oldElevation + elevationAtWP * 100); + selectedFwApproachWp.setLandAltAsl(selectedFwApproachWp.getLandAltAsl() - oldElevation + elevationAtWP * 100); + } else { + selectedFwApproachWp.setApproachAltAsl(selectedFwApproachWp.getApproachAltAsl() - elevationAtWP * 100); + selectedFwApproachWp.setLandAltAsl(selectedFwApproachWp.getLandAltAsl() - elevationAtWP * 100); + } + selectedFwApproachWp.setElevation(elevationAtWP * 100); + selectedFwApproachWp.setIsSeaLevelRef($('#pointP3Alt').prop("checked") ? 1 : 0); + $('#wpApproachAlt').val(selectedFwApproachWp.getApproachAltAsl()); + $('#wpLandAlt').val(selectedFwApproachWp.getLandAltAsl()); + } + } const returnAltitude = checkAltElevSanity(false, selectedMarker.getAlt(), elevationAtWP, selectedMarker.getP3()); selectedMarker.setAlt(returnAltitude); $('#pointAlt').val(selectedMarker.getAlt()); - altitudeMeters = app.ConvertCentimetersToMeters(selectedMarker.getAlt()); + let altitudeMeters = app.ConvertCentimetersToMeters(selectedMarker.getAlt()); $('#altitudeInMeters').text(` ${altitudeMeters}m`); + $('#wpLandAltM').text(selectedFwApproachWp.getLandAltAsl() / 100 + " m"); + $('#wpApproachAltM').text(selectedFwApproachWp.getApproachAltAsl() / 100 + " m"); + + if (selectedFwApproachWp && selectedFwApproachWp.getIsSeaLevelRef() != $('#pointP3Alt').prop("checked")) { + selectedFwApproachWp.setIsSeaLevelRef($('#pointP3Alt').prop("checked")); + selectedFwApproachWp.setElevation(elevationAtWP * 100); + if ($('#pointP3Alt').prop("checked")) { + selectedFwApproachWp.setApproachAltAsl(selectedFwApproachWp.getApproachAltAsl() + elevationAtWP * 100); + selectedFwApproachWp.setLandAltAsl(selectedFwApproachWp.getLandAltAsl() + elevationAtWP * 100); + } else { + selectedFwApproachWp.setApproachAltAsl(selectedFwApproachWp.getApproachAltAsl() - elevationAtWP * 100); + selectedFwApproachWp.setLandAltAsl(selectedFwApproachWp.getLandAltAsl() - elevationAtWP * 100); + } + + $('#wpApproachAlt').val(selectedFwApproachWp.getApproachAltAsl()); + $('#wpLandAlt').val(selectedFwApproachWp.getLandAltAsl()); + } + + $('#wpLandAltM').text(selectedFwApproachWp.getLandAltAsl() / 100 + " m"); + $('#wpApproachAltM').text(selectedFwApproachWp.getApproachAltAsl() / 100 + " m"); + mission.updateWaypoint(selectedMarker); mission.update(singleMissionActive()); redrawLayer(); plotElevation(); - })() + })(); } }); @@ -2156,7 +2489,7 @@ TABS.mission_control.initialize = function (callback) { changeSwitchery($('#pointP3UserAction1'), TABS.mission_control.isBitSet(selectedMarker.getP3(), MWNP.P3.USER_ACTION_1)); } - P3Value = TABS.mission_control.setBit(selectedMarker.getP3(), MWNP.P3.USER_ACTION_1, $('#pointP3UserAction1').prop("checked")); + var P3Value = TABS.mission_control.setBit(selectedMarker.getP3(), MWNP.P3.USER_ACTION_1, $('#pointP3UserAction1').prop("checked")); selectedMarker.setP3(P3Value); mission.updateWaypoint(selectedMarker); @@ -2171,7 +2504,7 @@ TABS.mission_control.initialize = function (callback) { changeSwitchery($('#pointP3UserAction2'), TABS.mission_control.isBitSet(selectedMarker.getP3(), MWNP.P3.USER_ACTION_2)); } - P3Value = TABS.mission_control.setBit(selectedMarker.getP3(), MWNP.P3.USER_ACTION_2, $('#pointP3UserAction2').prop("checked")); + var P3Value = TABS.mission_control.setBit(selectedMarker.getP3(), MWNP.P3.USER_ACTION_2, $('#pointP3UserAction2').prop("checked")); selectedMarker.setP3(P3Value); mission.updateWaypoint(selectedMarker); @@ -2186,7 +2519,7 @@ TABS.mission_control.initialize = function (callback) { changeSwitchery($('#pointP3UserAction3'), TABS.mission_control.isBitSet(selectedMarker.getP3(), MWNP.P3.USER_ACTION_3)); } - P3Value = TABS.mission_control.setBit(selectedMarker.getP3(), MWNP.P3.USER_ACTION_3, $('#pointP3UserAction3').prop("checked")); + var P3Value = TABS.mission_control.setBit(selectedMarker.getP3(), MWNP.P3.USER_ACTION_3, $('#pointP3UserAction3').prop("checked")); selectedMarker.setP3(P3Value); mission.updateWaypoint(selectedMarker); @@ -2201,7 +2534,7 @@ TABS.mission_control.initialize = function (callback) { changeSwitchery($('#pointP3UserAction4'), TABS.mission_control.isBitSet(selectedMarker.getP3(), MWNP.P3.USER_ACTION_4)); } - P3Value = TABS.mission_control.setBit(selectedMarker.getP3(), MWNP.P3.USER_ACTION_4, $('#pointP3UserAction4').prop("checked")); + var P3Value = TABS.mission_control.setBit(selectedMarker.getP3(), MWNP.P3.USER_ACTION_4, $('#pointP3UserAction4').prop("checked")); selectedMarker.setP3(P3Value); mission.updateWaypoint(selectedMarker); @@ -2210,6 +2543,109 @@ TABS.mission_control.initialize = function (callback) { } }); + $('#wpApproachAlt').on('change', (event) => { + if (selectedMarker && selectedFwApproachWp) { + let altitude = Number($(event.currentTarget).val()); + if (checkApproachAltitude(altitude, $('#pointP3Alt').prop('checked'), Number($('#elevationValueAtWP').text()))) { + selectedFwApproachWp.setApproachAltAsl(Number($(event.currentTarget).val())); + $('#wpApproachAltM').text(selectedFwApproachWp.getApproachAltAsl() / 100 + " m"); + } + } + }); + + $('#wpLandAlt').on('change', (event) => { + if (selectedMarker && selectedFwApproachWp) { + let altitude = Number($(event.currentTarget).val()); + if (checkLandingAltitude(altitude, $('#pointP3Alt').prop('checked'), Number($('#elevationValueAtWP').text()))) { + selectedFwApproachWp.setLandAltAsl(Number($(event.currentTarget).val())); + $('#wpLandAltM').text(selectedFwApproachWp.getLandAltAsl() / 100 + " m"); + } + } + }); + + $('#wpApproachDirection').on('change', (event) => { + if (selectedMarker && selectedFwApproachWp) { + selectedFwApproachWp.setApproachDirection($(event.currentTarget).val()); + refreshLayers(); + } + }); + + $('#wpLandHeading1').on('change', (event) => { + if (selectedMarker && selectedFwApproachWp) { + let val = Number($(event.currentTarget).val()); + if (val < 0) { + val = 360; + $('#wpLandHeading1').val(360); + } + if (val > 360) { + val = 0; + $('#wpLandHeading1').val(0); + } + + if ($('#wpLandHeading1Excl').prop('checked')) { + val *= -1; + } + + selectedFwApproachWp.setLandHeading1(val); + refreshLayers(); + } + }); + + $('#wpLandHeading1Excl').on('change', (event) => { + + if (selectedMarker && selectedFwApproachWp) { + if (disableMarkerEdit) { + changeSwitchery($('#wpLandHeading1Excl'), selectedFwApproachWp.getLandHeading1() < 0); + return; + } + + if ($('#wpLandHeading1Excl').prop('checked')) { + selectedFwApproachWp.setLandHeading1(-Math.abs(selectedFwApproachWp.getLandHeading1())); + } else { + selectedFwApproachWp.setLandHeading1(Math.abs(selectedFwApproachWp.getLandHeading1())); + } + + refreshLayers(); + } + }); + + $('#wpLandHeading2').on('change', (event) => { + if (selectedMarker && selectedFwApproachWp) { + let val = Number($(event.currentTarget).val()); + if (val < 0) { + val = 360; + $('#wpLandHeading2').val(360); + } + if (val > 360) { + val = 0; + $('#wpLandHeading2').val(0); + } + + if ($('#wpLandHeading2Excl').prop('checked')) { + val *= -1; + } + + selectedFwApproachWp.setLandHeading2(val); + refreshLayers(); + } + }); + + $('#wpLandHeading2Excl').on('change', (event) => { + if (selectedMarker && selectedFwApproachWp) { + if (disableMarkerEdit) { + changeSwitchery($('#wpLandHeading2Excl'), selectedFwApproachWp.getLandHeading2() < 0); + return; + } + if ($('#wpLandHeading2Excl').prop('checked')) { + selectedFwApproachWp.setLandHeading2(-Math.abs(selectedFwApproachWp.getLandHeading2())); + } else { + selectedFwApproachWp.setLandHeading2(Math.abs(selectedFwApproachWp.getLandHeading2())); + } + refreshLayers(); + } + }); + + ///////////////////////////////////////////// // Callback for Waypoint Options Table ///////////////////////////////////////////// @@ -2224,7 +2660,7 @@ TABS.mission_control.initialize = function (callback) { } }); - $("[data-role='waypointOptions-add']").click(function () { + $("[data-role='waypointOptions-add']").on('click', function () { if (selectedMarker) { mission.addAttachedFromWaypoint(selectedMarker); renderWaypointOptionsTable(selectedMarker); @@ -2240,17 +2676,25 @@ TABS.mission_control.initialize = function (callback) { }); ///////////////////////////////////////////// - // Callback for SAFEHOMES Table + // Callback for SAFEHOMES ///////////////////////////////////////////// - $safehomesTableBody.on('click', "[data-role='safehome-center']", function (event) { + + + $('#addSafehome').on('click', () => { + if (FC.SAFEHOMES.safehomeCount() + 1 > FC.SAFEHOMES.getMaxSafehomeCount()){ + GUI.alert(i18n.getMessage('missionSafehomeMaxSafehomesReached')); + return; + } + let mapCenter = map.getView().getCenter(); - let tmpSH = SAFEHOMES.getSafehome($(event.currentTarget).attr("data-index")); - tmpSH.setLon(Math.round(ol.proj.toLonLat(mapCenter)[0] * 1e7)); - tmpSH.setLat(Math.round(ol.proj.toLonLat(mapCenter)[1] * 1e7)); - SAFEHOMES.updateSafehome(tmpSH); - renderSafehomesTable(); + let midLon = Math.round(ol.proj.toLonLat(mapCenter)[0] * 1e7); + let midLat = Math.round(ol.proj.toLonLat(mapCenter)[1] * 1e7); + FC.SAFEHOMES.put(new Safehome(FC.SAFEHOMES.safehomeCount(), 1, midLat, midLon)); + updateSelectedShAndFwAp(FC.SAFEHOMES.safehomeCount() - 1); + renderSafeHomeOptions(); cleanSafehomeLayers(); renderSafehomesOnMap(); + updateSafehomeInfo(); }); $('#cancelSafehome').on('click', function () { @@ -2259,27 +2703,214 @@ TABS.mission_control.initialize = function (callback) { $('#loadEepromSafehomeButton').on('click', function () { $(this).addClass('disabled'); - GUI.log(chrome.i18n.getMessage('startGettingSafehomePoints')); - mspHelper.loadSafehomes(); - setTimeout(function(){ - renderSafehomesTable(); + GUI.log('Start of getting Safehome points'); + var loadChainer = new MSPChainerClass(); + loadChainer.setChain([ + mspHelper.loadSafehomes, + mspHelper.loadFwApproach, + function() { + if (FC.SAFEHOMES.safehomeCount() >= 1) { + updateSelectedShAndFwAp(0); + } else { + selectedSafehome = null; + selectedFwApproachSh = null; + } + renderSafeHomeOptions(); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + updateSafehomeInfo(); + GUI.log(i18n.getMessage('endGettingSafehomePoints')); + $('#loadEepromSafehomeButton').removeClass('disabled'); + } + ]); + loadChainer.execute(); + }); + + $('#saveEepromSafehomeButton').on('click', function() { + $(this).addClass('disabled'); + GUI.log(i18n.getMessage('startSendingSafehomePoints')); + + var saveChainer = new MSPChainerClass(); + saveChainer.setChain([ + mspHelper.saveSafehomes, + mspHelper.saveFwApproach, + function() { + mspHelper.saveToEeprom(); + GUI.log(i18n.getMessage('endSendingSafehomePoints')); + $('#saveEepromSafehomeButton').removeClass('disabled'); + } + ]); + saveChainer.execute(); + }); + + $('#deleteSafehome').on('click', () => { + if (selectedSafehome && selectedFwApproachSh) { + var shNum = selectedSafehome.getNumber(); + FC.SAFEHOMES.drop(shNum); + FC.FW_APPROACH.clean(shNum); + + if (FC.SAFEHOMES.safehomeCount() > 0) { + updateSelectedShAndFwAp(FC.SAFEHOMES.safehomeCount() - 1); + } else { + selectedSafehome = null; + selectedFwApproachSh = null; + } + renderSafeHomeOptions(); cleanSafehomeLayers(); renderSafehomesOnMap(); - GUI.log(chrome.i18n.getMessage('endGettingSafehomePoints')); - $('#loadEepromSafehomeButton').removeClass('disabled'); - }, 500); + updateSafehomeInfo(); + } + }); + $('#safehomeLatitude').on('change', event => { + if (selectedFwApproachSh) { + selectedFwApproachSh.setLat(Math.round(Number($(event.currentTarget).val()) * 1e7)); + renderSafeHomeOptions(); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + } }); - $('#saveEepromSafehomeButton').on('click', function() { - $(this).addClass('disabled'); - GUI.log(chrome.i18n.getMessage('startSendingSafehomePoints')); - mspHelper.saveSafehomes(); - setTimeout(function(){ - mspHelper.saveToEeprom(); - GUI.log(chrome.i18n.getMessage('endSendingSafehomePoints')); - $('#saveEepromSafehomeButton').removeClass('disabled'); - }, 500); + + $('#safehomeLongitude').on('change', event => { + if (selectedFwApproachSh) { + selectedFwApproachSh.setLon(Math.round(Number($(event.currentTarget).val()) * 1e7)); + renderSafeHomeOptions(); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + } + }); + + $('#safehomeSeaLevelRef').on('change', event => { + + let isChecked = $(event.currentTarget).prop('checked') ? 1 : 0; + if (selectedSafehome && selectedFwApproachSh && isChecked != selectedFwApproachSh.getIsSeaLevelRef()) { + selectedFwApproachSh.setIsSeaLevelRef(isChecked); + + (async () => { + const elevation = await selectedFwApproachSh.getElevationFromServer(selectedSafehome.getLonMap(), selectedSafehome.getLatMap(), globalSettings) * 100; + selectedFwApproachSh.setElevation(elevation); + + if (isChecked) { + selectedFwApproachSh.setApproachAltAsl(selectedFwApproachSh.getApproachAltAsl() + elevation); + selectedFwApproachSh.setLandAltAsl(selectedFwApproachSh.getLandAltAsl() + elevation); + } else { + selectedFwApproachSh.setApproachAltAsl(selectedFwApproachSh.getApproachAltAsl() - elevation); + selectedFwApproachSh.setLandAltAsl(selectedFwApproachSh.getLandAltAsl() - elevation); + + } + + $('#safehomeElevation').text(elevation / 100); + $('#safehomeApproachAlt').val(selectedFwApproachSh.getApproachAltAsl()); + $('#safehomeLandAlt').val(selectedFwApproachSh.getLandAltAsl()); + $('#safehomeLandAltM').text(selectedFwApproachSh.getLandAltAsl() / 100 + " m"); + $('#safehomeApproachAltM').text(selectedFwApproachSh.getApproachAltAsl() / 100 + " m"); + + renderSafeHomeOptions(); + })(); + } + }); + + $('#safehomeApproachAlt').on('change', event => { + + if (selectedFwApproachSh) { + let altitude = Number($(event.currentTarget).val()); + if (checkApproachAltitude(altitude, $('#safehomeSeaLevelRef').prop('checked'), Number($('#safehomeElevation').text()))) { + selectedFwApproachSh.setApproachAltAsl(Number($(event.currentTarget).val())); + $('#safehomeApproachAltM').text(selectedFwApproachSh.getApproachAltAsl() / 100 + " m"); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + renderHomeTable(); + } + $('#safehomeApproachAlt').val(selectedFwApproachSh.getApproachAltAsl()); + } + + }); + + $('#safehomeLandAlt').on('change', event => { + + if (selectedFwApproachSh) { + let altitude = Number($(event.currentTarget).val()); + if (checkLandingAltitude(altitude, $('#safehomeSeaLevelRef').prop('checked'), Number($('#safehomeElevation').text()))) { + selectedFwApproachSh.setLandAltAsl(altitude); + $('#safehomeLandAltM').text(selectedFwApproachSh.getLandAltAsl() / 100 + " m"); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + renderHomeTable(); + } else { + $('#safehomeLandAlt').val(selectedFwApproachSh.getLandAltAsl()); + } + } + }); + + $('#geozoneApproachDirection').on('change', event => { + if (selectedFwApproachSh) { + selectedFwApproachSh.setApproachDirection($(event.currentTarget).val()); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + } + }); + + $('#safehomeLandHeading1Excl').on('change', event => { + if (selectedFwApproachSh && !lockShExclHeading) { + selectedFwApproachSh.setLandHeading1(selectedFwApproachSh.getLandHeading1() * -1); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + } + }); + + $('#safehomeLandHeading1').on('change', event => { + if (selectedFwApproachSh) { + let val = Number($(event.currentTarget).val()); + if (val < 0) { + val = 360; + $('#safehomeLandHeading1').val(360); + } + if (val > 360) { + val = 0; + $('#safehomeLandHeading1').val(0); + } + + if ($('#safehomeLandHeading1Excl').prop('checked')) { + val *= -1; + } + + selectedFwApproachSh.setLandHeading1(val); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + } + }); + + + $('#safehomeLandHeading2Excl').on('change', event => { + if (selectedFwApproachSh && !lockShExclHeading) { + selectedFwApproachSh.setLandHeading2(selectedFwApproachSh.getLandHeading2() * -1); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + } + }); + + + $('#safehomeLandHeading2').on('change', event => { + if (selectedFwApproachSh) { + let val = Number($(event.currentTarget).val()); + if (val < 0) { + val = 360; + $('#safehomeLandHeading2').val(360); + } + if (val > 360) { + val = 0; + $('#safehomeLandHeading2').val(0); + } + + if ($('#safehomeLandHeading2Excl').prop('checked')) { + val *= -1; + } + + selectedFwApproachSh.setLandHeading2(val); + cleanSafehomeLayers(); + renderSafehomesOnMap(); + } }); ///////////////////////////////////////////// @@ -2353,7 +2984,7 @@ TABS.mission_control.initialize = function (callback) { }); $('#updateMultimissionButton').on('click', function () { - $('#multimissionOptionList').val('0').change(); + $('#multimissionOptionList').val('0').trigger('change'); }); $('#cancelMultimission').on('click', function () { @@ -2368,12 +2999,14 @@ TABS.mission_control.initialize = function (callback) { // Callback for Remove buttons ///////////////////////////////////////////// $('#removeAllPoints').on('click', function () { - if (markers.length && confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) { + if (markers.length && confirm(i18n.getMessage('confirm_delete_all_points'))) { if (removeAllMultiMissionCheck()) { removeAllWaypoints(); updateMultimissionState(); } - + for (let i = FC.SAFEHOMES.getMaxSafehomeCount(); i < FC.FW_APPROACH.getMaxFwApproachCount(); i++) { + FC.FW_APPROACH.clean(i); + } plotElevation(); } }); @@ -2381,11 +3014,16 @@ TABS.mission_control.initialize = function (callback) { $('#removePoint').on('click', function () { if (selectedMarker) { if (mission.isJumpTargetAttached(selectedMarker)) { - alert(chrome.i18n.getMessage('MissionPlannerJumpTargetRemoval')); + GUI.alert(i18n.getMessage('MissionPlannerJumpTargetRemoval')); } else if (mission.getAttachedFromWaypoint(selectedMarker) && mission.getAttachedFromWaypoint(selectedMarker).length != 0) { - if (confirm(chrome.i18n.getMessage('confirm_delete_point_with_options'))) { + if (confirm(i18n.getMessage('confirm_delete_point_with_options'))) { mission.getAttachedFromWaypoint(selectedMarker).forEach(function (element) { + + if (element.getAction() == MWNP.WPTYPE.LAND) { + FC.FW_APPROACH.clean(element.getNumber()); + } + mission.dropWaypoint(element); mission.update(singleMissionActive()); }); @@ -2399,6 +3037,9 @@ TABS.mission_control.initialize = function (callback) { } else { mission.dropWaypoint(selectedMarker); + if (selectedMarker.getAction() == MWNP.WPTYPE.LAND) { + FC.FW_APPROACH.clean(selectedFwApproachWp.getNumber()); + } selectedMarker = null; mission.update(singleMissionActive()); clearEditForm(); @@ -2415,55 +3056,68 @@ TABS.mission_control.initialize = function (callback) { $('#loadFileMissionButton').on('click', function () { if (!fileLoadMultiMissionCheck()) return; - if (markers.length && !confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) return; - nwdialog.setContext(document); - nwdialog.openFileDialog('.mission', function(result) { - loadMissionFile(result); + if (markers.length && !confirm(i18n.getMessage('confirm_delete_all_points'))) return; + var options = { + filters: [ { name: "Mission file", extensions: ['mission'] } ] + }; + dialog.showOpenDialog(options).then(result => { + if (result.canceled) { + console.log('No file selected'); + return; + } + if (result.filePaths.length == 1) { + loadMissionFile(result.filePaths[0]); + } }) }); $('#saveFileMissionButton').on('click', function () { - nwdialog.setContext(document); - nwdialog.saveFileDialog('', '.mission', function(result) { - saveMissionFile(result); - }) + var options = { + filters: [ { name: "Mission file", extensions: ['mission'] } ] + }; + dialog.showSaveDialog(options).then(result => { + if (result.canceled) { + return; + } + saveMissionFile(result.filePath); + }); }); $('#loadMissionButton').on('click', function () { let message = multimissionCount ? 'confirm_overwrite_multimission_file_load_option' : 'confirm_delete_all_points'; - if ((markers.length || multimissionCount) && !confirm(chrome.i18n.getMessage(message))) return; + if ((markers.length || multimissionCount) && !confirm(i18n.getMessage(message))) return; removeAllWaypoints(); $(this).addClass('disabled'); - GUI.log(chrome.i18n.getMessage('startGetPoint')); + GUI.log(i18n.getMessage('startGetPoint')); getWaypointsFromFC(false); }); $('#saveMissionButton').on('click', function () { if (mission.isEmpty()) { - alert(chrome.i18n.getMessage('no_waypoints_to_save')); + GUI.alert(i18n.getMessage('no_waypoints_to_save')); return; } $(this).addClass('disabled'); - GUI.log(chrome.i18n.getMessage('startSendPoint')); + GUI.log(i18n.getMessage('startSendPoint')); sendWaypointsToFC(false); }); $('#loadEepromMissionButton').on('click', function () { let message = multimissionCount ? 'confirm_overwrite_multimission_file_load_option' : 'confirm_delete_all_points'; - if ((markers.length || multimissionCount) && !confirm(chrome.i18n.getMessage(message))) return; + if ((markers.length || multimissionCount) && !confirm(i18n.getMessage(message))) return; removeAllWaypoints(); $(this).addClass('disabled'); - GUI.log(chrome.i18n.getMessage('startGetPoint')); + GUI.log(i18n.getMessage('startGetPoint')); getWaypointsFromFC(true); }); $('#saveEepromMissionButton').on('click', function () { if (mission.isEmpty()) { - alert(chrome.i18n.getMessage('no_waypoints_to_save')); + GUI.alert(i18n.getMessage('no_waypoints_to_save')); return; } $(this).addClass('disabled'); - GUI.log(chrome.i18n.getMessage('startSendPoint')); + GUI.log(i18n.getMessage('startSendPoint')); sendWaypointsToFC(true); }); @@ -2473,9 +3127,12 @@ TABS.mission_control.initialize = function (callback) { $('#saveSettings').on('click', function () { let oldSafeRadiusSH = settings.safeRadiusSH; - settings.speed = Number($('#MPdefaultPointSpeed').val()); + // update only default settings settings.alt = Number($('#MPdefaultPointAlt').val()); + settings.speed = Number($('#MPdefaultPointSpeed').val()); settings.safeRadiusSH = Number($('#MPdefaultSafeRangeSH').val()); + settings.fwApproachAlt = Number($('#MPdefaultFwApproachAlt').val()); + settings.fwLandAlt = Number($('#MPdefaultLandAlt').val()); saveSettings(); @@ -2502,18 +3159,19 @@ TABS.mission_control.initialize = function (callback) { // ///////////////////////////////////////////// function loadMissionFile(filename) { - const fs = require('fs'); - if (!window.xml2js) return GUI.log(chrome.i18n.getMessage('errorReadingFileXml2jsNotFound')); + for (let i = FC.SAFEHOMES.getMaxSafehomeCount(); i < FC.FW_APPROACH.getMaxFwApproachCount(); i++) { + FC.FW_APPROACH.clean(i); + } fs.readFile(filename, (err, data) => { if (err) { - GUI.log(chrome.i18n.getMessage('errorReadingFile')); + GUI.log(i18n.getMessage('errorReadingFile')); return console.error(err); } - window.xml2js.Parser({ 'explicitChildren': true, 'preserveChildrenOrder': true }).parseString(data, (err, result) => { + xml2js.Parser({ 'explicitChildren': true, 'preserveChildrenOrder': true }).parseString(data, (err, result) => { if (err) { - GUI.log(chrome.i18n.getMessage('errorParsingFile')); + GUI.log(i18n.getMessage('errorParsingFile')); return console.error(err); } @@ -2525,6 +3183,7 @@ TABS.mission_control.initialize = function (callback) { for (var noderoot in result) { if (!nodemission && noderoot.match(/mission/i)) { nodemission = result[noderoot]; + var missionIdx = -1; if (nodemission.$$ && nodemission.$$.length) { for (var i = 0; i < nodemission.$$.length; i++) { node = nodemission.$$[i]; @@ -2536,7 +3195,9 @@ TABS.mission_control.initialize = function (callback) { } } else if (node['#name'].match(/meta/i) || node['#name'].match(/mwp/i) && node.$) { for (var attr in node.$) { - if (attr.match(/zoom/i)) { + if (attr.match(/mission/i)) { + missionIdx = parseInt(node.$[attr]) -1; + } else if (attr.match(/zoom/i)) { mission.setCenterZoom(parseInt(node.$[attr])); } else if (attr.match(/cx/i)) { mission.setCenterLon(parseFloat(node.$[attr]) * 10000000); @@ -2593,7 +3254,33 @@ TABS.mission_control.initialize = function (callback) { } } } + if (missionIdx >= 0) { + point.setMultiMissionIdx(missionIdx); + } mission.put(point); + } else if (node['#name'].match(/fwapproach/i) && node.$) { + var fwApproach = new FwApproach(0); + var idx = -1; + for (var attr in node.$) { + if (attr.match(/index/i)) { + idx = parseInt(node.$[attr]); + } else if (attr.match(/no/i)) { + fwApproach.setNumber(parseInt(node.$[attr])); + } else if (attr.match(/approach-alt/i)) { + fwApproach.setApproachAltAsl(parseInt(node.$[attr])); + } else if (attr.match(/land-alt/i)) { + fwApproach.setLandAltAsl(parseInt(node.$[attr])); + } else if (attr.match(/approach-direction/i)) { + fwApproach.setApproachDirection(node.$[attr] == 'left' ? 0 : 1); + } else if (attr.match(/landheading1/i)) { + fwApproach.setLandHeading1(parseInt(node.$[attr])); + } else if (attr.match(/landheading2/i)) { + fwApproach.setLandHeading2(parseInt(node.$[attr])); + } else if (attr.match(/sealevel-ref/i)) { + fwApproach.setIsSeaLevelRef(parseBooleans(node.$[attr]) ? 1 : 0); + } + } + FC.FW_APPROACH.insert(fwApproach, FC.SAFEHOMES.getMaxSafehomeCount() + idx); } } } @@ -2601,7 +3288,7 @@ TABS.mission_control.initialize = function (callback) { } if (missionEndFlagCount > 1) { - if (multimissionCount && !confirm(chrome.i18n.getMessage('confirm_multimission_file_load'))) { + if (multimissionCount && !confirm(i18n.getMessage('confirm_multimission_file_load'))) { mission.flush(); return; } else { @@ -2645,16 +3332,13 @@ TABS.mission_control.initialize = function (callback) { } updateTotalInfo(); let sFilename = String(filename.split('\\').pop().split('/').pop()); - GUI.log(sFilename + chrome.i18n.getMessage('loadedSuccessfully')); + GUI.log(sFilename + i18n.getMessage('loadedSuccessfully')); updateFilename(sFilename); }); }); } function saveMissionFile(filename) { - const fs = require('fs'); - if (!window.xml2js) return GUI.log(chrome.i18n.getMessage('errorWritingFileXml2jsNotFound')); - var center = ol.proj.toLonLat(map.getView().getCenter()); var zoom = map.getView().getZoom(); let multimission = multimissionCount && !singleMissionActive(); @@ -2666,7 +3350,8 @@ TABS.mission_control.initialize = function (callback) { 'home-x' : HOME.getLonMap(), 'home-y' : HOME.getLatMap(), 'zoom': zoom } }, - 'missionitem': [] + 'missionitem': [], + 'fwapproach': [] }; let missionStartWPNumber = 0; @@ -2696,100 +3381,129 @@ TABS.mission_control.initialize = function (callback) { missionNumber ++; } }); + let approachIdx = 0; + for (let i = FC.SAFEHOMES.getMaxSafehomeCount(); i < FC.FW_APPROACH.getMaxFwApproachCount(); i++){ + let approach = FC.FW_APPROACH.get()[i]; + if (approach.getLandHeading1() != 0 || approach.getLandHeading2() != 0) { + var item = { $: { + 'index': approachIdx, + 'no': approach.getNumber(), + 'approach-alt': approach.getApproachAltAsl(), + 'land-alt': approach.getLandAltAsl(), + 'approach-direction': approach.getApproachDirection() == 0 ? 'left' : 'right', + 'landheading1': approach.getLandHeading1(), + 'landheading2': approach.getLandHeading2(), + 'sealevel-ref': approach.getIsSeaLevelRef() ? 'true' : 'false' + }}; + data.fwapproach.push(item); + } + approachIdx++; + } - var builder = new window.xml2js.Builder({ 'rootName': 'mission', 'renderOpts': { 'pretty': true, 'indent': '\t', 'newline': '\n' } }); + var builder = new xml2js.Builder({ 'rootName': 'mission', 'renderOpts': { 'pretty': true, 'indent': '\t', 'newline': '\n' } }); var xml = builder.buildObject(data); xml = xml.replace(/missionitem mission/g, 'meta mission'); fs.writeFile(filename, xml, (err) => { if (err) { - GUI.log(chrome.i18n.getMessage('ErrorWritingFile')); + GUI.log(i18n.getMessage('ErrorWritingFile')); return console.error(err); } let sFilename = String(filename.split('\\').pop().split('/').pop()); - GUI.log(sFilename + chrome.i18n.getMessage('savedSuccessfully')); + GUI.log(sFilename + i18n.getMessage('savedSuccessfully')); updateFilename(sFilename); }); } ///////////////////////////////////////////// // Load/Save FC mission Toolbox - // mission = configurator store, WP number indexed from 0, MISSION_PLANNER = FC NVM store, WP number indexed from 1 + // mission = configurator store, WP number indexed from 0, FC.MISSION_PLANNER = FC NVM store, WP number indexed from 1 ///////////////////////////////////////////// function getWaypointsFromFC(loadEeprom) { + + var loadChainer = new MSPChainerClass(); + var chain = [mspHelper.loadFwApproach]; if (loadEeprom) { - MSP.send_message(MSPCodes.MSP_WP_MISSION_LOAD, [0], getWaypointData); - } else { - getWaypointData(); + chain.push(function(callback) { + MSP.send_message(MSPCodes.MSP_WP_MISSION_LOAD, [0], callback); + }); } + chain.push(mspHelper.loadWaypoints); + chain.push(function() { + GUI.log(i18n.getMessage('endGetPoint')); + if (loadEeprom) { + GUI.log(i18n.getMessage('eeprom_load_ok')); + $('#loadEepromMissionButton').removeClass('disabled'); + } else { + $('#loadMissionButton').removeClass('disabled'); + } + if (!FC.MISSION_PLANNER.getCountBusyPoints()) { + GUI.alert(i18n.getMessage('no_waypoints_to_load')); + return; + } + mission.reinit(); + mission.copy(FC.MISSION_PLANNER); + mission.update(false, true); - function getWaypointData() { - mspHelper.loadWaypoints(function() { - GUI.log(chrome.i18n.getMessage('endGetPoint')); - if (loadEeprom) { - GUI.log(chrome.i18n.getMessage('eeprom_load_ok')); - $('#loadEepromMissionButton').removeClass('disabled'); - } else { - $('#loadMissionButton').removeClass('disabled'); - } - if (!MISSION_PLANNER.getCountBusyPoints()) { - alert(chrome.i18n.getMessage('no_waypoints_to_load')); - return; - } - mission.reinit(); - mission.copy(MISSION_PLANNER); - mission.update(false, true); - - /* check multimissions */ - multimissionCount = 0; - mission.get().forEach(function (element) { - if (element.getEndMission() == 0xA5) { - multimissionCount ++; - } - }); - multimissionCount = multimissionCount > 1 ? multimissionCount : 0; - multimission.reinit(); - if (multimissionCount > 1) { - multimission.copy(mission); - $('#missionPlannerMultiMission').fadeIn(300); + /* check multimissions */ + multimissionCount = 0; + mission.get().forEach(function (element) { + if (element.getEndMission() == 0xA5) { + element.setMultiMissionIdx(multimissionCount); + multimissionCount++; } - renderMultimissionTable(); - - setView(16); - redrawLayers(); - updateTotalInfo(); }); - }; + multimissionCount = multimissionCount > 1 ? multimissionCount : 0; + multimission.reinit(); + if (multimissionCount > 1) { + multimission.copy(mission); + $('#missionPlannerMultiMission').fadeIn(300); + } + renderMultimissionTable(); + setView(16); + redrawLayers(); + updateTotalInfo(); + }); + + loadChainer.setChain(chain); + loadChainer.execute(); } function sendWaypointsToFC(saveEeprom) { - MISSION_PLANNER.reinit(); - MISSION_PLANNER.copy(mission); - MISSION_PLANNER.update(false, true, true); - mspHelper.saveWaypoints(function() { - GUI.log(chrome.i18n.getMessage('endSendPoint')); - if (saveEeprom) { - $('#saveEepromMissionButton').removeClass('disabled'); - GUI.log(chrome.i18n.getMessage('eeprom_saved_ok')); - MSP.send_message(MSPCodes.MSP_WP_MISSION_SAVE, [0], false, setMissionIndex); - } else { - $('#saveMissionButton').removeClass('disabled'); + FC.MISSION_PLANNER.reinit(); + FC.MISSION_PLANNER.copy(mission); + FC.MISSION_PLANNER.update(false, true, true); + let saveChainer = new MSPChainerClass(); + saveChainer.setChain([ + mspHelper.saveWaypoints, + mspHelper.saveFwApproach, + function () { + GUI.log(i18n.getMessage('endSendPoint')); + if (saveEeprom) { + $('#saveEepromMissionButton').removeClass('disabled'); + GUI.log(i18n.getMessage('eeprom_saved_ok')); + MSP.send_message(MSPCodes.MSP_WP_MISSION_SAVE, [0], false, setMissionIndex); + } else { + $('#saveMissionButton').removeClass('disabled'); + } + mission.setMaxWaypoints(FC.MISSION_PLANNER.getMaxWaypoints()); + mission.setValidMission(FC.MISSION_PLANNER.getValidMission()); + mission.setCountBusyPoints(FC.MISSION_PLANNER.getCountBusyPoints()); + multimission.setMaxWaypoints(mission.getMaxWaypoints()); + updateTotalInfo(); + mission.reinit(); + mission.copy(FC.MISSION_PLANNER); + mission.update(false, true); + refreshLayers(); + $('#MPeditPoint').fadeOut(300); } - mission.setMaxWaypoints(MISSION_PLANNER.getMaxWaypoints()); - mission.setValidMission(MISSION_PLANNER.getValidMission()); - mission.setCountBusyPoints(MISSION_PLANNER.getCountBusyPoints()); - multimission.setMaxWaypoints(mission.getMaxWaypoints()); - updateTotalInfo(); - mission.reinit(); - mission.copy(MISSION_PLANNER); - mission.update(false, true); - refreshLayers(); - $('#MPeditPoint').fadeOut(300); - }); + ]); + saveChainer.execute(); + function setMissionIndex() { let activeIndex = singleMissionActive() ? 1 : $('#activeNissionIndex').text(); mspHelper.setSetting("nav_wp_multi_mission_index", activeIndex, function () { MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function () { - GUI.log(chrome.i18n.getMessage('multimission_active_index_saved_eeprom')); + GUI.log(i18n.getMessage('multimission_active_index_saved_eeprom')); }); }); } @@ -2802,7 +3516,7 @@ TABS.mission_control.initialize = function (callback) { availableWPs = availableWPs - multimission.get().length; } $('#availablePoints').text(availableWPs + '/' + mission.getMaxWaypoints()); - $('#missionValid').html(mission.getValidMission() ? chrome.i18n.getMessage('armingCheckPass') : chrome.i18n.getMessage('armingCheckFail')); + $('#missionValid').html(mission.getValidMission() ? i18n.getMessage('armingCheckPass') : i18n.getMessage('armingCheckFail')); } } @@ -2817,6 +3531,11 @@ TABS.mission_control.initialize = function (callback) { } } + function updateSelectedShAndFwAp(index) { + selectedSafehome = FC.SAFEHOMES.get()[index]; + selectedFwApproachSh = FC.FW_APPROACH.get()[index]; + } + /* resetAltitude = true : For selected WPs only. Changes WP Altitude value back to previous value if setting below ground level. ^ resetAltitude = false : changes WP Altitude to value required to give ground clearance = default Altitude setting ^ AbsAltCheck : check value for whether or not to use absolute altitude. This can be the P3 bitset or excplicitly set to true or false */ @@ -2828,7 +3547,7 @@ TABS.mission_control.initialize = function (callback) { if (AbsAltCheck) { if (checkAltitude < 100 * elevation) { if (resetAltitude) { - alert(chrome.i18n.getMessage('MissionPlannerAltitudeChangeReset')); + GUI.alert(i18n.getMessage('MissionPlannerAltitudeChangeReset')); altitude = selectedMarker.getAlt(); } else { altitude = settings.alt + 100 * elevation; @@ -2839,7 +3558,7 @@ TABS.mission_control.initialize = function (callback) { let elevationAtHome = HOME.getAlt(); if ((checkAltitude / 100 + elevationAtHome) < elevation) { if (resetAltitude) { - alert(chrome.i18n.getMessage('MissionPlannerAltitudeChangeReset')); + GUI.alert(i18n.getMessage('MissionPlannerAltitudeChangeReset')); altitude = selectedMarker.getAlt(); } else { let currentGroundClearance = 100 * Number($('#groundClearanceValueAtWP').text()); @@ -2952,6 +3671,13 @@ TABS.mission_control.initialize = function (callback) { } } } + + function parseBooleans (str) { + if (/^(?:true|false)$/i.test(str)) { + str = str.toLowerCase() === 'true'; + } + return str; + }; }; TABS.mission_control.isBitSet = function(bits, testBit) { @@ -2968,9 +3694,9 @@ TABS.mission_control.setBit = function(bits, bit, value) { // function handleError(evt) { // if (evt.message) { // Chrome sometimes provides this - // alert("error: "+evt.message +" at linenumber: "+evt.lineno+" of file: "+evt.filename); + // GUI.alert("error: "+evt.message +" at linenumber: "+evt.lineno+" of file: "+evt.filename); // } else { - // alert("error: "+evt.type+" from element: "+(evt.srcElement || evt.target)); + // GUI.alert("error: "+evt.type+" from element: "+(evt.srcElement || evt.target)); // } // } diff --git a/tabs/mixer.html b/tabs/mixer.html index ae03334a7..47539ccf6 100644 --- a/tabs/mixer.html +++ b/tabs/mixer.html @@ -15,11 +15,26 @@ -
- - +
+
+ Motor direction +
+ + +
+
+ +
@@ -45,10 +60,10 @@
-
1
-
2
-
3
-
4
+ + + +
diff --git a/tabs/mixer.js b/tabs/mixer.js index 85d1f2623..bdd41a419 100644 --- a/tabs/mixer.js +++ b/tabs/mixer.js @@ -1,6 +1,21 @@ -/*global $,helper,mspHelper,MSP,GUI,SERVO_RULES,MOTOR_RULES,MIXER_CONFIG,googleAnalytics,LOGIC_CONDITIONS,TABS,ServoMixRule*/ 'use strict'; +const path = require('path'); + +const MSPChainerClass = require('./../js/msp/MSPchainer'); +const mspHelper = require('./../js/msp/MSPHelper'); +const MSPCodes = require('./../js/msp/MSPCodes'); +const MSP = require('./../js/msp'); +const { GUI, TABS } = require('./../js/gui'); +const FC = require('./../js/fc'); +const i18n = require('./../js/localization'); +const { mixer, platform, PLATFORM, INPUT, STABILIZED } = require('./../js/model'); +const Settings = require('./../js/settings'); +const jBox = require('../js/libraries/jBox/jBox.min'); +const interval = require('./../js/intervals'); +const ServoMixRule = require('./../js/servoMixRule'); +const MotorMixRule = require('./../js/motorMixRule'); + TABS.mixer = {}; TABS.mixer.initialize = function (callback, scrollPosition) { @@ -19,7 +34,6 @@ TABS.mixer.initialize = function (callback, scrollPosition) { if (GUI.active_tab != 'mixer') { GUI.active_tab = 'mixer'; - googleAnalytics.sendAppView('Mixer'); } loadChainer.setChain([ @@ -46,12 +60,12 @@ TABS.mixer.initialize = function (callback, scrollPosition) { saveChainer.setExitPoint(reboot); function saveSettings(onComplete) { - Settings.saveInputs().then(onComplete); + Settings.saveInputs(onComplete); } function reboot() { //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); + GUI.log(i18n.getMessage('configurationEepromSaved')); GUI.tab_switch_cleanup(function() { MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); @@ -60,16 +74,16 @@ TABS.mixer.initialize = function (callback, scrollPosition) { function reinitialize() { //noinspection JSUnresolvedVariable - GUI.log(chrome.i18n.getMessage('deviceRebooting')); + GUI.log(i18n.getMessage('deviceRebooting')); GUI.handleReconnect($('.tab_mixer a')); } function loadHtml() { - GUI.load("./tabs/mixer.html", Settings.processHtml(processHtml)); + GUI.load(path.join(__dirname, "mixer.html"), Settings.processHtml(processHtml)); } function renderOutputTable() { - let outputCount = OUTPUT_MAPPING.getOutputCount(), + let outputCount = FC.OUTPUT_MAPPING.getOutputCount(), $outputRow = $('#output-row'), $functionRow = $('#function-row'); @@ -78,10 +92,11 @@ TABS.mixer.initialize = function (callback, scrollPosition) { for (let i = 1; i <= outputCount; i++) { - let timerId = OUTPUT_MAPPING.getTimerId(i - 1); - let color = OUTPUT_MAPPING.getOutputTimerColor(i - 1); + let timerId = FC.OUTPUT_MAPPING.getTimerId(i - 1); + let color = FC.OUTPUT_MAPPING.getOutputTimerColor(i - 1); + let isLed = FC.OUTPUT_MAPPING.isLedPin(i - 1); - $outputRow.append('S' + i + ' (Timer ' + (timerId + 1) + ')'); + $outputRow.append('S' + i + (isLed ? '/LED' : '') + ' (Timer ' + (timerId + 1) + ')'); $functionRow.append('-'); } @@ -90,33 +105,34 @@ TABS.mixer.initialize = function (callback, scrollPosition) { } function updateTimerOverride() { - let timers = OUTPUT_MAPPING.getUsedTimerIds(); + let timers = FC.OUTPUT_MAPPING.getUsedTimerIds(); for(let i =0; i < timers.length;++i) { let timerId = timers[i]; - $select = $('#timer-output-' + timerId); + let $select = $('#timer-output-' + timerId); if(!$select) { continue; } - OUTPUT_MAPPING.setTimerOverride(timerId, $select.val()); + FC.OUTPUT_MAPPING.setTimerOverride(timerId, $select.val()); } } function renderTimerOverride() { - let outputCount = OUTPUT_MAPPING.getOutputCount(), + let outputCount = FC.OUTPUT_MAPPING.getOutputCount(), $container = $('#timerOutputsList'), timers = {}; - let usedTimers = OUTPUT_MAPPING.getUsedTimerIds(); + let usedTimers = FC.OUTPUT_MAPPING.getUsedTimerIds(); - for (t of usedTimers) { - var usageMode = OUTPUT_MAPPING.getTimerOverride(t); + for (let t of usedTimers) { + var usageMode = FC.OUTPUT_MAPPING.getTimerOverride(t); $container.append( - '
' + + '
' + '' + '
diff --git a/tabs/onboard_logging.js b/tabs/onboard_logging.js index 8b460fddf..b8b692596 100644 --- a/tabs/onboard_logging.js +++ b/tabs/onboard_logging.js @@ -1,8 +1,20 @@ -/*global $,MSP,MSPCodes,TABS,GUI,CONFIGURATOR,helper,mspHelper,nwdialog,SDCARD,chrome*/ 'use strict'; -var - sdcardTimer; +const { dialog } = require("@electron/remote"); +const fs = require('fs'); +const path = require('path'); + +const MSPCodes = require('./../js/msp/MSPCodes'); +const MSP = require('./../js/msp'); +const mspHelper = require("./../js/msp/MSPHelper"); +const { GUI, TABS } = require('./../js/gui'); +const FC = require('./../js/fc'); +const CONFIGURATOR = require('./../js/data_storage'); +const features = require('./../js/feature_framework'); +const i18n = require('./../js/localization'); +const BitHelper = require('./../js/bitHelper'); + +var sdcardTimer; TABS.onboard_logging = { }; @@ -30,7 +42,6 @@ TABS.onboard_logging.initialize = function (callback) { if (GUI.active_tab != 'onboard_logging') { GUI.active_tab = 'onboard_logging'; - googleAnalytics.sendAppView('onboard_logging'); } if (CONFIGURATOR.connectionValid) { @@ -55,7 +66,7 @@ TABS.onboard_logging.initialize = function (callback) { } function reboot() { - GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); + GUI.log(i18n.getMessage('configurationEepromSaved')); GUI.tab_switch_cleanup(function() { MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); @@ -63,61 +74,61 @@ TABS.onboard_logging.initialize = function (callback) { } function reinitialize() { - GUI.log(chrome.i18n.getMessage('deviceRebooting')); + GUI.log(i18n.getMessage('deviceRebooting')); GUI.handleReconnect($('.tab_onboard_logging a')); } function load_html() { - GUI.load("./tabs/onboard_logging.html", function() { + GUI.load(path.join(__dirname, "onboard_logging.html"), function() { // translate to user-selected language - localize(); + i18n.localize();; var - dataflashPresent = DATAFLASH.totalSize > 0, + dataflashPresent = FC.DATAFLASH.totalSize > 0, blackboxSupport = false; - if ((BLACKBOX.supported || DATAFLASH.supported) && bit_check(FEATURES, 19)) { + if ((FC.BLACKBOX.supported || FC.DATAFLASH.supported) && BitHelper.bit_check(FC.FEATURES, 19)) { blackboxSupport = true; } $(".tab-onboard_logging") .addClass("serial-supported") - .toggleClass("dataflash-supported", DATAFLASH.supported) + .toggleClass("dataflash-supported", FC.DATAFLASH.supported) .toggleClass("dataflash-present", dataflashPresent) - .toggleClass("sdcard-supported", SDCARD.supported) - .toggleClass("blackbox-config-supported", BLACKBOX.supported) + .toggleClass("sdcard-supported", FC.SDCARD.supported) + .toggleClass("blackbox-config-supported", FC.BLACKBOX.supported) .toggleClass("blackbox-supported", blackboxSupport) .toggleClass("blackbox-unsupported", !blackboxSupport); if (dataflashPresent) { // UI hooks - $('.tab-onboard_logging a.erase-flash').click(ask_to_erase_flash); + $('.tab-onboard_logging a.erase-flash').on('click', ask_to_erase_flash); - $('.tab-onboard_logging a.erase-flash-confirm').click(flash_erase); - $('.tab-onboard_logging a.erase-flash-cancel').click(flash_erase_cancel); + $('.tab-onboard_logging a.erase-flash-confirm').on('click', flash_erase); + $('.tab-onboard_logging a.erase-flash-cancel').on('click', flash_erase_cancel); - $('.tab-onboard_logging a.save-flash').click(flash_save_begin); - $('.tab-onboard_logging a.save-flash-cancel').click(flash_save_cancel); - $('.tab-onboard_logging a.save-flash-dismiss').click(dismiss_saving_dialog); + $('.tab-onboard_logging a.save-flash').on('click', flash_save_begin); + $('.tab-onboard_logging a.save-flash-cancel').on('click', flash_save_cancel); + $('.tab-onboard_logging a.save-flash-dismiss').on('click', dismiss_saving_dialog); } - $('.save-blackbox-feature').click(function () { - helper.features.reset(); - helper.features.fromUI($('.require-blackbox-unsupported')); - helper.features.execute(save_to_eeprom); + $('.save-blackbox-feature').on('click', function () { + features.reset(); + features.fromUI($('.require-blackbox-unsupported')); + features.execute(save_to_eeprom); }); - if (BLACKBOX.supported) { - $(".tab-onboard_logging a.save-settings").click(function() { + if (FC.BLACKBOX.supported) { + $(".tab-onboard_logging a.save-settings").on('click', function () { var rate = $(".blackboxRate select").val().split('/'); - BLACKBOX.blackboxRateNum = parseInt(rate[0], 10); - BLACKBOX.blackboxRateDenom = parseInt(rate[1], 10); - BLACKBOX.blackboxDevice = parseInt($(".blackboxDevice select").val(), 10); - BLACKBOX.blackboxIncludeFlags = getIncludeFlags(); - helper.features.reset(); - helper.features.fromUI($('.require-blackbox-supported')); - helper.features.execute(function () { + FC.BLACKBOX.blackboxRateNum = parseInt(rate[0], 10); + FC.BLACKBOX.blackboxRateDenom = parseInt(rate[1], 10); + FC.BLACKBOX.blackboxDevice = parseInt($(".blackboxDevice select").val(), 10); + FC.BLACKBOX.blackboxIncludeFlags = getIncludeFlags(); + features.reset(); + features.fromUI($('.require-blackbox-supported')); + features.execute(function () { mspHelper.sendBlackboxConfiguration(save_to_eeprom); }); }); @@ -127,7 +138,7 @@ TABS.onboard_logging.initialize = function (callback) { const blackboxFieldsDiv = $("#blackBoxFlagsDiv"); for (let i = 0; i < blackBoxFields.length; i++) { const FIELD_ID = blackBoxFields[i]; - const isEnabled = (BLACKBOX.blackboxIncludeFlags & 1<') input.attr("id",FIELD_ID); input.attr("checked",isEnabled); @@ -136,7 +147,7 @@ TABS.onboard_logging.initialize = function (callback) { label.attr("for",FIELD_ID) const span = $(''); - span.html(chrome.i18n.getMessage(FIELD_ID)) + span.html(i18n.getMessage(FIELD_ID)) label.append(span); const checkbox = $('
') @@ -160,20 +171,20 @@ TABS.onboard_logging.initialize = function (callback) { deviceSelect = $(".blackboxDevice select").empty(); deviceSelect.append(''); - if (DATAFLASH.ready) { + if (FC.DATAFLASH.ready) { deviceSelect.append(''); } - if (SDCARD.supported) { + if (FC.SDCARD.supported) { deviceSelect.append(''); } - deviceSelect.val(BLACKBOX.blackboxDevice); + deviceSelect.val(FC.BLACKBOX.blackboxDevice); } function populateLoggingRates() { var - userRateGCD = gcd(BLACKBOX.blackboxRateNum, BLACKBOX.blackboxRateDenom), - userRate = {num: BLACKBOX.blackboxRateNum / userRateGCD, denom: BLACKBOX.blackboxRateDenom / userRateGCD}; + userRateGCD = gcd(FC.BLACKBOX.blackboxRateNum, FC.BLACKBOX.blackboxRateDenom), + userRate = {num: FC.BLACKBOX.blackboxRateNum / userRateGCD, denom: FC.BLACKBOX.blackboxRateDenom / userRateGCD}; // Offer a reasonable choice of logging rates (if people want weird steps they can use CLI) var @@ -253,20 +264,20 @@ TABS.onboard_logging.initialize = function (callback) { } function update_html() { - update_bar_width($(".tab-onboard_logging .dataflash-used"), DATAFLASH.usedSize, DATAFLASH.totalSize, "Used space", false); - update_bar_width($(".tab-onboard_logging .dataflash-free"), DATAFLASH.totalSize - DATAFLASH.usedSize, DATAFLASH.totalSize, "Free space", false); + update_bar_width($(".tab-onboard_logging .dataflash-used"), FC.DATAFLASH.usedSize, FC.DATAFLASH.totalSize, "Used space", false); + update_bar_width($(".tab-onboard_logging .dataflash-free"), FC.DATAFLASH.totalSize - FC.DATAFLASH.usedSize, FC.DATAFLASH.totalSize, "Free space", false); - update_bar_width($(".tab-onboard_logging .sdcard-other"), SDCARD.totalSizeKB - SDCARD.freeSizeKB, SDCARD.totalSizeKB, "Unavailable space", true); - update_bar_width($(".tab-onboard_logging .sdcard-free"), SDCARD.freeSizeKB, SDCARD.totalSizeKB, "Free space for logs", true); + update_bar_width($(".tab-onboard_logging .sdcard-other"), FC.SDCARD.totalSizeKB - FC.SDCARD.freeSizeKB, FC.SDCARD.totalSizeKB, "Unavailable space", true); + update_bar_width($(".tab-onboard_logging .sdcard-free"), FC.SDCARD.freeSizeKB, FC.SDCARD.totalSizeKB, "Free space for logs", true); - $(".btn a.erase-flash, .btn a.save-flash").toggleClass("disabled", DATAFLASH.usedSize == 0); + $(".btn a.erase-flash, .btn a.save-flash").toggleClass("disabled", FC.DATAFLASH.usedSize == 0); $(".tab-onboard_logging") - .toggleClass("sdcard-error", SDCARD.state == MSP.SDCARD_STATE_FATAL) - .toggleClass("sdcard-initializing", SDCARD.state == MSP.SDCARD_STATE_CARD_INIT || SDCARD.state == MSP.SDCARD_STATE_FS_INIT) - .toggleClass("sdcard-ready", SDCARD.state == MSP.SDCARD_STATE_READY); + .toggleClass("sdcard-error", FC.SDCARD.state == MSP.SDCARD_STATE_FATAL) + .toggleClass("sdcard-initializing", FC.SDCARD.state == MSP.SDCARD_STATE_CARD_INIT || FC.SDCARD.state == MSP.SDCARD_STATE_FS_INIT) + .toggleClass("sdcard-ready", FC.SDCARD.state == MSP.SDCARD_STATE_READY); - switch (SDCARD.state) { + switch (FC.SDCARD.state) { case MSP.SDCARD_STATE_NOT_PRESENT: $(".sdcard-status").text("No card inserted"); break; @@ -283,10 +294,10 @@ TABS.onboard_logging.initialize = function (callback) { $(".sdcard-status").text("Filesystem starting..."); break; default: - $(".sdcard-status").text("Unknown state " + SDCARD.state); + $(".sdcard-status").text("Unknown state " + FC.SDCARD.state); } - if (SDCARD.supported && !sdcardTimer) { + if (FC.SDCARD.supported && !sdcardTimer) { // Poll for changes in SD card status sdcardTimer = setTimeout(function() { sdcardTimer = false; @@ -344,10 +355,9 @@ TABS.onboard_logging.initialize = function (callback) { if (GUI.connected_to) { // Begin by refreshing the occupied size in case it changed while the tab was open flash_update_summary(function() { - const maxBytes = DATAFLASH.usedSize; + const maxBytes = FC.DATAFLASH.usedSize; prepare_file(function(filename) { - const fs = require('fs'); let nextAddress = 0; show_saving_dialog(); @@ -395,9 +405,15 @@ TABS.onboard_logging.initialize = function (callback) { + zeroPad(date.getDate(), 2) + '_' + zeroPad(date.getHours(), 2) + zeroPad(date.getMinutes(), 2) + zeroPad(date.getSeconds(), 2) + '.TXT'; - nwdialog.setContext(document); - nwdialog.saveFileDialog(filename, function(file) { - onComplete(file); + var options = { + defaultPath: filename, + filters: [ { name: "TXT file", extensions: ['txt'] } ] + }; + dialog.showSaveDialog(options).then(result => { + if (result.canceled) { + return; + } + onComplete(result.filePath); }); } @@ -410,7 +426,7 @@ TABS.onboard_logging.initialize = function (callback) { function poll_for_erase_completion() { flash_update_summary(function() { if (CONFIGURATOR.connectionValid && !eraseCancelled) { - if (DATAFLASH.ready) { + if (FC.DATAFLASH.ready) { $(".dataflash-confirm-erase")[0].close(); } else { setTimeout(poll_for_erase_completion, 500); diff --git a/tabs/options.html b/tabs/options.html index af41f97a6..d7e6c2f9f 100644 --- a/tabs/options.html +++ b/tabs/options.html @@ -10,21 +10,26 @@
- +
-
+ +
+ + +
@@ -82,5 +87,27 @@
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+ + +
+
+
\ No newline at end of file diff --git a/tabs/osd.html b/tabs/osd.html index f9489d713..8488bfbd4 100644 --- a/tabs/osd.html +++ b/tabs/osd.html @@ -10,6 +10,15 @@

+ + + + + + + + +
@@ -326,7 +335,17 @@

-
+ + +
+
+
+
+
+
+ + +
@@ -435,6 +454,12 @@

Font:

+
diff --git a/tabs/osd.js b/tabs/osd.js index 3467d084e..00d920821 100644 --- a/tabs/osd.js +++ b/tabs/osd.js @@ -1,6 +1,26 @@ -/*global $,nwdialog*/ 'use strict'; +const inflection = require( 'inflection' ); +const fs = require('fs'); +const path = require('path'); +const semver = require('semver'); +const mapSeries = require('promise-map-series'); +const { dialog } = require("@electron/remote"); +const Store = require('electron-store'); +const store = new Store(); + +const FC = require('./../js/fc'); +const { GUI, TABS } = require('./../js/gui'); +const MSP = require('./../js/msp'); +const MSPCodes = require('./../js/msp/MSPCodes'); +const mspHelper = require('./../js/msp/MSPHelper'); +const Settings = require('./../js/settings'); +const { globalSettings } = require('./../js/globalSettings'); +const { PortHandler } = require('./../js/port_handler'); +const i18n = require('./../js/localization'); +const jBox = require('./../js/libraries/jBox/jBox.min'); + + var SYM = SYM || {}; SYM.LAST_CHAR = 225; // For drawing the font preview SYM.BLANK = 0x20; @@ -127,17 +147,17 @@ SYM.AH_AIRCRAFT2 = 0x1A4; SYM.AH_AIRCRAFT3 = 0x1A5; SYM.AH_AIRCRAFT4 = 0x1A6; -SYM.AH_CROSSHAIRS = new Array(0x166, 0x1A4, new Array(0x190, 0x191, 0x192), new Array(0x193, 0x194, 0x195), new Array(0x196, 0x197, 0x198), new Array(0x199, 0x19A, 0x19B), new Array (0x19C, 0x19D, 0x19E), new Array (0x19F, 0x1A0, 0x1A1)); +SYM.SYM_HUD_SIGNAL_3 = 0x163; +SYM.SYM_HUD_CARDINAL = 0x1BA; -var useESCTelemetry = false; -var useBaro = false; -var useCRSFRx = false; -var usePitot = false; +SYM.AH_CROSSHAIRS = new Array(0x166, 0x1A4, new Array(0x190, 0x191, 0x192), new Array(0x193, 0x194, 0x195), new Array(0x196, 0x197, 0x198), new Array(0x199, 0x19A, 0x19B), new Array (0x19C, 0x19D, 0x19E), new Array (0x19F, 0x1A0, 0x1A1)); var video_type = null; var isGuidesChecked = false; var FONT = FONT || {}; +var layout_clipboard = {layout: [], filled: false}; + var FONT = FONT || {}; FONT.initData = function () { if (FONT.data) { @@ -236,13 +256,24 @@ FONT.parseMCMFontFile = function (data) { //noinspection JSUnusedLocalSymbols FONT.openFontFile = function ($preview) { return new Promise(function (resolve) { - - nwdialog.setContext(document); - nwdialog.openFileDialog('.mcm', function(filename) { - const fs = require('fs'); - const fontData = fs.readFileSync(filename, {flag: "r"}); - FONT.parseMCMFontFile(fontData.toString()); - resolve(); + var options = { + filters: [ + { name: 'Font file', extensions: ['mcm'] } + ], + }; + dialog.showOpenDialog(options).then(result => { + if (result.canceled) { + console.log('No file selected'); + return; + } + + if (result.filePaths.length == 1) { + const fontData = fs.readFileSync(result.filePaths[0], {flag: "r"}); + FONT.parseMCMFontFile(fontData.toString()); + resolve(); + } + }).catch (err => { + console.log(err); }); }); }; @@ -341,7 +372,7 @@ FONT.preview = function ($el) { $el.empty(); for (var i = 1; i <= SYM.LAST_CHAR; i++) { var url = FONT.data.character_image_urls[i]; - $el.append(' '); + $el.append(' '); } }; @@ -489,9 +520,7 @@ OSD.initData = function () { item_count: 0, items: [], groups: {}, - preview: [], - isDjiHdFpv: false, - isMspDisplay: false + preview: [] }; }; @@ -921,7 +950,7 @@ OSD.constants = { name: 'AIR_SPEED', id: 27, enabled: function() { - return usePitot; + return HARDWARE.capabilities.usePitot; }, preview: function(osd_data) { var speed; @@ -946,7 +975,7 @@ OSD.constants = { name: 'AIR_MAX_SPEED', id: 127, enabled: function() { - return usePitot; + return HARDWARE.capabilities.usePitot; }, preview: function(osd_data) { // 3 chars @@ -977,7 +1006,7 @@ OSD.constants = { id: 106, min_version: '2.3.0', enabled: function() { - return useESCTelemetry; + return HARDWARE.capabilities.useESCTelemetry; }, preview: function(){ let rpmPreview = '112974'.substr((6 - parseInt(Settings.getInputValue('osd_esc_rpm_precision')))); @@ -1057,7 +1086,7 @@ OSD.constants = { name: 'BARO_TEMPERATURE', id: 87, enabled: function() { - return useBaro; + return HARDWARE.capabilities.useBaro; }, preview: function(osd_data) { switch (OSD.data.preferences.units) { @@ -1073,7 +1102,7 @@ OSD.constants = { id: 107, min_version: '2.5.0', enabled: function() { - return useESCTelemetry; + return HARDWARE.capabilities.useESCTelemetry; }, preview: function(osd_data) { switch (OSD.data.preferences.units) { @@ -1677,13 +1706,13 @@ OSD.constants = { }, { name: 'ADSB_WARNING_MESSAGE', - id: 147, + id: 150, min_version: '7.1.0', preview: FONT.symbol(SYM.ADSB) + '19.25' + FONT.symbol(SYM.DIR_TO_HOME+1) + '2.75', }, { name: 'ADSB_INFO', - id: 148, + id: 151, min_version: '7.1.0', preview: FONT.symbol(SYM.ADSB) + '2', }, @@ -1748,6 +1777,15 @@ OSD.constants = { id: 99, preview: FONT.symbol(SYM.DIRECTION) + '\nN', }, + { + name: 'FORMATION_FLIGHT', + id: 153, + min_version: '8.0.0', + positionable: true, + preview: function(osd_data) { + return FONT.symbol(SYM.DIRECTION) + 'B' + FONT.symbol(SYM.SYM_HUD_SIGNAL_3) + FONT.symbol(SYM.SYM_HUD_CARDINAL) + "\n 150" + "\n" + FONT.symbol(SYM.AH_DECORATION_UP) + " 27" ; + } + }, ], }, { @@ -1771,7 +1809,7 @@ OSD.constants = { { name: 'osdGroupCRSF', enabled: function() { - return useCRSFRx; + return HARDWARE.capabilities.useCRSFRx; }, items: [ { @@ -1865,6 +1903,27 @@ OSD.constants = { id: 116, positionable: true, preview: 'G3:30126' + }, + { + name: 'CUSTOM ELEMENT 1', + id: 147, + min_version: '7.1.0', + positionable: true, + preview: "CE_1", + }, + { + name: 'CUSTOM ELEMENT 2', + id: 148, + min_version: '7.1.0', + positionable: true, + preview: "CE_2", + }, + { + name: 'CUSTOM ELEMENT 3', + id: 149, + min_version: '7.1.0', + positionable: true, + preview: "CE_3", } ] }, @@ -1939,27 +1998,27 @@ OSD.constants = { preview: 'TEX 0' }, { - name: 'STABILIZED_RC_EXPO', + name: 'STABILIZED.RC_EXPO', id: 64, preview: 'EXP 20' }, { - name: 'STABILIZED_RC_YAW_EXPO', + name: 'STABILIZED.RC_YAW_EXPO', id: 65, preview: 'YEX 20' }, { - name: 'STABILIZED_PITCH_RATE', + name: 'STABILIZED.PITCH_RATE', id: 67, preview: 'SPR 20' }, { - name: 'STABILIZED_ROLL_RATE', + name: 'STABILIZED.ROLL_RATE', id: 68, preview: 'SRR 20' }, { - name: 'STABILIZED_YAW_RATE', + name: 'STABILIZED.YAW_RATE', id: 69, preview: 'SYR 20' }, @@ -2095,7 +2154,7 @@ OSD.is_item_displayed = function(item, group) { if (typeof group.enabled === 'function' && group.enabled() === false) { return false; } - if (item.min_version && !semver.gte(CONFIG.flightControllerVersion, item.min_version)) { + if (item.min_version && !semver.gte(FC.CONFIG.flightControllerVersion, item.min_version)) { return false; } if (typeof item.enabled === 'function' && item.enabled() === false) { @@ -2121,23 +2180,12 @@ OSD.reload = function(callback) { } }; - MSP.promise(MSPCodes.MSP2_CF_SERIAL_CONFIG).then(function (resp) { - $.each(SERIAL_CONFIG.ports, function(index, port){ - if(port.functions.includes('DJI_FPV')) { - OSD.data.isDjiHdFpv = true; - } - if(port.functions.includes('MSP_DISPLAYPORT')) { - OSD.data.isMspDisplay = true; - } - }); - }); - MSP.promise(MSPCodes.MSP2_INAV_OSD_LAYOUTS).then(function (resp) { OSD.msp.decodeLayoutCounts(resp); // Get data for all layouts var ids = Array.apply(null, {length: OSD.data.layout_count}).map(Number.call, Number); - var layouts = Promise.mapSeries(ids, function (layoutIndex, ii) { + var layouts = mapSeries(ids, function (layoutIndex, ii) { var data = []; data.push8(layoutIndex); return MSP.promise(MSPCodes.MSP2_INAV_OSD_LAYOUTS, data).then(function (resp) { @@ -2158,6 +2206,11 @@ OSD.reload = function(callback) { }); }); }); + + if(semver.gte(FC.CONFIG.flightControllerVersion, '7.1.0')) + { + MSP.send_message(MSPCodes.MSP2_INAV_CUSTOM_OSD_ELEMENTS); + } }; OSD.updateSelectedLayout = function(new_layout) { @@ -2495,7 +2548,7 @@ OSD.GUI.preview = { position += overflows_line; } - $('input.' + item_id + '.position').val(position).change(); + $('input.' + item_id + '.position').val(position).trigger('change'); } }; @@ -2517,11 +2570,11 @@ OSD.GUI.updateVideoMode = function() { // video mode var $videoTypes = $('.video-types').empty(); - if (!OSD.data.isDjiHdFpv) { + if (!HARDWARE.capabilities.isDjiHdFpv) { $('#dji_settings').hide(); } - if (OSD.data.isMspDisplay) { + if (HARDWARE.capabilities.isMspDisplay) { if (mspVideoSystem.includes(OSD.data.preferences.video_system) == false) { OSD.data.preferences.video_system = OSD.constants.VIDEO_TYPES.indexOf('HDZERO'); OSD.updateDisplaySize(); @@ -2535,7 +2588,7 @@ OSD.GUI.updateVideoMode = function() { } } - if (OSD.data.isMspDisplay) { + if (HARDWARE.capabilities.isMspDisplay) { for (var i = 0; i < OSD.constants.VIDEO_TYPES.length; i++) { if (mspVideoSystem.includes(i)) { @@ -2559,7 +2612,7 @@ OSD.GUI.updateVideoMode = function() { } } - $videoTypes.change(function () { + $videoTypes.on('change', function () { OSD.data.preferences.video_system = $(this).find(':selected').data('type'); OSD.updateDisplaySize(); OSD.GUI.saveConfig(); @@ -2573,10 +2626,10 @@ OSD.GUI.updateUnits = function() { for (var i = 0; i < OSD.constants.UNIT_TYPES.length; i++) { var unitType = OSD.constants.UNIT_TYPES[i]; - if (unitType.min_version && semver.lt(CONFIG.flightControllerVersion, unitType.min_version)) { + if (unitType.min_version && semver.lt(FC.CONFIG.flightControllerVersion, unitType.min_version)) { continue; } - var name = chrome.i18n.getMessage(unitType.name); + var name = i18n.getMessage(unitType.name); var $option = $(''); $option.attr('value', name); $option.data('type', unitType.value); @@ -2589,7 +2642,7 @@ OSD.GUI.updateUnits = function() { var unitType = OSD.constants.UNIT_TYPES[OSD.data.preferences.units]; var tip; if (unitType.tip) { - tip = chrome.i18n.getMessage(unitType.tip); + tip = i18n.getMessage(unitType.tip); } if (tip) { $unitTip.attr('title', tip); @@ -2599,7 +2652,7 @@ OSD.GUI.updateUnits = function() { } } updateUnitHelp(); - $unitMode.change(function (e) { + $unitMode.on('change', function (e) { var selected = $(this).find(':selected'); OSD.data.preferences.units = selected.data('type'); globalSettings.osdUnits = OSD.data.preferences.units; @@ -2630,9 +2683,9 @@ OSD.GUI.updateFields = function() { var groupContainer = $tmpl.clone().addClass('osd_group').show(); groupContainer.attr('id', group.name); var groupTitleContainer = groupContainer.find('.spacer_box_title'); - var groupTitle = chrome.i18n.getMessage(group.name); + var groupTitle = i18n.getMessage(group.name); groupTitleContainer.text(groupTitle); - var groupHelp = chrome.i18n.getMessage(group.name + '_HELP'); + var groupHelp = i18n.getMessage(group.name + '_HELP'); if (groupHelp) { $('
') .css('margin-top', '1px') @@ -2656,7 +2709,7 @@ OSD.GUI.updateFields = function() { var $field = $('
'); var name = item.name; var nameKey = 'osdElement_' + name; - var nameMessage = chrome.i18n.getMessage(nameKey); + var nameMessage = i18n.getMessage(nameKey); if (nameMessage) { name = nameMessage; } else { @@ -2666,7 +2719,7 @@ OSD.GUI.updateFields = function() { if (searchTerm.length > 0 && !name.toLowerCase().includes(searchTerm.toLowerCase())) { continue; } - var help = chrome.i18n.getMessage(nameKey + '_HELP'); + var help = i18n.getMessage(nameKey + '_HELP'); if (help) { $('
') .css('margin-top', '1px') @@ -2686,7 +2739,7 @@ OSD.GUI.updateFields = function() { $('') .data('item', item) .attr('checked', itemData.isVisible) - .change(function () { + .on('change', function () { var item = $(this).data('item'); var itemData = OSD.data.items[item.id]; var $position = $(this).parent().find('.position.' + item.name); @@ -2715,7 +2768,7 @@ OSD.GUI.updateFields = function() { $('') .data('item', item) .val(itemData.position) - .change($.debounce(250, function (e) { + .on('change', $.debounce(250, function (e) { var item = $(this).data('item'); var itemData = OSD.data.items[item.id]; itemData.position = parseInt($(this).val()); @@ -2737,7 +2790,7 @@ OSD.GUI.updateFields = function() { .attr('checked', isGuidesChecked) .on('change', function () { OSD.GUI.updateGuidesView(this.checked); - chrome.storage.local.set({'showOSDGuides': this.checked}); + store.set('showOSDGuides', this.checked); OSD.GUI.updatePreviews(); }) ); @@ -2746,7 +2799,7 @@ OSD.GUI.updateFields = function() { if ($('#djiUnsupportedElementsToggle').length == false) { $('#djiUnsupportedElements').prepend( $('') - .attr('checked', OSD.data.isDjiHdFpv && !OSD.data.isMspDisplay) + .attr('checked', HARDWARE.capabilities.isDjiHdFpv && !HARDWARE.capabilities.isMspDisplay) .on('change', function () { OSD.GUI.updateDjiView(this.checked); OSD.GUI.updatePreviews(); @@ -2802,7 +2855,7 @@ OSD.GUI.updateDjiMessageElements = function(on) { }; OSD.GUI.updateGuidesView = function(on) { - isHdZero = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'HDZERO'; + let isHdZero = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'HDZERO'; $('.hd_43_margin_left').toggleClass('hdzero_43_left', (isHdZero && on)) $('.hd_43_margin_right').toggleClass('hdzero_43_right', (isHdZero && on)) $('.hd_3016_box_top').toggleClass('hd_3016_top', (isHdZero && on)) @@ -2810,11 +2863,11 @@ OSD.GUI.updateGuidesView = function(on) { $('.hd_3016_box_left').toggleClass('hd_3016_left', (isHdZero && on)) $('.hd_3016_box_right').toggleClass('hd_3016_right', (isHdZero && on)) - isDJIWTF = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'DJIWTF'; + let isDJIWTF = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'DJIWTF'; $('.hd_43_margin_left').toggleClass('dji_hd_43_left', (isDJIWTF && on)) $('.hd_43_margin_right').toggleClass('dji_hd_43_right', (isDJIWTF && on)) - isAvatar = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'AVATAR'; + let isAvatar = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'AVATAR'; $('.hd_43_margin_left').toggleClass('hd_avatar_43_left', (isAvatar && on)) $('.hd_43_margin_right').toggleClass('hd_avatar_43_right', (isAvatar && on)) $('.hd_avatar_bottom_bar').toggleClass('hd_avatar_bottom', (isAvatar && on)) @@ -2823,13 +2876,13 @@ OSD.GUI.updateGuidesView = function(on) { $('.hd_avatar_storage_box_left').toggleClass('hd_avatar_storagebox_l', (isAvatar && on)) $('.hd_avatar_storage_box_right').toggleClass('hd_avatar_storagebox_r', (isAvatar && on)) - isBfHdCompat = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'BFHDCOMPAT'; + let isBfHdCompat = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'BFHDCOMPAT'; $('.hd_43_margin_left').toggleClass('hd_bfhdcompat_43_left', (isBfHdCompat && on)); $('.hd_43_margin_right').toggleClass('hd_bfhdcompat_43_right', (isBfHdCompat && on)); $('.hd_bfhdcompat_bottom_box').toggleClass('hd_bfhdcompat_bottom', (isBfHdCompat && on)); $('.hd_bfhdcompat_storage_box').toggleClass('hd_bfhdcompat_storagebox', (isBfHdCompat && on)); - isPAL = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'PAL' || OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'AUTO'; + let isPAL = OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'PAL' || OSD.constants.VIDEO_TYPES[OSD.data.preferences.video_system] == 'AUTO'; $('.pal_ntsc_box_bottom').toggleClass('ntsc_bottom', (isPAL && on)) }; @@ -2883,10 +2936,10 @@ OSD.GUI.updateDjiView = function(on) { }; OSD.GUI.updateAlarms = function() { - $(".osd_use_airspeed_alarm").toggle(usePitot); - $(".osd_use_baro_temp_alarm").toggle(useBaro); - $(".osd_use_esc_telemetry").toggle(useESCTelemetry); - $(".osd_use_crsf").toggle(useCRSFRx); + $(".osd_use_airspeed_alarm").toggle(HARDWARE.capabilities.usePitot); + $(".osd_use_baro_temp_alarm").toggle(HARDWARE.capabilities.useBaro); + $(".osd_use_esc_telemetry").toggle(HARDWARE.capabilities.useESCTelemetry); + $(".osd_use_crsf").toggle(HARDWARE.capabilities.useCRSFRx); }; OSD.GUI.updateMapPreview = function(mapCenter, name, directionSymbol, centerSymbol) { @@ -2901,7 +2954,7 @@ OSD.GUI.updatePreviews = function() { OSD.data.preview = []; // clear the buffer - for (i = 0; i < OSD.data.display_size.total; i++) { + for (let i = 0; i < OSD.data.display_size.total; i++) { OSD.data.preview.push([null, ' '.charCodeAt(0)]); }; @@ -2938,7 +2991,7 @@ OSD.GUI.updatePreviews = function() { } var x = 0; var y = 0; - for (i = 0; i < preview.length; i++) { + for (let i = 0; i < preview.length; i++) { var charCode = preview.charCodeAt(i); if (charCode == '\n'.charCodeAt(0)) { x = 0; @@ -2980,15 +3033,15 @@ OSD.GUI.updatePreviews = function() { // artificial horizon if ($('input[name="ARTIFICIAL_HORIZON"]').prop('checked')) { - for (i = 0; i < 9; i++) { + for (let i = 0; i < 9; i++) { OSD.GUI.checkAndProcessSymbolPosition(hudCenterPosition - 4 + i, SYM.AH_BAR9_0 + 4); } } // crosshairs if ($('input[name="CROSSHAIRS"]').prop('checked')) { - crsHNumber = Settings.getInputValue('osd_crosshairs_style'); - if (crsHNumber == 1) { + let crsHNumber = Settings.getInputValue('osd_crosshairs_style'); + if (crsHNumber == 1) { // AIRCRAFT style OSD.GUI.checkAndProcessSymbolPosition(hudCenterPosition - 2, SYM.AH_AIRCRAFT0); OSD.GUI.checkAndProcessSymbolPosition(hudCenterPosition - 1, SYM.AH_AIRCRAFT1); @@ -3012,7 +3065,7 @@ OSD.GUI.updatePreviews = function() { if ($('input[name="HORIZON_SIDEBARS"]').prop('checked')) { var hudwidth = OSD.constants.AHISIDEBARWIDTHPOSITION; var hudheight = OSD.constants.AHISIDEBARHEIGHTPOSITION; - for (i = -hudheight; i <= hudheight; i++) { + for (let i = -hudheight; i <= hudheight; i++) { OSD.GUI.checkAndProcessSymbolPosition(hudCenterPosition - hudwidth + (i * FONT.constants.SIZES.LINE), SYM.AH_DECORATION); OSD.GUI.checkAndProcessSymbolPosition(hudCenterPosition + hudwidth + (i * FONT.constants.SIZES.LINE), SYM.AH_DECORATION); } @@ -3028,7 +3081,7 @@ OSD.GUI.updatePreviews = function() { // render var $preview = $('.display-layout .preview').empty(); var $row = $('
'); - for (i = 0; i < OSD.data.display_size.total;) { + for (let i = 0; i < OSD.data.display_size.total;) { var charCode = OSD.data.preview[i]; var colorStyle = ''; @@ -3053,7 +3106,7 @@ OSD.GUI.updatePreviews = function() { $img.find('img').css('pointer-events', 'none'); if (item && item.positionable !== false) { var nameKey = 'osdElement_' + item.name; - var nameMessage = chrome.i18n.getMessage(nameKey); + var nameMessage = i18n.getMessage(nameKey); if (!nameMessage) { nameMessage = inflection.titleize(item.name); @@ -3080,10 +3133,13 @@ OSD.GUI.updateAll = function() { return; } var layouts = $('.osd_layouts'); + var copy = $('.osd_copy'); + var paste = $('.osd_paste').hide(); + var clear = $('.osd_clear'); if (OSD.data.layout_count > 1) { layouts.empty(); for (var ii = 0; ii < OSD.data.layout_count; ii++) { - var name = ii > 0 ? chrome.i18n.getMessage('osdLayoutAlternative', [ii]) : chrome.i18n.getMessage('osdLayoutDefault'); + var name = ii > 0 ? i18n.getMessage('osdLayoutAlternative', [ii]) : i18n.getMessage('osdLayoutDefault'); var opt = $('
+ +
+ + +

@@ -101,7 +266,7 @@

-
+
@@ -115,13 +280,20 @@

+
-
- - +
+
+
+ +
+
+ +
+
-