From 95c1b1da312082c571d6d23799f6603e413a3c66 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:48:11 +1000 Subject: [PATCH 01/25] Figuring out commands --- installer.ps1 | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 installer.ps1 diff --git a/installer.ps1 b/installer.ps1 new file mode 100644 index 0000000..e740d80 --- /dev/null +++ b/installer.ps1 @@ -0,0 +1,53 @@ +# +# © 2023 Peter Cole +# +# This file is part of EX-CommandStation +# +# This is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# It is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CommandStation. If not, see . +# + +# 32/64 bit Win installer +# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip +# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip + +param( + [Parameter()] + [String]$BUILD_DIR +) + +if (!$PSBoundParameters.ContainsKey('BUILD_DIR')) { +# Use the current date/time stamp to create a unique directory if one is not specified. + $BUILD_DATE = Get-Date -Format 'yyyyMMdd-HHmmss' + $BUILD_DIR = $env:TEMP + "\" + $BUILD_DATE +} + +if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { + $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" +} else { + $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" +} + +Write-Output "Downloading installer to $OUTFILE" + +$ProgressPreference = "SilentlyContinue" +Invoke-WebRequest -Uri $URL -OutFile $OUTFILE + +$CLI_INSTALL = $env:TEMP + "\" + "arduino-cli_installer" + +Expand-Archive -Path $OUTFILE -DestinationPath $CLI_INSTALL -Force +$ProgressPreference = "Continue" + +Write-Output "Installing using directory $BUILD_DIR" \ No newline at end of file From 305e0902f47bafb24d012ae6c2dd738c3cef4b45 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 4 Apr 2023 19:30:59 +1000 Subject: [PATCH 02/25] Got tag list --- installer.ps1 | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index e740d80..defccd6 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -21,33 +21,39 @@ # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" + param( [Parameter()] - [String]$BUILD_DIR + [String]$buildDirectory ) -if (!$PSBoundParameters.ContainsKey('BUILD_DIR')) { +if (!$PSBoundParameters.ContainsKey('buildDirectory')) { # Use the current date/time stamp to create a unique directory if one is not specified. - $BUILD_DATE = Get-Date -Format 'yyyyMMdd-HHmmss' - $BUILD_DIR = $env:TEMP + "\" + $BUILD_DATE + $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' + $buildDirectory = $env:TEMP + "\" + $buildDate } if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { - $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" - $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" } else { - $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" - $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" } -Write-Output "Downloading installer to $OUTFILE" +Write-Output "Downloading installer to $arduinoCLIZip" $ProgressPreference = "SilentlyContinue" -Invoke-WebRequest -Uri $URL -OutFile $OUTFILE +Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip -$CLI_INSTALL = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" -Expand-Archive -Path $OUTFILE -DestinationPath $CLI_INSTALL -Force +Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force $ProgressPreference = "Continue" -Write-Output "Installing using directory $BUILD_DIR" \ No newline at end of file +Write-Output "Installing using directory $buildDirectory" + +foreach ($tag in Invoke-RestMethod -Uri $gitHubAPITags | Format-List -Property ref) { + $tag.getType() +} From b18df1405cc0317cf419977a7f57a3626ed84258 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Wed, 5 Apr 2023 05:30:00 +1000 Subject: [PATCH 03/25] Got tag version and URL --- installer.ps1 | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index defccd6..3b95535 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -20,10 +20,11 @@ # 32/64 bit Win installer # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip +# +# For script errors set ExecutionPolicy: +# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass -$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" - -param( +Param( [Parameter()] [String]$buildDirectory ) @@ -34,6 +35,8 @@ if (!$PSBoundParameters.ContainsKey('buildDirectory')) { $buildDirectory = $env:TEMP + "\" + $buildDate } +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" + if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" @@ -54,6 +57,12 @@ $ProgressPreference = "Continue" Write-Output "Installing using directory $buildDirectory" -foreach ($tag in Invoke-RestMethod -Uri $gitHubAPITags | Format-List -Property ref) { - $tag.getType() +$tagList = Invoke-RestMethod -Uri $gitHubAPITags + +# Example zip: https://github.com/DCC-EX/CommandStation-EX/archive/refs/tags/v4.2.36-Devel.zip + +foreach ($tag in $tagList) { + $version = $tag.ref.split("/")[2] + $versionURL = "https://github.com/DCC-EX/CommandStation-EX/archive/" + $tag.ref + Write-Output "$version : $versionURL" } From de6e91a7782576c460d4d2011ffea9ad5cf49994 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:54:48 +1000 Subject: [PATCH 04/25] Working on logic --- installer.ps1 | 119 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 3b95535..d232187 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -1,4 +1,4 @@ -# +<# # © 2023 Peter Cole # # This file is part of EX-CommandStation @@ -15,28 +15,31 @@ # # You should have received a copy of the GNU General Public License # along with CommandStation. If not, see . -# +#> -# 32/64 bit Win installer -# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip -# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip -# -# For script errors set ExecutionPolicy: -# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass +<############################################ +For script errors set ExecutionPolicy: +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass +############################################> +<############################################ +Optional command line parameters: + $buildDirectory - specify an existing directory rather than generating a new unique one + $version - specify an exact version to download +############################################> Param( [Parameter()] - [String]$buildDirectory + [String]$buildDirectory, + [Parameter()] + [String]$version ) -if (!$PSBoundParameters.ContainsKey('buildDirectory')) { -# Use the current date/time stamp to create a unique directory if one is not specified. - $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' - $buildDirectory = $env:TEMP + "\" + $buildDate -} - +<############################################ +Define global parameters here such as known URLs etc. +############################################> +$installerVersion = "v0.0.1" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" - +$gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" @@ -44,25 +47,87 @@ if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchi $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" } - -Write-Output "Downloading installer to $arduinoCLIZip" - -$ProgressPreference = "SilentlyContinue" -Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip - $arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" -Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force -$ProgressPreference = "Continue" +<############################################ +Set default action for progress indicators, warnings, and errors +############################################> +$global:ProgressPreference = "SilentlyContinue" +$global:WarningPreference = "SilentlyContinue" +$global:ErrorActionPreference = "SilentlyContinue" +<############################################ +If $buildDirectory not provided, generate a new time/date stamp based directory to use +############################################> +if (!$PSBoundParameters.ContainsKey('buildDirectory')) { + $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' + $buildDirectory = $env:TEMP + "\" + $buildDate +} +$commandStationDirectory = $buildDirectory + "\CommandStation-EX" + +<############################################ +Write out intro message and prompt to continue +############################################> +@" +Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVersion) + +Current installer options: +- EX-CommandStation will be built in $commandStationDirectory +- Arduino CLI will be in $arduinoCLIDirectory +"@ + + +<############################################ +Create build directory if it doesn't exist, or fail +############################################> +if (!(Test-Path -PathType Container -Path $buildDirectory)) { + try { + New-Item -ItemType Directory -Path $buildDirectory | Out-Null + } + catch { + Write-Output "Could not create build directory $buildDirectory" + Exit + } +} + +<############################################ +See if we have the Arduino CLI already, otherwise download and extract it +############################################> +if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { + if (!(Test-Path -PathType Container -Path $arduinoCLIDirectory)) { + try { + New-Item -ItemType Directory -Path $arduinoCLIDirectory | Out-Null + } + catch { + Write-Output "Arduino CLI does not exist and cannot create directory $arduinoCLIDirectory" + Exit + } + } + Write-Output "Downloading and extracting Arduino CLI" + try { + Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip + } + catch { + Write-Output "Failed to download Arduino CLI" + Exit + } + try { + Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force + } + catch { + Write-Output "Failed to extract Arduino CLI" + } +} + +<# Write-Output "Installing using directory $buildDirectory" $tagList = Invoke-RestMethod -Uri $gitHubAPITags -# Example zip: https://github.com/DCC-EX/CommandStation-EX/archive/refs/tags/v4.2.36-Devel.zip - foreach ($tag in $tagList) { $version = $tag.ref.split("/")[2] - $versionURL = "https://github.com/DCC-EX/CommandStation-EX/archive/" + $tag.ref + $versionURL = $gitHubURLPrefix + $tag.ref Write-Output "$version : $versionURL" } +#> \ No newline at end of file From 18a992bf0867a64d335fce149bfffd053d4afb0e Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 6 Apr 2023 05:31:11 +1000 Subject: [PATCH 05/25] Start getting tag list --- installer.ps1 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/installer.ps1 b/installer.ps1 index d232187..296fb6d 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -120,6 +120,31 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } +<# +Get the list of tags +#> +try { + $tagList = Invoke-RestMethod -Uri $gitHubAPITags +} +catch { + Write-Output "Failed to obtain list of available EX-CommandStation versions" + Exit +} + +<# +Get latest two Prod and Devel releases, add to hash table for selection by user +#> +foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { + if ($tag.ref -Like "*Prod") { + Write-Output $tag.ref + } +} +foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { + if ($tag.ref -Like "*Devel") { + Write-Output $tag.ref + } +} + <# Write-Output "Installing using directory $buildDirectory" From de06c0ae3eb9422f8e4d85e7d0d5de432507658e Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:06:22 +1000 Subject: [PATCH 06/25] Working on CLI commands --- installer.ps1 | 129 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 14 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 296fb6d..b803e50 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -31,7 +31,7 @@ Param( [Parameter()] [String]$buildDirectory, [Parameter()] - [String]$version + [String]$configDirectory ) <############################################ @@ -120,30 +120,131 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } -<# +<############################################ Get the list of tags -#> +############################################> try { - $tagList = Invoke-RestMethod -Uri $gitHubAPITags + $gitHubTags = Invoke-RestMethod -Uri $gitHubAPITags } catch { Write-Output "Failed to obtain list of available EX-CommandStation versions" Exit } -<# -Get latest two Prod and Devel releases, add to hash table for selection by user -#> -foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { - if ($tag.ref -Like "*Prod") { - Write-Output $tag.ref +<############################################ +Get our GitHub tag list in a hash so we can sort by version numbers and extract just the ones we want +############################################> +$versionMatch = ".*?v(\d+)\.(\d+).(\d+)-(.*)" +$tagList = @{} +foreach ($tag in $gitHubTags) { + $tagHash = @{} + $tagHash["Ref"] = $tag.ref + $version = $tag.ref.split("/")[2] + $null = $version -match $versionMatch + $tagHash["Major"] = [int]$Matches[1] + $tagHash["Minor"] = [int]$Matches[2] + $tagHash["Patch"] = [int]$Matches[3] + $tagHash["Type"] = $Matches[4] + $tagList.Add($version, $tagHash) +} + +<############################################ +Get latest two Prod and Devel for user to select +############################################> +$userList = @{} +$prodCount = 1 +$devCount = 1 +$select = 1 +foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$_]["Minor"]},{$tagList[$_]["Patch"]} -Descending) { + if (($tagList[$tag]["Type"] -eq "Prod") -and $prodCount -le 2) { + $userList[$select] = $tag + $select++ + $prodCount++ + } elseif (($tagList[$tag]["Type"] -eq "Devel") -and $devCount -le 2) { + $userList[$select] = $tag + $select++ + $devCount++ } } -foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { - if ($tag.ref -Like "*Devel") { - Write-Output $tag.ref - } + +<############################################ +Display options for user to select and get the selection +############################################> +foreach ($selection in $userList.Keys | Sort-Object $selection) { + Write-Output "$selection - $($userList[$selection])" } +Write-Output "5 - Exit" +$userSelection = 0 +do { + [int]$userSelection = Read-Host "Select the version to install from the list above (1 - 5)" +} until ( + (($userSelection -ge 1) -and ($userSelection -le 5)) +) +if ($userSelection -eq 5) { + Write-Output "Exiting installer" + Exit +} else { + $downloadURL = $gitHubURLPrefix + $tagList[$userList[$userSelection]]["Ref"] + ".zip" +} + +<############################################ +Download the chosen version to the build directory +############################################> +$downladFile = $buildDirectory + "\CommandStation-EX.zip" +Write-Output "Downloading and extracting $($userList[$userSelection])" +try { + Invoke-WebRequest -Uri $downloadURL -OutFile $downladFile +} +catch { + Write-Output "Error downloading EX-CommandStation zip file" + Exit +} + +<############################################ +Extract and rename to CommandStation-EX to allow building +############################################> +try { + Expand-Archive -Path $downladFile -DestinationPath $buildDirectory -Force +} +catch { + Write-Output "Failed to extract EX-CommandStation zip file" + Exit +} + +$folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") +Write-Output $folderName +try { + Rename-Item -Path $folderName -NewName $commandStationDirectory +} +catch { + Write-Output "Could not rename folder" + Exit +} + +<############################################ +If config directory provided, copy files here +############################################> + + +<############################################ +Once files all together, identify available board(s) +############################################> +# Need to do an initial board list to download everything first +& $arduinoCLI board list | Out-Null +# Run again to generate the list of discovered boards +& $arduinoCLI board list --format json + +<############################################ +Get user to select board +############################################> + + + + +<############################################ +Upload the sketch to the selected board +############################################> +#$arduinoCLI upload -b fqbn -p port $commandStationDirectory <# Write-Output "Installing using directory $buildDirectory" From 9f212c27bf3ec38576fd7a4525db6bde85ee7e24 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 7 Apr 2023 08:06:32 +1000 Subject: [PATCH 07/25] Initial test working! --- installer.ps1 | 142 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 16 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index b803e50..1c47fc3 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -34,6 +34,24 @@ Param( [String]$configDirectory ) +<############################################ +List of supported devices with FQBN in case clones used that aren't detected +############################################> +$supportedDevices = @( + @{ + name = "Arduino Mega or Mega 2560" + fqbn = "arduino:avr:mega" + }, + @{ + name = "Arduino Nano" + fqbn = "arduino:avr:nano" + }, + @{ + name = "Arduino Uno" + fqbn = "arduino:avr:uno" + } +) + <############################################ Define global parameters here such as known URLs etc. ############################################> @@ -75,6 +93,9 @@ Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVers Current installer options: - EX-CommandStation will be built in $commandStationDirectory - Arduino CLI will be in $arduinoCLIDirectory + +Available EX-CommandStation versions: +------------------------------------- "@ @@ -176,7 +197,7 @@ foreach ($selection in $userList.Keys | Sort-Object $selection) { Write-Output "5 - Exit" $userSelection = 0 do { - [int]$userSelection = Read-Host "Select the version to install from the list above (1 - 5)" + [int]$userSelection = Read-Host "`r`nSelect the version to install from the list above (1 - 5)" } until ( (($userSelection -ge 1) -and ($userSelection -le 5)) ) @@ -200,6 +221,14 @@ catch { Exit } +<############################################ +If folder exists, bail out and tell user +############################################> +if (Test-Path -PathType Container -Path "$buildDirectory\CommandStation-EX") { + Write-Output "EX-CommandStation directory already exists, please ensure you have copied any user files then delete manually: $buildDirectory\CommandStation-EX" + Exit +} + <############################################ Extract and rename to CommandStation-EX to allow building ############################################> @@ -224,36 +253,117 @@ catch { <############################################ If config directory provided, copy files here ############################################> - +# To be done +# If exists copy config.h, myAutomation.h, myHal.cpp, mySetup.h <############################################ Once files all together, identify available board(s) ############################################> # Need to do an initial board list to download everything first -& $arduinoCLI board list | Out-Null -# Run again to generate the list of discovered boards -& $arduinoCLI board list --format json +try { + & $arduinoCLI board list | Out-Null +} +catch { + Write-Output "Failed to update Arduino CLI board list" + Exit +} +# Run again to generate the list of discovered boards into a custom object +try { + $boardList = & $arduinoCLI board list --format jsonmini | ConvertFrom-Json +} +catch { + Write-Output "Failed to obtain list of boards" + Exit +} <############################################ Get user to select board ############################################> +if ($boardList.count -eq 0) { + Write-Output "Could not find any attached devices, please ensure your device is plugged in to a USB port and Windows recognises it" + Exit +} else { +@" +Devices attached to COM ports: +------------------------------ +"@ + $boardSelect = 1 + foreach ($board in $boardList) { + if ($board.matching_boards.name) { + $boardName = $board.matching_boards.name + } else { + $boardName = "Unknown device" + } + $port = $board.port.address + Write-Output "$boardSelect - $boardName on port $port" + $boardSelect++ + } + Write-Output "$boardSelect - Exit" + $userSelection = 0 + do { + [int]$userSelection = Read-Host "`r`nSelect the device to use from the list above" + } until ( + (($userSelection -ge 1) -and ($userSelection -le ($boardList.count + 1))) + ) + if ($userSelection -eq ($boardList.count + 1)) { + Write-Output "Exiting installer" + Exit + } else { + $selectedBoard = $userSelection - 1 + } +} +<############################################ +If the board is unknown, need to choose which one +############################################> +if ($null -eq $boardList[$selectedBoard].matching_boards.name) { + Write-Output "The device selected is unknown, these boards are supported:`r`n" + $deviceSelect = 1 + foreach ($device in $supportedDevices) { + Write-Output "$deviceSelect - $($supportedDevices[$deviceSelect - 1].name)" + $deviceSelect++ + } + Write-Output "$deviceSelect - Exit" + $userSelection = 0 + do { + [int]$userSelection = Read-Host "Select the board type from the list above" + } until ( + (($userSelection -ge 1) -and ($userSelection -le ($supportedDevices.count + 1))) + ) + if ($userSelection -eq ($supportedDevices.count + 1)) { + Write-Output "Exiting installer" + Exit + } else { + $deviceName = $supportedDevices[$userSelection - 1].name + $deviceFQBN = $supportedDevices[$userSelection - 1].fqbn + } +} else { + $deviceName = $boardList[$selectedBoard].matching_boards.name + $deviceFQBN = $boardList[$selectedBoard].matching_boards.fqbn + $devicePort = $boardList[$selectedBoard].port.address +} <############################################ Upload the sketch to the selected board ############################################> #$arduinoCLI upload -b fqbn -p port $commandStationDirectory - -<# -Write-Output "Installing using directory $buildDirectory" - -$tagList = Invoke-RestMethod -Uri $gitHubAPITags - -foreach ($tag in $tagList) { - $version = $tag.ref.split("/")[2] - $versionURL = $gitHubURLPrefix + $tag.ref - Write-Output "$version : $versionURL" +Write-Output "Compiling for $deviceName" +try { + $output = & $arduinoCLI compile -b $deviceFQBN $commandStationDirectory --format jsonmini | ConvertFrom-Json } -#> \ No newline at end of file +catch { + Write-Output "Failed to compile" + Exit +} +Write-Output "$output" +Write-Output "Now uploading to $deviceName on port $devicePort" +try { + $output = & $arduinoCLI upload -t -b $deviceFQBN -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json +} +catch { + Write-Output "Failed to upload" + Exit +} +Write-Output "$output" From ef3d36ae25027bc0d5e93a2573fc22405dce4b05 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:48:11 +1000 Subject: [PATCH 08/25] Figuring out commands --- installer.ps1 | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 installer.ps1 diff --git a/installer.ps1 b/installer.ps1 new file mode 100644 index 0000000..e740d80 --- /dev/null +++ b/installer.ps1 @@ -0,0 +1,53 @@ +# +# © 2023 Peter Cole +# +# This file is part of EX-CommandStation +# +# This is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# It is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CommandStation. If not, see . +# + +# 32/64 bit Win installer +# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip +# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip + +param( + [Parameter()] + [String]$BUILD_DIR +) + +if (!$PSBoundParameters.ContainsKey('BUILD_DIR')) { +# Use the current date/time stamp to create a unique directory if one is not specified. + $BUILD_DATE = Get-Date -Format 'yyyyMMdd-HHmmss' + $BUILD_DIR = $env:TEMP + "\" + $BUILD_DATE +} + +if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { + $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" +} else { + $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" +} + +Write-Output "Downloading installer to $OUTFILE" + +$ProgressPreference = "SilentlyContinue" +Invoke-WebRequest -Uri $URL -OutFile $OUTFILE + +$CLI_INSTALL = $env:TEMP + "\" + "arduino-cli_installer" + +Expand-Archive -Path $OUTFILE -DestinationPath $CLI_INSTALL -Force +$ProgressPreference = "Continue" + +Write-Output "Installing using directory $BUILD_DIR" \ No newline at end of file From 3a3071f35bd4ddfdaea854b560cd34f326b03029 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 4 Apr 2023 19:30:59 +1000 Subject: [PATCH 09/25] Got tag list --- installer.ps1 | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index e740d80..defccd6 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -21,33 +21,39 @@ # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" + param( [Parameter()] - [String]$BUILD_DIR + [String]$buildDirectory ) -if (!$PSBoundParameters.ContainsKey('BUILD_DIR')) { +if (!$PSBoundParameters.ContainsKey('buildDirectory')) { # Use the current date/time stamp to create a unique directory if one is not specified. - $BUILD_DATE = Get-Date -Format 'yyyyMMdd-HHmmss' - $BUILD_DIR = $env:TEMP + "\" + $BUILD_DATE + $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' + $buildDirectory = $env:TEMP + "\" + $buildDate } if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { - $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" - $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" } else { - $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" - $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" } -Write-Output "Downloading installer to $OUTFILE" +Write-Output "Downloading installer to $arduinoCLIZip" $ProgressPreference = "SilentlyContinue" -Invoke-WebRequest -Uri $URL -OutFile $OUTFILE +Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip -$CLI_INSTALL = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" -Expand-Archive -Path $OUTFILE -DestinationPath $CLI_INSTALL -Force +Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force $ProgressPreference = "Continue" -Write-Output "Installing using directory $BUILD_DIR" \ No newline at end of file +Write-Output "Installing using directory $buildDirectory" + +foreach ($tag in Invoke-RestMethod -Uri $gitHubAPITags | Format-List -Property ref) { + $tag.getType() +} From 61db37c7ea2717b4c2ddfd952bdab87cf4e52a31 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Wed, 5 Apr 2023 05:30:00 +1000 Subject: [PATCH 10/25] Got tag version and URL --- installer.ps1 | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index defccd6..3b95535 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -20,10 +20,11 @@ # 32/64 bit Win installer # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip +# +# For script errors set ExecutionPolicy: +# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass -$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" - -param( +Param( [Parameter()] [String]$buildDirectory ) @@ -34,6 +35,8 @@ if (!$PSBoundParameters.ContainsKey('buildDirectory')) { $buildDirectory = $env:TEMP + "\" + $buildDate } +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" + if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" @@ -54,6 +57,12 @@ $ProgressPreference = "Continue" Write-Output "Installing using directory $buildDirectory" -foreach ($tag in Invoke-RestMethod -Uri $gitHubAPITags | Format-List -Property ref) { - $tag.getType() +$tagList = Invoke-RestMethod -Uri $gitHubAPITags + +# Example zip: https://github.com/DCC-EX/CommandStation-EX/archive/refs/tags/v4.2.36-Devel.zip + +foreach ($tag in $tagList) { + $version = $tag.ref.split("/")[2] + $versionURL = "https://github.com/DCC-EX/CommandStation-EX/archive/" + $tag.ref + Write-Output "$version : $versionURL" } From d2c7e7fb8d9a536a00536e5c7aaa0f3130b8b9f8 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:54:48 +1000 Subject: [PATCH 11/25] Working on logic --- installer.ps1 | 119 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 3b95535..d232187 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -1,4 +1,4 @@ -# +<# # © 2023 Peter Cole # # This file is part of EX-CommandStation @@ -15,28 +15,31 @@ # # You should have received a copy of the GNU General Public License # along with CommandStation. If not, see . -# +#> -# 32/64 bit Win installer -# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip -# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip -# -# For script errors set ExecutionPolicy: -# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass +<############################################ +For script errors set ExecutionPolicy: +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass +############################################> +<############################################ +Optional command line parameters: + $buildDirectory - specify an existing directory rather than generating a new unique one + $version - specify an exact version to download +############################################> Param( [Parameter()] - [String]$buildDirectory + [String]$buildDirectory, + [Parameter()] + [String]$version ) -if (!$PSBoundParameters.ContainsKey('buildDirectory')) { -# Use the current date/time stamp to create a unique directory if one is not specified. - $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' - $buildDirectory = $env:TEMP + "\" + $buildDate -} - +<############################################ +Define global parameters here such as known URLs etc. +############################################> +$installerVersion = "v0.0.1" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" - +$gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" @@ -44,25 +47,87 @@ if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchi $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" } - -Write-Output "Downloading installer to $arduinoCLIZip" - -$ProgressPreference = "SilentlyContinue" -Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip - $arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" -Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force -$ProgressPreference = "Continue" +<############################################ +Set default action for progress indicators, warnings, and errors +############################################> +$global:ProgressPreference = "SilentlyContinue" +$global:WarningPreference = "SilentlyContinue" +$global:ErrorActionPreference = "SilentlyContinue" +<############################################ +If $buildDirectory not provided, generate a new time/date stamp based directory to use +############################################> +if (!$PSBoundParameters.ContainsKey('buildDirectory')) { + $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' + $buildDirectory = $env:TEMP + "\" + $buildDate +} +$commandStationDirectory = $buildDirectory + "\CommandStation-EX" + +<############################################ +Write out intro message and prompt to continue +############################################> +@" +Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVersion) + +Current installer options: +- EX-CommandStation will be built in $commandStationDirectory +- Arduino CLI will be in $arduinoCLIDirectory +"@ + + +<############################################ +Create build directory if it doesn't exist, or fail +############################################> +if (!(Test-Path -PathType Container -Path $buildDirectory)) { + try { + New-Item -ItemType Directory -Path $buildDirectory | Out-Null + } + catch { + Write-Output "Could not create build directory $buildDirectory" + Exit + } +} + +<############################################ +See if we have the Arduino CLI already, otherwise download and extract it +############################################> +if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { + if (!(Test-Path -PathType Container -Path $arduinoCLIDirectory)) { + try { + New-Item -ItemType Directory -Path $arduinoCLIDirectory | Out-Null + } + catch { + Write-Output "Arduino CLI does not exist and cannot create directory $arduinoCLIDirectory" + Exit + } + } + Write-Output "Downloading and extracting Arduino CLI" + try { + Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip + } + catch { + Write-Output "Failed to download Arduino CLI" + Exit + } + try { + Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force + } + catch { + Write-Output "Failed to extract Arduino CLI" + } +} + +<# Write-Output "Installing using directory $buildDirectory" $tagList = Invoke-RestMethod -Uri $gitHubAPITags -# Example zip: https://github.com/DCC-EX/CommandStation-EX/archive/refs/tags/v4.2.36-Devel.zip - foreach ($tag in $tagList) { $version = $tag.ref.split("/")[2] - $versionURL = "https://github.com/DCC-EX/CommandStation-EX/archive/" + $tag.ref + $versionURL = $gitHubURLPrefix + $tag.ref Write-Output "$version : $versionURL" } +#> \ No newline at end of file From 72ceb6391335898ed1d7dbd81a55772be592622f Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 6 Apr 2023 05:31:11 +1000 Subject: [PATCH 12/25] Start getting tag list --- installer.ps1 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/installer.ps1 b/installer.ps1 index d232187..296fb6d 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -120,6 +120,31 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } +<# +Get the list of tags +#> +try { + $tagList = Invoke-RestMethod -Uri $gitHubAPITags +} +catch { + Write-Output "Failed to obtain list of available EX-CommandStation versions" + Exit +} + +<# +Get latest two Prod and Devel releases, add to hash table for selection by user +#> +foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { + if ($tag.ref -Like "*Prod") { + Write-Output $tag.ref + } +} +foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { + if ($tag.ref -Like "*Devel") { + Write-Output $tag.ref + } +} + <# Write-Output "Installing using directory $buildDirectory" From 91bc9df44eb6d9630af0013e5e4f55ba9943d517 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:06:22 +1000 Subject: [PATCH 13/25] Working on CLI commands --- installer.ps1 | 129 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 14 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 296fb6d..b803e50 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -31,7 +31,7 @@ Param( [Parameter()] [String]$buildDirectory, [Parameter()] - [String]$version + [String]$configDirectory ) <############################################ @@ -120,30 +120,131 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } -<# +<############################################ Get the list of tags -#> +############################################> try { - $tagList = Invoke-RestMethod -Uri $gitHubAPITags + $gitHubTags = Invoke-RestMethod -Uri $gitHubAPITags } catch { Write-Output "Failed to obtain list of available EX-CommandStation versions" Exit } -<# -Get latest two Prod and Devel releases, add to hash table for selection by user -#> -foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { - if ($tag.ref -Like "*Prod") { - Write-Output $tag.ref +<############################################ +Get our GitHub tag list in a hash so we can sort by version numbers and extract just the ones we want +############################################> +$versionMatch = ".*?v(\d+)\.(\d+).(\d+)-(.*)" +$tagList = @{} +foreach ($tag in $gitHubTags) { + $tagHash = @{} + $tagHash["Ref"] = $tag.ref + $version = $tag.ref.split("/")[2] + $null = $version -match $versionMatch + $tagHash["Major"] = [int]$Matches[1] + $tagHash["Minor"] = [int]$Matches[2] + $tagHash["Patch"] = [int]$Matches[3] + $tagHash["Type"] = $Matches[4] + $tagList.Add($version, $tagHash) +} + +<############################################ +Get latest two Prod and Devel for user to select +############################################> +$userList = @{} +$prodCount = 1 +$devCount = 1 +$select = 1 +foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$_]["Minor"]},{$tagList[$_]["Patch"]} -Descending) { + if (($tagList[$tag]["Type"] -eq "Prod") -and $prodCount -le 2) { + $userList[$select] = $tag + $select++ + $prodCount++ + } elseif (($tagList[$tag]["Type"] -eq "Devel") -and $devCount -le 2) { + $userList[$select] = $tag + $select++ + $devCount++ } } -foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { - if ($tag.ref -Like "*Devel") { - Write-Output $tag.ref - } + +<############################################ +Display options for user to select and get the selection +############################################> +foreach ($selection in $userList.Keys | Sort-Object $selection) { + Write-Output "$selection - $($userList[$selection])" } +Write-Output "5 - Exit" +$userSelection = 0 +do { + [int]$userSelection = Read-Host "Select the version to install from the list above (1 - 5)" +} until ( + (($userSelection -ge 1) -and ($userSelection -le 5)) +) +if ($userSelection -eq 5) { + Write-Output "Exiting installer" + Exit +} else { + $downloadURL = $gitHubURLPrefix + $tagList[$userList[$userSelection]]["Ref"] + ".zip" +} + +<############################################ +Download the chosen version to the build directory +############################################> +$downladFile = $buildDirectory + "\CommandStation-EX.zip" +Write-Output "Downloading and extracting $($userList[$userSelection])" +try { + Invoke-WebRequest -Uri $downloadURL -OutFile $downladFile +} +catch { + Write-Output "Error downloading EX-CommandStation zip file" + Exit +} + +<############################################ +Extract and rename to CommandStation-EX to allow building +############################################> +try { + Expand-Archive -Path $downladFile -DestinationPath $buildDirectory -Force +} +catch { + Write-Output "Failed to extract EX-CommandStation zip file" + Exit +} + +$folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") +Write-Output $folderName +try { + Rename-Item -Path $folderName -NewName $commandStationDirectory +} +catch { + Write-Output "Could not rename folder" + Exit +} + +<############################################ +If config directory provided, copy files here +############################################> + + +<############################################ +Once files all together, identify available board(s) +############################################> +# Need to do an initial board list to download everything first +& $arduinoCLI board list | Out-Null +# Run again to generate the list of discovered boards +& $arduinoCLI board list --format json + +<############################################ +Get user to select board +############################################> + + + + +<############################################ +Upload the sketch to the selected board +############################################> +#$arduinoCLI upload -b fqbn -p port $commandStationDirectory <# Write-Output "Installing using directory $buildDirectory" From 751b46b6bb4ceef91d31d018ad4b6c378e7fe8d0 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 7 Apr 2023 08:06:32 +1000 Subject: [PATCH 14/25] Initial test working! --- installer.ps1 | 142 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 16 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index b803e50..1c47fc3 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -34,6 +34,24 @@ Param( [String]$configDirectory ) +<############################################ +List of supported devices with FQBN in case clones used that aren't detected +############################################> +$supportedDevices = @( + @{ + name = "Arduino Mega or Mega 2560" + fqbn = "arduino:avr:mega" + }, + @{ + name = "Arduino Nano" + fqbn = "arduino:avr:nano" + }, + @{ + name = "Arduino Uno" + fqbn = "arduino:avr:uno" + } +) + <############################################ Define global parameters here such as known URLs etc. ############################################> @@ -75,6 +93,9 @@ Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVers Current installer options: - EX-CommandStation will be built in $commandStationDirectory - Arduino CLI will be in $arduinoCLIDirectory + +Available EX-CommandStation versions: +------------------------------------- "@ @@ -176,7 +197,7 @@ foreach ($selection in $userList.Keys | Sort-Object $selection) { Write-Output "5 - Exit" $userSelection = 0 do { - [int]$userSelection = Read-Host "Select the version to install from the list above (1 - 5)" + [int]$userSelection = Read-Host "`r`nSelect the version to install from the list above (1 - 5)" } until ( (($userSelection -ge 1) -and ($userSelection -le 5)) ) @@ -200,6 +221,14 @@ catch { Exit } +<############################################ +If folder exists, bail out and tell user +############################################> +if (Test-Path -PathType Container -Path "$buildDirectory\CommandStation-EX") { + Write-Output "EX-CommandStation directory already exists, please ensure you have copied any user files then delete manually: $buildDirectory\CommandStation-EX" + Exit +} + <############################################ Extract and rename to CommandStation-EX to allow building ############################################> @@ -224,36 +253,117 @@ catch { <############################################ If config directory provided, copy files here ############################################> - +# To be done +# If exists copy config.h, myAutomation.h, myHal.cpp, mySetup.h <############################################ Once files all together, identify available board(s) ############################################> # Need to do an initial board list to download everything first -& $arduinoCLI board list | Out-Null -# Run again to generate the list of discovered boards -& $arduinoCLI board list --format json +try { + & $arduinoCLI board list | Out-Null +} +catch { + Write-Output "Failed to update Arduino CLI board list" + Exit +} +# Run again to generate the list of discovered boards into a custom object +try { + $boardList = & $arduinoCLI board list --format jsonmini | ConvertFrom-Json +} +catch { + Write-Output "Failed to obtain list of boards" + Exit +} <############################################ Get user to select board ############################################> +if ($boardList.count -eq 0) { + Write-Output "Could not find any attached devices, please ensure your device is plugged in to a USB port and Windows recognises it" + Exit +} else { +@" +Devices attached to COM ports: +------------------------------ +"@ + $boardSelect = 1 + foreach ($board in $boardList) { + if ($board.matching_boards.name) { + $boardName = $board.matching_boards.name + } else { + $boardName = "Unknown device" + } + $port = $board.port.address + Write-Output "$boardSelect - $boardName on port $port" + $boardSelect++ + } + Write-Output "$boardSelect - Exit" + $userSelection = 0 + do { + [int]$userSelection = Read-Host "`r`nSelect the device to use from the list above" + } until ( + (($userSelection -ge 1) -and ($userSelection -le ($boardList.count + 1))) + ) + if ($userSelection -eq ($boardList.count + 1)) { + Write-Output "Exiting installer" + Exit + } else { + $selectedBoard = $userSelection - 1 + } +} +<############################################ +If the board is unknown, need to choose which one +############################################> +if ($null -eq $boardList[$selectedBoard].matching_boards.name) { + Write-Output "The device selected is unknown, these boards are supported:`r`n" + $deviceSelect = 1 + foreach ($device in $supportedDevices) { + Write-Output "$deviceSelect - $($supportedDevices[$deviceSelect - 1].name)" + $deviceSelect++ + } + Write-Output "$deviceSelect - Exit" + $userSelection = 0 + do { + [int]$userSelection = Read-Host "Select the board type from the list above" + } until ( + (($userSelection -ge 1) -and ($userSelection -le ($supportedDevices.count + 1))) + ) + if ($userSelection -eq ($supportedDevices.count + 1)) { + Write-Output "Exiting installer" + Exit + } else { + $deviceName = $supportedDevices[$userSelection - 1].name + $deviceFQBN = $supportedDevices[$userSelection - 1].fqbn + } +} else { + $deviceName = $boardList[$selectedBoard].matching_boards.name + $deviceFQBN = $boardList[$selectedBoard].matching_boards.fqbn + $devicePort = $boardList[$selectedBoard].port.address +} <############################################ Upload the sketch to the selected board ############################################> #$arduinoCLI upload -b fqbn -p port $commandStationDirectory - -<# -Write-Output "Installing using directory $buildDirectory" - -$tagList = Invoke-RestMethod -Uri $gitHubAPITags - -foreach ($tag in $tagList) { - $version = $tag.ref.split("/")[2] - $versionURL = $gitHubURLPrefix + $tag.ref - Write-Output "$version : $versionURL" +Write-Output "Compiling for $deviceName" +try { + $output = & $arduinoCLI compile -b $deviceFQBN $commandStationDirectory --format jsonmini | ConvertFrom-Json } -#> \ No newline at end of file +catch { + Write-Output "Failed to compile" + Exit +} +Write-Output "$output" +Write-Output "Now uploading to $deviceName on port $devicePort" +try { + $output = & $arduinoCLI upload -t -b $deviceFQBN -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json +} +catch { + Write-Output "Failed to upload" + Exit +} +Write-Output "$output" From 2e518fcac229a18efe6f4a0c6f9610198d62b03b Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 7 Apr 2023 19:30:15 +1000 Subject: [PATCH 15/25] Enable using existing configs --- installer.ps1 | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 1c47fc3..0b4e94b 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -55,7 +55,7 @@ $supportedDevices = @( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.1" +$installerVersion = "v0.0.2" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { @@ -253,8 +253,18 @@ catch { <############################################ If config directory provided, copy files here ############################################> -# To be done -# If exists copy config.h, myAutomation.h, myHal.cpp, mySetup.h +$configFiles = @("config.h", "myAutomation.h", "myHal.cpp", "mySetup.h") +if ($PSBoundParameters.ContainsKey('configDirectory')) { + if (Test-Path -PathType Container -Path $configDirectory) { + foreach ($file in $configFiles) { + if (Test-Path -PathType Leaf -Path "$configDirectory\$file") { + Copy-Item -Path "$configDirectory\$file" -Destination "$commandStationDirectory\$file" + } + } + } else { + Write-Output "Provided configuration directory $configDirectory does not exist, skipping" + } +} <############################################ Once files all together, identify available board(s) @@ -338,6 +348,7 @@ if ($null -eq $boardList[$selectedBoard].matching_boards.name) { } else { $deviceName = $supportedDevices[$userSelection - 1].name $deviceFQBN = $supportedDevices[$userSelection - 1].fqbn + $devicePort = $boardList[$selectedBoard].port.address } } else { $deviceName = $boardList[$selectedBoard].matching_boards.name @@ -349,21 +360,13 @@ if ($null -eq $boardList[$selectedBoard].matching_boards.name) { Upload the sketch to the selected board ############################################> #$arduinoCLI upload -b fqbn -p port $commandStationDirectory -Write-Output "Compiling for $deviceName" +Write-Output "Compiling and uploading to $deviceName on $devicePort" +Write-Output "& $arduinoCLI compile -b $deviceFQBN -u -t -p $devicePort $commandStationDirectory" try { - $output = & $arduinoCLI compile -b $deviceFQBN $commandStationDirectory --format jsonmini | ConvertFrom-Json + $output = & $arduinoCLI compile -b $deviceFQBN -u -t -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json } catch { Write-Output "Failed to compile" Exit } Write-Output "$output" -Write-Output "Now uploading to $deviceName on port $devicePort" -try { - $output = & $arduinoCLI upload -t -b $deviceFQBN -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json -} -catch { - Write-Output "Failed to upload" - Exit -} -Write-Output "$output" From a100d709ce5ef60e5c87f4ba599e5b222f2fc23b Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 8 Apr 2023 06:22:09 +1000 Subject: [PATCH 16/25] Install core library, add batch wrapper --- install_via_powershell.cmd | 15 +++++++++++++++ installer.ps1 | 29 +++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 install_via_powershell.cmd diff --git a/install_via_powershell.cmd b/install_via_powershell.cmd new file mode 100644 index 0000000..f367b21 --- /dev/null +++ b/install_via_powershell.cmd @@ -0,0 +1,15 @@ +@ECHO OFF + +FOR /f "tokens=*" %%a IN ('powershell Get-ExecutionPolicy -Scope CurrentUser') DO SET PS_POLICY=%%a + +IF NOT %PS_POLICY=="Bypass" ( + powershell Set-ExecutionPolicy -Scope CurrentUser Bypass +) + +powershell %~dp0%installer.ps1 + +IF NOT %PS_POLICY=="Bypass" ( + powershell Set-ExecutionPolicy -Scope CurrentUser %PS_POLICY% +) + +PAUSE \ No newline at end of file diff --git a/installer.ps1 b/installer.ps1 index 0b4e94b..65b0a61 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -55,7 +55,7 @@ $supportedDevices = @( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.2" +$installerVersion = "v0.0.3" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { @@ -241,7 +241,6 @@ catch { } $folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") -Write-Output $folderName try { Rename-Item -Path $folderName -NewName $commandStationDirectory } @@ -356,12 +355,24 @@ if ($null -eq $boardList[$selectedBoard].matching_boards.name) { $devicePort = $boardList[$selectedBoard].port.address } +<############################################ +Install core libraries for the platform +############################################> +$platformArray = $deviceFQBN.split(":") +$platform = $platformArray[0] + ":" + $platformArray[1] +try { + & $arduinoCLI core install $platform +} +catch { + Write-Output "Error install core libraries" + Exit +} + <############################################ Upload the sketch to the selected board ############################################> #$arduinoCLI upload -b fqbn -p port $commandStationDirectory Write-Output "Compiling and uploading to $deviceName on $devicePort" -Write-Output "& $arduinoCLI compile -b $deviceFQBN -u -t -p $devicePort $commandStationDirectory" try { $output = & $arduinoCLI compile -b $deviceFQBN -u -t -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json } @@ -369,4 +380,14 @@ catch { Write-Output "Failed to compile" Exit } -Write-Output "$output" +if ($output.success -eq "True") { + Write-Output "`r`nCongratulations! DCC-EX EX-CommandStation $($userList[$userSelection]) has been installed on your $deviceName`r`n" +} else { + Write-Output "`r`nThere was an error installing $($userList[$userSelection]) on your $($deviceName), please take note of the errors provided:`r`n" + if ($null -ne $output.compiler_err) { + Write-Output "Compiler error: $($output.compiler_err)`r`n" + } + if ($null -ne $output.builder_result) { + Write-Output "Builder result: $($output.builder_result)`r`n" + } +} From 4d236446b039047ba706532f7d34fb3fe973d375 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 8 Apr 2023 06:57:09 +1000 Subject: [PATCH 17/25] Add core index update --- installer.ps1 | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 65b0a61..a92edae 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -34,6 +34,22 @@ Param( [String]$configDirectory ) +<############################################ +Define global parameters here such as known URLs etc. +############################################> +$installerVersion = "v0.0.4" +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" +$gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" +if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" +} else { + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" +} +$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" + <############################################ List of supported devices with FQBN in case clones used that aren't detected ############################################> @@ -52,22 +68,6 @@ $supportedDevices = @( } ) -<############################################ -Define global parameters here such as known URLs etc. -############################################> -$installerVersion = "v0.0.3" -$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" -$gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" -if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { - $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" - $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" -} else { - $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" - $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" -} -$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" -$arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" - <############################################ Set default action for progress indicators, warnings, and errors ############################################> @@ -266,9 +266,17 @@ if ($PSBoundParameters.ContainsKey('configDirectory')) { } <############################################ -Once files all together, identify available board(s) +Make sure Arduino CLI core index updated and list of boards populated ############################################> # Need to do an initial board list to download everything first +try { + & $arduinoCLI core update-index | Out-Null +} +catch { + Write-Output "Failed to update Arduino CLI core index" + Exit +} +# Need to do an initial board list to download everything first try { & $arduinoCLI board list | Out-Null } @@ -276,7 +284,10 @@ catch { Write-Output "Failed to update Arduino CLI board list" Exit } -# Run again to generate the list of discovered boards into a custom object + +<############################################ +Once files all together, identify available board(s) +############################################> try { $boardList = & $arduinoCLI board list --format jsonmini | ConvertFrom-Json } From fe035f4096d2723e0f0336fd45a4a5ccbf0eb928 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 8 Apr 2023 08:38:02 +1000 Subject: [PATCH 18/25] Move from temp to user home dir --- installer.ps1 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index a92edae..ac2ec9e 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -37,17 +37,18 @@ Param( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.4" +$installerVersion = "v0.0.5" +$userDirectory = $env:USERPROFILE + "\" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" - $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIZip = $userDirectory + "Downloads\" + "arduino-cli_latest_Windows_64bit.zip" } else { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" - $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIZip = $userDirectory + "Downloads\" + "arduino-cli_latest_Windows_32bit.zip" } -$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLIDirectory = $userDirectory + "arduino-cli_installer" $arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" <############################################ @@ -80,7 +81,7 @@ If $buildDirectory not provided, generate a new time/date stamp based directory ############################################> if (!$PSBoundParameters.ContainsKey('buildDirectory')) { $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' - $buildDirectory = $env:TEMP + "\" + $buildDate + $buildDirectory = $userDirectory + "EX-CommandStation-Installer\" + $buildDate } $commandStationDirectory = $buildDirectory + "\CommandStation-EX" @@ -94,8 +95,6 @@ Current installer options: - EX-CommandStation will be built in $commandStationDirectory - Arduino CLI will be in $arduinoCLIDirectory -Available EX-CommandStation versions: -------------------------------------- "@ @@ -191,6 +190,11 @@ foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$ <############################################ Display options for user to select and get the selection ############################################> +@" + +Available EX-CommandStation versions: +------------------------------------- +"@ foreach ($selection in $userList.Keys | Sort-Object $selection) { Write-Output "$selection - $($userList[$selection])" } From e7d9626a729e52fb05c1cbc2f86b75cdde63f48e Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 8 Apr 2023 08:39:42 +1000 Subject: [PATCH 19/25] Fix CLI directory name --- installer.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index ac2ec9e..cb3bf73 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -37,7 +37,7 @@ Param( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.5" +$installerVersion = "v0.0.6" $userDirectory = $env:USERPROFILE + "\" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" @@ -48,7 +48,7 @@ if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchi $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" $arduinoCLIZip = $userDirectory + "Downloads\" + "arduino-cli_latest_Windows_32bit.zip" } -$arduinoCLIDirectory = $userDirectory + "arduino-cli_installer" +$arduinoCLIDirectory = $userDirectory + "arduino-cli" $arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" <############################################ From 1d29be9de6d604c625d17843a0cc5d83f4996f43 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Mon, 10 Apr 2023 19:52:31 +1000 Subject: [PATCH 20/25] Working on 0.0.7 --- installer.ps1 | 395 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 252 insertions(+), 143 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index cb3bf73..56eeec0 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -25,7 +25,7 @@ Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass <############################################ Optional command line parameters: $buildDirectory - specify an existing directory rather than generating a new unique one - $version - specify an exact version to download + $configDirectory - specify a directory containing existing files as per $configFiles ############################################> Param( [Parameter()] @@ -37,7 +37,9 @@ Param( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.6" +$installerVersion = "v0.0.7" +$configFiles = @("config.h", "myAutomation.h", "myHal.cpp", "mySetup.h") +$wifiBoards = @("arduino:avr:mega", "esp32:esp32:esp32") $userDirectory = $env:USERPROFILE + "\" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" @@ -66,6 +68,47 @@ $supportedDevices = @( @{ name = "Arduino Uno" fqbn = "arduino:avr:uno" + }, + @{ + name = "ESP32 Dev Module" + fqbn = "esp32:esp32:esp32" + } +) + +<############################################ +List of supported displays and scroll modes +############################################> +$displayList = @( + @{ + option = "LCD 16 columns x 2 rows" + configLine = "#define LCD_DRIVER 0x27,16,2" + }, + @{ + option = "LCD 16 columns x 4 rows" + configLine = "#define LCD_DRIVER 0x27,16,4" + }, + @{ + option = "OLED 128 x 32" + configLine = "#define OLED_DRIVER 128,32,0x3c" + }, + @{ + option = "OLED 128 x 64" + configLine = "#define OLED_DRIVER 128,64,0x3c" + } +) + +$scrollList = @( + @{ + mode = "Continuous - fill screen if possible" + configLine = "#define SCROLLMODE 0" + }, + @{ + mode = "Page (default) - alternate between pages" + configLine = "#define SCROLLMODE 1" + }, + @{ + mode = "Row - move up one row at a time" + configLine = "#define SCROLLMODE 2" } ) @@ -92,23 +135,27 @@ Write out intro message and prompt to continue Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVersion) Current installer options: + - EX-CommandStation will be built in $commandStationDirectory -- Arduino CLI will be in $arduinoCLIDirectory +- Arduino CLI will downloaded and extracted to $arduinoCLIDirectory + +Before continuing, please ensure: + +- Your computer is connected to the internet +- The device you wish to install EX-CommandStation on is connected to a USB port + +This installer will obtain the Arduino CLI (if not already present), and then download and install your chosen version of EX-CommandStation "@ - <############################################ -Create build directory if it doesn't exist, or fail +Prompt user to confirm all is ready to proceed ############################################> -if (!(Test-Path -PathType Container -Path $buildDirectory)) { - try { - New-Item -ItemType Directory -Path $buildDirectory | Out-Null - } - catch { - Write-Output "Could not create build directory $buildDirectory" - Exit - } +$confirmation = Read-Host "Enter 'Y' or 'y' then press to confirm you are ready to proceed, any other key to exit" +if ($confirmation -ne "Y" -and $confirmation -ne "y") { + Exit +} else { + Write-Output "Proceeding to obtain the Arduino CLI..." } <############################################ @@ -140,135 +187,6 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } -<############################################ -Get the list of tags -############################################> -try { - $gitHubTags = Invoke-RestMethod -Uri $gitHubAPITags -} -catch { - Write-Output "Failed to obtain list of available EX-CommandStation versions" - Exit -} - -<############################################ -Get our GitHub tag list in a hash so we can sort by version numbers and extract just the ones we want -############################################> -$versionMatch = ".*?v(\d+)\.(\d+).(\d+)-(.*)" -$tagList = @{} -foreach ($tag in $gitHubTags) { - $tagHash = @{} - $tagHash["Ref"] = $tag.ref - $version = $tag.ref.split("/")[2] - $null = $version -match $versionMatch - $tagHash["Major"] = [int]$Matches[1] - $tagHash["Minor"] = [int]$Matches[2] - $tagHash["Patch"] = [int]$Matches[3] - $tagHash["Type"] = $Matches[4] - $tagList.Add($version, $tagHash) -} - -<############################################ -Get latest two Prod and Devel for user to select -############################################> -$userList = @{} -$prodCount = 1 -$devCount = 1 -$select = 1 -foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$_]["Minor"]},{$tagList[$_]["Patch"]} -Descending) { - if (($tagList[$tag]["Type"] -eq "Prod") -and $prodCount -le 2) { - $userList[$select] = $tag - $select++ - $prodCount++ - } elseif (($tagList[$tag]["Type"] -eq "Devel") -and $devCount -le 2) { - $userList[$select] = $tag - $select++ - $devCount++ - } -} - -<############################################ -Display options for user to select and get the selection -############################################> -@" - -Available EX-CommandStation versions: -------------------------------------- -"@ -foreach ($selection in $userList.Keys | Sort-Object $selection) { - Write-Output "$selection - $($userList[$selection])" -} -Write-Output "5 - Exit" -$userSelection = 0 -do { - [int]$userSelection = Read-Host "`r`nSelect the version to install from the list above (1 - 5)" -} until ( - (($userSelection -ge 1) -and ($userSelection -le 5)) -) -if ($userSelection -eq 5) { - Write-Output "Exiting installer" - Exit -} else { - $downloadURL = $gitHubURLPrefix + $tagList[$userList[$userSelection]]["Ref"] + ".zip" -} - -<############################################ -Download the chosen version to the build directory -############################################> -$downladFile = $buildDirectory + "\CommandStation-EX.zip" -Write-Output "Downloading and extracting $($userList[$userSelection])" -try { - Invoke-WebRequest -Uri $downloadURL -OutFile $downladFile -} -catch { - Write-Output "Error downloading EX-CommandStation zip file" - Exit -} - -<############################################ -If folder exists, bail out and tell user -############################################> -if (Test-Path -PathType Container -Path "$buildDirectory\CommandStation-EX") { - Write-Output "EX-CommandStation directory already exists, please ensure you have copied any user files then delete manually: $buildDirectory\CommandStation-EX" - Exit -} - -<############################################ -Extract and rename to CommandStation-EX to allow building -############################################> -try { - Expand-Archive -Path $downladFile -DestinationPath $buildDirectory -Force -} -catch { - Write-Output "Failed to extract EX-CommandStation zip file" - Exit -} - -$folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") -try { - Rename-Item -Path $folderName -NewName $commandStationDirectory -} -catch { - Write-Output "Could not rename folder" - Exit -} - -<############################################ -If config directory provided, copy files here -############################################> -$configFiles = @("config.h", "myAutomation.h", "myHal.cpp", "mySetup.h") -if ($PSBoundParameters.ContainsKey('configDirectory')) { - if (Test-Path -PathType Container -Path $configDirectory) { - foreach ($file in $configFiles) { - if (Test-Path -PathType Leaf -Path "$configDirectory\$file") { - Copy-Item -Path "$configDirectory\$file" -Destination "$commandStationDirectory\$file" - } - } - } else { - Write-Output "Provided configuration directory $configDirectory does not exist, skipping" - } -} - <############################################ Make sure Arduino CLI core index updated and list of boards populated ############################################> @@ -290,7 +208,7 @@ catch { } <############################################ -Once files all together, identify available board(s) +Identify available board(s) ############################################> try { $boardList = & $arduinoCLI board list --format jsonmini | ConvertFrom-Json @@ -370,6 +288,197 @@ if ($null -eq $boardList[$selectedBoard].matching_boards.name) { $devicePort = $boardList[$selectedBoard].port.address } +<############################################ +Get the list of tags +############################################> +try { + $gitHubTags = Invoke-RestMethod -Uri $gitHubAPITags +} +catch { + Write-Output "Failed to obtain list of available EX-CommandStation versions" + Exit +} + +<############################################ +Get our GitHub tag list in a hash so we can sort by version numbers and extract just the ones we want +############################################> +$versionMatch = ".*?v(\d+)\.(\d+).(\d+)-(.*)" +$tagList = @{} +foreach ($tag in $gitHubTags) { + $tagHash = @{} + $tagHash["Ref"] = $tag.ref + $version = $tag.ref.split("/")[2] + $null = $version -match $versionMatch + $tagHash["Major"] = [int]$Matches[1] + $tagHash["Minor"] = [int]$Matches[2] + $tagHash["Patch"] = [int]$Matches[3] + $tagHash["Type"] = $Matches[4] + $tagList.Add($version, $tagHash) +} + +<############################################ +Get latest two Prod and Devel for user to select +############################################> +$userList = @{} +$prodCount = 1 +$devCount = 1 +$select = 1 +foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$_]["Minor"]},{$tagList[$_]["Patch"]} -Descending) { + if (($tagList[$tag]["Type"] -eq "Prod") -and $prodCount -le 2) { + $userList[$select] = $tag + $select++ + $prodCount++ + } elseif (($tagList[$tag]["Type"] -eq "Devel") -and $devCount -le 2) { + $userList[$select] = $tag + $select++ + $devCount++ + } +} + +<############################################ +Display options for user to select and get the selection +############################################> +@" + +Available EX-CommandStation versions: +------------------------------------- +"@ +foreach ($selection in $userList.Keys | Sort-Object $selection) { + Write-Output "$selection - $($userList[$selection])" +} +Write-Output "5 - Exit" +$userSelection = 0 +do { + [int]$userSelection = Read-Host "`r`nSelect the version to install from the list above (1 - 5)" +} until ( + (($userSelection -ge 1) -and ($userSelection -le 5)) +) +if ($userSelection -eq 5) { + Write-Output "Exiting installer" + Exit +} else { + $downloadURL = $gitHubURLPrefix + $tagList[$userList[$userSelection]]["Ref"] + ".zip" +} + +<############################################ +Create build directory if it doesn't exist, or fail +############################################> +if (!(Test-Path -PathType Container -Path $buildDirectory)) { + try { + New-Item -ItemType Directory -Path $buildDirectory | Out-Null + } + catch { + Write-Output "Could not create build directory $buildDirectory" + Exit + } +} + +<############################################ +Download the chosen version to the build directory +############################################> +$downladFile = $buildDirectory + "\CommandStation-EX.zip" +Write-Output "Downloading and extracting $($userList[$userSelection])" +try { + Invoke-WebRequest -Uri $downloadURL -OutFile $downladFile +} +catch { + Write-Output "Error downloading EX-CommandStation zip file" + Exit +} + +<############################################ +If folder exists, bail out and tell user +############################################> +if (Test-Path -PathType Container -Path "$buildDirectory\CommandStation-EX") { + Write-Output "EX-CommandStation directory already exists, please ensure you have copied any user files then delete manually: $buildDirectory\CommandStation-EX" + Exit +} + +<############################################ +Extract and rename to CommandStation-EX to allow building +############################################> +try { + Expand-Archive -Path $downladFile -DestinationPath $buildDirectory -Force +} +catch { + Write-Output "Failed to extract EX-CommandStation zip file" + Exit +} + +$folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") +try { + Rename-Item -Path $folderName -NewName $commandStationDirectory +} +catch { + Write-Output "Could not rename folder" + Exit +} + +<############################################ +If config directory provided, copy files here +############################################> +if ($PSBoundParameters.ContainsKey('configDirectory')) { + if (Test-Path -PathType Container -Path $configDirectory) { + foreach ($file in $configFiles) { + if (Test-Path -PathType Leaf -Path "$configDirectory\$file") { + Copy-Item -Path "$configDirectory\$file" -Destination "$commandStationDirectory\$file" + } + } + } else { + Write-Output "User provided configuration directory $configDirectory does not exist, skipping" + } +} else { + +<############################################ +If no config directory provided, prompt for display option, and WiFi if using Mega +############################################> + Write-Output "`r`nIf you have an LCD or OLED display connected, you can configure it here`r`n" + $displaySelect = 1 + foreach ($display in $displayList) { + Write-Output "$displaySelect - $($displayList[$displaySelect - 1].option)" + $displaySelect++ + } + Write-Output "$($displayList.Count + 1) - I have no display" + Write-Output "$($displayList.Count + 2) - Exit" + do { + [int]$displayChoice = Read-Host "`r`nSelect a display option" + } until ( + ($displayChoice -ge 1 -and $displayChoice -le ($displayList.Count + 2)) + ) + if ($displayChoice -eq ($displayList.Count + 2)) { + Exit + } elseif ($displayChoice -le ($displayList.Count + 1)) { + $displayLine = $displayList[$displayChoice - 1].configLine + $scrollSelect = 1 + $defaultScroll = 1 + Write-Host "`r`n Select a scroll option, or press to accept the default [$($defaultScroll)]" + foreach ($scroll in $scrollList) { + Write-Output "$scrollSelect - $($scrollList[$scrollSelect - 1].mode)" + $scrollSelect++ + } + Write-Output "$($scrollList.Count + 1) - Exit" + do { + [int]$displayScroll = Read-Host "`r`n Select a scroll option, or press to accept the default [$($defaultScroll)]" + } until ( + (($displayScroll -ge 1 -and $displayScroll -le ($scrollList.Count + 1)) -or $displayScroll -eq "") + ) + if ($displayScroll -eq ($scrollList.Count + 1)) { + Exit + } elseif ($displayScroll -eq "") { + $displayScroll = $defaultScroll + } + $scrollLine = $scrollList[$displayScroll - 1] + } + if ($wifiBoards.Contains($deviceFQBN)) { + # WiFi prompt + } +} + +<############################################ +If display or WiFi options set, create config.h +############################################> + + <############################################ Install core libraries for the platform ############################################> From 1aae0aed0af18207204987bce46bf21cca09605e Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 11 Apr 2023 05:31:44 +1000 Subject: [PATCH 21/25] Working on config output --- installer.ps1 | 64 +++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 56eeec0..e1cd44a 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -76,7 +76,7 @@ $supportedDevices = @( ) <############################################ -List of supported displays and scroll modes +List of supported displays ############################################> $displayList = @( @{ @@ -97,19 +97,13 @@ $displayList = @( } ) -$scrollList = @( - @{ - mode = "Continuous - fill screen if possible" - configLine = "#define SCROLLMODE 0" - }, - @{ - mode = "Page (default) - alternate between pages" - configLine = "#define SCROLLMODE 1" - }, - @{ - mode = "Row - move up one row at a time" - configLine = "#define SCROLLMODE 2" - } +<############################################ +Basics of config.h +############################################> +$configLines = @( + "/*", + "This config.h file was generated by the DCC-EX PowerShell installer $version", + "*/`r`n" ) <############################################ @@ -447,37 +441,31 @@ If no config directory provided, prompt for display option, and WiFi if using Me ) if ($displayChoice -eq ($displayList.Count + 2)) { Exit - } elseif ($displayChoice -le ($displayList.Count + 1)) { - $displayLine = $displayList[$displayChoice - 1].configLine - $scrollSelect = 1 - $defaultScroll = 1 - Write-Host "`r`n Select a scroll option, or press to accept the default [$($defaultScroll)]" - foreach ($scroll in $scrollList) { - Write-Output "$scrollSelect - $($scrollList[$scrollSelect - 1].mode)" - $scrollSelect++ - } - Write-Output "$($scrollList.Count + 1) - Exit" - do { - [int]$displayScroll = Read-Host "`r`n Select a scroll option, or press to accept the default [$($defaultScroll)]" - } until ( - (($displayScroll -ge 1 -and $displayScroll -le ($scrollList.Count + 1)) -or $displayScroll -eq "") - ) - if ($displayScroll -eq ($scrollList.Count + 1)) { - Exit - } elseif ($displayScroll -eq "") { - $displayScroll = $defaultScroll - } - $scrollLine = $scrollList[$displayScroll - 1] + } elseif ($displayChoice -le 1 -and ($displayList.Count + 1)) { + $configLines.Add("// Display configuration") + $configLines.Add($displayList[$displayChoice - 1].configLine) + $configLines.Add("#define SCROLLMODE 1 // Alternate between pages") } if ($wifiBoards.Contains($deviceFQBN)) { # WiFi prompt } -} <############################################ -If display or WiFi options set, create config.h +Write out config.h to a file here only if config directory not provided ############################################> - + $configH = $commandStationDirectory + "\config.h" + foreach ($line in $configLines) { + Write-Output $line + } + Pause + try { + $configLines | Out-File -FilePath $configH + } + catch { + Write-Output "Error writing config file to $configH" + Exit + } +} <############################################ Install core libraries for the platform From 6199cecd42d4f8e5531a9b94ba52b6652b8788fe Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 08:55:04 +1000 Subject: [PATCH 22/25] 0.0.7 Ready for testing --- installer.ps1 | 64 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index e1cd44a..9436e0a 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -89,11 +89,11 @@ $displayList = @( }, @{ option = "OLED 128 x 32" - configLine = "#define OLED_DRIVER 128,32,0x3c" + configLine = "#define OLED_DRIVER 128,32" }, @{ option = "OLED 128 x 64" - configLine = "#define OLED_DRIVER 128,64,0x3c" + configLine = "#define OLED_DRIVER 128,64" } ) @@ -102,8 +102,12 @@ Basics of config.h ############################################> $configLines = @( "/*", - "This config.h file was generated by the DCC-EX PowerShell installer $version", - "*/`r`n" + "This config.h file was generated by the DCC-EX PowerShell installer $installerVersion", + "*/", + "", + "// Define standard motor shield", + "#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD", + "" ) <############################################ @@ -148,8 +152,6 @@ Prompt user to confirm all is ready to proceed $confirmation = Read-Host "Enter 'Y' or 'y' then press to confirm you are ready to proceed, any other key to exit" if ($confirmation -ne "Y" -and $confirmation -ne "y") { Exit -} else { - Write-Output "Proceeding to obtain the Arduino CLI..." } <############################################ @@ -165,7 +167,7 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { Exit } } - Write-Output "Downloading and extracting Arduino CLI" + Write-Output "`r`nDownloading and extracting Arduino CLI" try { Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip } @@ -179,6 +181,8 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { catch { Write-Output "Failed to extract Arduino CLI" } +} else { + Write-Output "`r`nArduino CLI already downloaded, ensuring it is up to date and you have a board connected" } <############################################ @@ -424,7 +428,7 @@ if ($PSBoundParameters.ContainsKey('configDirectory')) { } else { <############################################ -If no config directory provided, prompt for display option, and WiFi if using Mega +If no config directory provided, prompt for display option ############################################> Write-Output "`r`nIf you have an LCD or OLED display connected, you can configure it here`r`n" $displaySelect = 1 @@ -441,25 +445,49 @@ If no config directory provided, prompt for display option, and WiFi if using Me ) if ($displayChoice -eq ($displayList.Count + 2)) { Exit - } elseif ($displayChoice -le 1 -and ($displayList.Count + 1)) { - $configLines.Add("// Display configuration") - $configLines.Add($displayList[$displayChoice - 1].configLine) - $configLines.Add("#define SCROLLMODE 1 // Alternate between pages") + } elseif ($displayChoice -le ($displayList.Count)) { + $configLines+= "// Display configuration" + $configLines+= "$($displayList[$displayChoice - 1].configLine)" + $configLines+= "#define SCROLLMODE 1 // Alternate between pages" } +<############################################ +If device supports WiFi, prompt to configure +############################################> if ($wifiBoards.Contains($deviceFQBN)) { - # WiFi prompt + Write-Output "`r`nYour chosen board supports WiFi`r`n" + Write-Output "1 - I don't want WiFi, skip this step +2 - Configure my device as an access point I will connect to directly +3 - Configure my device to connect to my home WiFi network +4 - Exit" + do { + [int]$wifiChoice = Read-Host "`r`nSelect a WiFi option" + } until ( + ($wifiChoice -ge 1 -and $wifiChoice -le 4) + ) + if ($wifiChoice -eq 4) { + Exit + } elseif ($wifiChoice -ne 1) { + $configLines+= "" + $configLines+= "// WiFi configuration" + $configLines+= "#define ENABLE_WIFI true" + $configLines+= "#define IP_PORT 2560" + $configLines+= "#define WIFI_HOSTNAME ""dccex""" + $configLines+= "#define WIFI_CHANNEL 1" + if ($wifiChoice -eq 3) { + $wifiSSID = Read-Host "Please enter the SSID of your home network here" + $wifiPassword = Read-Host "Please enter your home network WiFi password here" + $configLines+= "#define WIFI_SSID ""$($wifiSSID)""" + $configLines+= "#define WIFI_PASSWORD ""$($wifiPassword)""" + } + } } <############################################ Write out config.h to a file here only if config directory not provided ############################################> $configH = $commandStationDirectory + "\config.h" - foreach ($line in $configLines) { - Write-Output $line - } - Pause try { - $configLines | Out-File -FilePath $configH + $configLines | Out-File -FilePath $configH -Encoding ascii } catch { Write-Output "Error writing config file to $configH" From bb7cdc5422603465bb4486f24e2a51fd8caf99ed Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 09:42:54 +1000 Subject: [PATCH 23/25] WiFi AP mode compiles --- installer.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/installer.ps1 b/installer.ps1 index 9436e0a..4931081 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -473,6 +473,10 @@ If device supports WiFi, prompt to configure $configLines+= "#define IP_PORT 2560" $configLines+= "#define WIFI_HOSTNAME ""dccex""" $configLines+= "#define WIFI_CHANNEL 1" + if ($wifiChoice -eq 2) { + $configLines+= "#define WIFI_SSID ""Your network name""" + $configLines+= "#define WIFI_PASSWORD ""Your network passwd""" + } if ($wifiChoice -eq 3) { $wifiSSID = Read-Host "Please enter the SSID of your home network here" $wifiPassword = Read-Host "Please enter your home network WiFi password here" From 05b0fc3d2e9524afbfdafaaf4ef3ea024a87eb05 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 10:26:15 +1000 Subject: [PATCH 24/25] Add exe version --- install_via_powershell.cmd | 2 -- installer.exe | Bin 0 -> 75264 bytes installer.ps1 | 15 +++++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 installer.exe diff --git a/install_via_powershell.cmd b/install_via_powershell.cmd index f367b21..c3a7e03 100644 --- a/install_via_powershell.cmd +++ b/install_via_powershell.cmd @@ -11,5 +11,3 @@ powershell %~dp0%installer.ps1 IF NOT %PS_POLICY=="Bypass" ( powershell Set-ExecutionPolicy -Scope CurrentUser %PS_POLICY% ) - -PAUSE \ No newline at end of file diff --git a/installer.exe b/installer.exe new file mode 100644 index 0000000000000000000000000000000000000000..f783eb2c325a77da38f9b6a62d96ec446f83d8b2 GIT binary patch literal 75264 zcmd?S33wb?c`p1`^&+XYSf$otdlqX}NAwk6rxyf8DKwz@2}-Rf?- zTVu;VFV8N!4m3lj!1goFgbO+p|Lk_mwTNw^P8Ai3}d{#*hHke@)t_j$js zj;eZ*EW79^4S!%A_^TVnXVS&)d?7boNX~XoC9~OFsr!W6U6{*ur?cI=N5;Bmb18RIb93xw zui%3NzyT`+=do=m^RBPJwi>NQ;93i)*Q=j?fdzB}xX0o_&uy(Y|5$(dyJ&&e`4_@H zzW}t#fB)A@dVgnMU}22s_gMJKLLq^_2P`xNlFR< z07N&ouCMFrSp&pw?db+$8~<(HYTl51H}V%id~+0Uwxqqzn(y_5fhcQnlfL0*Aa=Ct z$O^0H7QJw*UMM~fh&8s=Kiv@q?6&%<3^NxZKv-^+7d3Ms3WS~drnUy};f7Al+t8`E z8k*Z;Pj^IEE^ER!&RnQtX-u+4GZ*SjlGU2I&|s2$*UW_&-w=z3qjmKSZQ&jg&8^F5 zQx6b}M>^xt^~KJ3-TDb9;zXUgp0z-%PR`Z=vGXAy97u+N@R}9JE3R~Z8^G~3tuN{< zt_NZd#Q@8x>pBvzbLuMks`o8UwChMb>O?E5c-NUrmo7Pxb*p9Y*6r%qz~SDwKsdXU zl1NTUNr-QbU>-7!oY2bSoljexPg_C&vChUe^lSoRUB@?s>sCErlB1WO5HQ_H1S{Qn#jCvi1V8HuP*^*MwGkZz^tGps5IUTjW1*yb)Xx zlA+qX*|a_+1G}Z$8`2da8N1D`rV~Ok(zjXb3{ke#c|^eu3=jpA0irP7>k$Q$0ivjD zYzukCguE(vw8dng7$yU>#iU*AXm@aM|oO@DT7nKnJE%{Tq3 zeWP=>eCFA?oj{DkMs}mA#x`X*D{QIT3R^2>OQm}5;COa+YSyMUJMdt0TSz#S-iwFZ z!hxIdNLxhiE>SG`&aeeU;PahuXYK_+EZikOl|06~YAUKf^Y%@7j3LtxhYh)(e7N=<+W!a-}jc8@4JiTyWZ~EFRw>v zvyG%D-R(6^7dnBky{6^DO7C~I_j|4Pdp&U)+3|iGaGcy2g>HLO zU2dGc&gVBYe`YIS^-KVuU2jXpQZ9PYUSfLNd z_Z(wcd?Frp!dQ ze0RGAv^&wR*MwN9cBj45X?Um8o=XwZJQaz@oY*;)s_TEj2^ZYJGn%P%8y{?pdX;D_ zx_tjhAhzRvmK|?uk2lwUrqgs`&osO2KS+|FiI-pe69B>HHQAby*3{)@7TVot7v_Mx zcYUEhHZ)gc`c;$3)~;xFnhU?q0ZDUQTWrlP)Uf_`r&+tHzAkr)UDV_>ZM?y0uGbE0 zdaC1AyYL=UpcE5tehR@7H@;>4xGlp}ambx!fh|r;ylMTGU<*6#!oM#JU{A)3Tzh^~vUVo(pj9I{!ZIK<4pK8Mf03Wx3Z+p-2@0!g~yv+t; z)$`4pXPvQPo|8)Yq$!env2^Z^{{rAN+H&vcrUEUctn5K+?t!?<){{$gVYa?m>CPiFWqh>J{Ter5rzJ?hGmCEHg&dx9IgQxfqYsIpN$qd28+*kDa}Z zcHxp4pI&$FU(1pP3}A=b;Ls62|E#;Yd-_ToN&{6<|Kp4U0C2vi)D{?9f>(j zI(Gl@w2WQThB~K7$8NE5R$0i`E0yq_$`I;$ewJjzHpJ`doVwggNZTuTc9GW_zkG>3 zU8ls|&=QU}e))d`cq(-3Q*i1EM=ftY>>PXg(j{+rns>aH4>im1kb{&BP=l8?BNKCC z9la~Pi%zV3Xqfq-g_+yCSljMnmoDTL9`0&(B5E9r&+sm&!tP3zWnn4 zx^&5D-caW>YmYjO)WiA?sQODfZnc0rEl-#A{vNb|odAAYaW%2E(EVRpz{dc*HbD3B z%_00AfW}4XJ|aE7G1?Mcb$nyHUASO*97~x|Z2fJ4Yb~B|YO`Cyg*S0XNUqApBf_8} zvGzv0@UUM9`9ot=hc#OGZ43E@QdULR+$)o&^R^bRyr>AFShTMAQjuJ$mxl1$%V@)f z=U(lv*QvH{wNSs{mk{!h@pOtmPsy%oIng&YsWr`8>w12H6gA>qxqj!TcimoxK++bq$*HoQOTo#kG^9G_aq@s{Yi_+|?$05rqA+omEJjYWyS zEUc#u3vdLv#00N2hQvA$77Ip1-W8QAb!yYNJ|x%HowM!p0+G&N1B45IWg#CDnkOoZ zuKO1CkLZTZo>#I`|Fm#7_bS36+deOF>wFiV_3!GltED@X4wh5LSCc;m>$uFeRXulo zNUp7mO6!!jUdI~#z^^}NwA>b)czzXc$L;dQ3$`>iM7!rToG`DY_IIOzM@j!8(>F=_ zbv(UUPG3(39lFrXMEJrDKzJSJ^Cp1f=-B6ZX`|h~&OUz&5WZ#O>RWBSSR4eRJ#PSF zx5O^+UX-NB7xI@c;xFH_ZgpX!jXaa`f=!**bgsF!_)Bb9)cbeRKF>OI@mufDUbJEDYJm7vBiP&e@?0x3c6d_Iduw;I6w}6Cpv?3%5Ap{w+zFr`V(ZElHVM z>}LO#q|7Z6p?^zK<`yZ_za=Sii-YUml9ai{5%+IN%G{z3;NOyzxkbIeza^>b?eov$ z)f?^51v$CiKHp<5$cfQL7W+MCpWnp(Tu0&kW&&UP{B1HWzbsnCHJd{iRGY5T9xI*} z<;lLVkya}E{CXgKfzQ{4FYqb*{07p+#^$=GqZuGtd<(fzv_1E*Kx&HJb%51Mp>rd; zcOgVM^Gf^tS|A)MH2~LybB~ai-^#n20F zUnK@;vI8yRJSSFr2aN%M(;n|=k9QVN0?~LEab+~t+4!?>r)+S#inmbZ;(MY_m%OjI z6NolDU7f{ua2m0vdfrJ*<>_dOZG0E2@M}PSr&V%ILmvE<2dmPn|aT1x-PJ>PTR&6PWlQa55A8Nb~qim-=-n&38o@qzTI%Gf~e^F23iqv?vOxc$m+=GK75Lbs_I9!e+bTE5H0+S_xxa zk957}`7GyJ=i1ISH@?leHuoXXX*W3yg*S$f_i9z|)YFP8mmdvW?)fkYczu`Lu6I^< z9f_}YRy(U+o4$L+rAwE3{*W^0@euMoAK@tc4_2>HbbmkK{het1Fxpxj&`yNcW4gBKI-A<>NqX z)je4{Bb=5;+FE=5jQS8YPp4_a3iFnx+$Z=J@1d2sPx8?}=iyVVG&O7030HYpy8JBh zrX2)|)z21g_;c^EjW<=_6~&~m-elRZ(bPpN9(Y05t4{z{?k~K4*zsvngyss=wD5dW z)Z%aJH(55UTl8(6m?t)WqRC>;88R zqqGY|H?}%m!Ufl)a-ZgG`e(0@++PQ{#%D+gp9NyqY`xKAcqW_N`WxQ=91mo~Pjz}X zH#C`CqH;Uo+~;|_$q8?uS}7NwS{-jM%yC?ZC!KaFMkeM{sN2w5co%OGBK3TMjgAuX zN7p$kgZj#Asgs0(7e;_iPgkKn{s4dY!?F4T6S>WZJz@MxFZW=2U z(%I=EbL=JS{uFT2#260zKz*P$?U_jI25|2WfKn8=X;+4}82PgS>+s$0x~?Gx;6HEK z%Er+!G0h18V2fwBpl_1b{~lu3le)3}^eHnR-IX^0c)t2&uB`;Fpz z_#9%m*6OfgxK)lDtqv=SE!O7{#h}Hz?~yBCkt-pa*IzBiUzX#y<@o>G%(*?fIvT~U z=m!zQsp$S_6lbE$|BG^bfAoWh;*aF`8Od{9U2|O&uc>4H@6>&%E{5mTvnFqqUuucrb8`G)3(MKHf@S`{ zE7-=HTUqB5t-SKKR$lptTxoQ8ueRF#Z544Z8$MEWQ=6|Og|Ga&q zJ&G^4vwwcr&Ke%*;Q75BJb#~@|7{1K`kq{A?&RH_oh)IZlX-rp^HrTu{AuT_I%D{s zoy=L^#p}Deq?|71e1ja{C&z#2da^5qA9np~R}4KXH@CN7YUS>gElA5TC&!0Y?p_(g zZ>*I1%lQ{pvdr(UWSJeSSm%waIMy#(#VfzQ>X%wb=t_G!vs$qdJ%~uE&1%ICp=ldy z5VKmvb>4u8m!dDf0d-gd3#}MK51P@4H3(Tc_Z>9i7D=ULCOEi7P-+o9=s<^;x`-aE zLN|88Mk`(=dg`qh#M=S94Lty!hZo6n?~^w?562O*TJd}B)M(e@q@+4Km|Bmtq}EDm zBTh@|t&-Y|Ssc1JWW^D}o#D?QguQYc4Ra>k7yesBaa@i`Ii8f`DLLkOjD9%$4NPAA z)9}Aw^5W;^_)l{DH#u67e_6=W7HP01FAiBe-V|xDENqK(Sy9{(xyFiOM2;yro|oer zSsC&zEf(T-kYS@@f}qGjQWb!RLK|5bP1vf$M7xS{?fmW96h*I5?!H}LLz z8lSW*e6TT&sN|30i|sEDM{#GzuY{vG-0}8s6hGVX-i0gw)cpR6D*9h+dF5>mwKX2Ve3zL_6)Vdq3ILHVUe4_pjtle;M*h@WWCE(z=m-?DD zf;K$lrM_#8qYZEMQY-9Zh~sfDb)$V8aeTr{ZMR*tHx4e5P zdpA1pJuh|EPSAo(CX7$u9d-eocqiRs_(c5&?3bVmk9nz&+au`0hrQHOl6q28s0+Om zT^PSfOIZ+$(p^$oX&j-ea&^E8zR9Y;5=^-}kS zMsNf6d8yN(aom8EmpT`E95>>$mwILB3ET*JM1hyBd?b{GR7A$%QR|C=)S2)<;AZ@c zvp_IQwY~Rj@yxv)I(TTchtHaU-9nUE~y`R zsiC_2t=qA+SIZo#djdQ00xvaI_XO_1F)wwZ?g_jAZ}w6zuFK+1ynP{czjY`6UQ!R? zeBJ%l-B>}fjZfgkbx&Xyy1dk{)J&%e~#gJ08o z^}Y44vJ!a8OO4mR(MsU!UTRuW-||vFQ~$7)!1uk>tLh&|0{`Wu-dO*rl|W>x_Sw7Y z-^JOYDg1VQ774_?)JG-N;idjYQe9r^8Y;|+IDoTW>XC*74&Y^8>H`hCF@#_8QlD-}U#AX9g6M7LHsW-bt;y?LHwJSdTDGo4&joQdNh{6AvAAe zFTJe(L$Tc$#cD70`B(y@pyiPF{x!B6W7zJc8XFTBL%)}LUgK_z?~KC2yDOcgt}=at=vp6IEAENcpF#uAi0b4>hv~UfnF% zeu+F5-X(AOfV?GGAHLV}u2ko#zM{{5OiFuFp4Afc_&K@$#X6Sx!A2hSJov_E>iqBI z?O&2N{!`U?yn}BK-hH_2N;5X&t9T^zCF?t&4Euo`E$flc6KIg5Bga*8yg`m@<=AUI z5=vvI#i#n@O2YaB>uXlRdeZu))rUty-?kFgz7^lM64tR5-?#d3dWDOGHMinFtv>vM zoWE|xe_9Fa9g^n>9`z~wk=*?_@8WOd{O={_cAKT`w)s?D=#rJNK45bUv~c9PFU}7 zzGOGZ=saXS68ftBdg=dFa%HU?8$evu0Ajm`t?xPCve$}Ky;knt45!TwZNQqg_RuS^ zzU^^5W>GhyV`CGJ!bt@+wGyptPi){ z6MD@0VcTHnqgE__Ec6OcVm@qj#UBW*6+Ez3(8M>aTjSHA$E@CXE;NrvLKi~U;;#75 zg&wnZ#a|hE1rEmf)C2K1hF*b8{8iRkd_B6~VmXh5)(YnMj`b_?m*5B1`{P+`lbYWt zHQy~Ye?2}UCH&9$w?hwG|119e(8JchcvpTHVx19w*!ph#acqHB(x*^etB=gdU4&J@cZGkyLdmYZRgkx)qTl+ zKW=H?A5K^U?WwS16Spwum+V#cbo;6BDx0Wdl})tqDa^O$!@W}4JRWMF3x5`mgf50} zusOfh+MGK{`|BMq!6Eyb9iOz)_9LM`4kzqT=bwdl+N(NU96){hSHi2n5m_tPYFw`L z**A5*1P@zVJHHj~wI2z6(wevXJHHctNJ^NuOP&7_eue#)oll2fZ-1jR8hO3_qs}Z6 zw$;@fd56Bmy0I%BN!Z)FR!0){!LA!3kJ$t5&x_m(*5Lq7cfBBzlA5P*S9~~r60a(r&m54`Hnre@+J6&{rZ(}kNm*?m6a~6 z(0f<@MkHZ>aOE2!Kd|fD-y3NN{pCv1&nLSc2e1EbBw>GfG)#G@*{aW6&>Owz?%-e5V^*Ai4 z$<7c*q#j{(q5+%nZ8TsTzJ~_vz<;6vcj6Kna1X*(1NNfXYQO=>c@S;ZCagz)bQ8A9 z@kJPoZo*A+{sKAn%W=OP$B>AA%=%dLd-x|g-emLqfu{F_zlR9^(OQ9j6eEBJ--qNk zjBR$_p0^*hf5-l`{W<%e?YD>G;k&~J!$-pRh0lloI&4R6SZF{KVxsxCqY2leS(Yfg zJ;dmPCqf?v@Q0y42Jjc5j|2FET>n~#v8?|lck9E<(-~%-TjYFm_(=dy*3oL-)bMT# zSlRGi3wWL!cQpKt1@t#C&)$aLw}7|Sf5Zari13Xga!kqbyd2*k$KRCWN96c9Iet@) zc9c*3awF^W&PLYIKj&J25G+KcN9x2XQjZ91v3gjDp$;}0(EzTG;Bf^SVWUNk4w_-3 z4J%+H4hJ??A`T0y|j0XFW! zO|Wq{Zh?(^@I2VqgSD`*SC0Fz9yStk+>ed0a6pd3xD7UjC(w_U6<>9t_6qyo?H}4rp)ZEM8v5tZ--W*(-V)gp*=>bz%?b;cg3^jWDCp%l^L8qIOjCXW@f#8eGZ}Lz7T=`LzzYMtTp+ zXxpem6!nOr5izu&87t6?R;-ZGe5>^j5h?i2pt|mAU)!d5p~$ zOYZEZp=35W?asQ{(x$$-Qf@X`O6Rgm-rTe|S1jS+7=PHXqFeAwpDj-13Yqi?uLMjc zGjm$`krSufsS*aVbF<#VUj3xw&jguN7-t z8%v*aHM5i`Ny+CY`*XQMDxFQ1+@j>{pDPq|g@d_bTDp_DD=zWndTF7&KyolMSDZ;? z^K&Jy70f=EE4Y<)t-Pk$``t_iqwZ8GnVrtK>;mt4E?aW5C9UIrcfRkzWIB^Pk2)Z4s%uyjE;_snn_&{jwto1>}k1JIcA5Gg={)I zU76RrT$y{fdt$CCuUxFmIq4QoGCW*}=Zn=he% zCYPRat8WbE3VI6%$NCFy%FUM2$xK;-Ql?mTWg=mwx0cRA{AW4;RJM^997tzf(}FQ~ zYOatj%|lLH^|~_u!DO*`CRa!;JkeJy%Obp8k1}9Vs9Y4f@RQ|dB7w4OjyFW3?iAl5 zPnv6*^2XMG-XkKppsmR#3vRZAxwL7{o=omUGLt^1(y@OoQ<^Kd!US`L^mIC#%+$Iz zkqBPhGnYKwB34nAe-vT z`xnRD(%4LHE|X#@2sW#t{$A3rd!XJ@=ZOT4&!wfVH5mh$f6W_gT}`gB@I>Xr!LH5n40sd;1JiOQlDo~XRRCl+C4d}fhbfu|}93_MkNi%*#u zRmlkX*kbpB&sCNge6I2yp9@eJvj&p9OUmodrBwbeBm$Wht`&Vsb7pUr-MQIhI(xi2CuYjAXeO@+!!VE0xojyt>uwSrmCKL0g$L79 zZV^MtLUAUU=_@XXdrV|!1&^l$?~lxt{9CCr0o-B|f;*APll_jm)12ywtp7|VFZ|ED zlgTSe_JLt0-=9h6{dDTgZnsz>j!oyXBf{B`$q%M88DxDBEvNy+8453R9sn%n<_c3T zQhpJb%B1r+$>)_%V#*-U;sPe0A&S9NQia&8TP!B0r4%FBikK3SRz%7@nVid%JTW#` zbPK{bO3A{sTawdcCX+kkrhFNtBJE@anmbVm`=#oM6kA2Hk<90j}A`kVYQUr_vy-a4d z+$Yr*OR5d{?Jc|PHNboIP%f1|nRW}Rzm=!@llkI-+|+3)dU$U3gj zO@kU?A(z?j&X1KAwAaciX*$@MGs!}7s^k_hlzY(CVdyKkN%R#8$$4rf2i)wmH-P%I z^ae-fh}1(bveS4?`@bS%d6vM~`UId^D${|A`f8z`jg<;>Q>6pR`CO2-YI=Vhat&TI_L4Obr%tv*lN$xI}TG(+4tQ$dO`XbjNdEZToZCsbmR5 z$1>J$S0*`Kl!RNrLd^~7v1%kR??EK859Ut0NMuV} zw_q?=m`#>g@PlrFYDqFXt)?K&Hsq2>IYW@kA5Uc67tbZhXQWko-AsPS69u{a@qysI z-6{pdv}8D)uNf7CgCX=e%xz^7yVJ#dt|-P1J$11l>oJxql-$&Tbg}fn7J2`Hu@6RNQ#ph!vnVm~!M%|)Y67YlBf(t4reW{e2^6}7} zL`-*`IDUK!5`D$_?9`}RoXeE_>)VhlRKFlued#3n$8Ns!t{umZXL3`?OmPPevRB7u zXuryzDb5I4<}z+S^_xtMDr~MO;e=m{Ueu_>6b_EDNj|+i(2J>D#%NJsS2dw{mD#*Py(VVjg@kFwO)A2C%L>3j-C}MPBSj8Gs-pk@_7Z7!LjwO zRKrZEkXELEWGYn)iF%|})2K@{P^96xNFF0+gGn05<`>fFFSyB)Yi^@2pDdI}P9hwn z`4yK&@^039s>WCaYpeaX-<>}w0+DQg;XOJgW2J(doK@jw&V!A9pjVo`K#_uC*q<16 z^BJ+I69*Msk4I}NU8Fs`lq{5rhts7Q@7`EGlP($4h9odvNY9!M+?`HNXLH3;da9^) zqiMH*1IfAU)Qri(xr>49R8Hn7hk9asa0kexcO{GN?b|%oEB0r`oQoY_;ZI*CBaio` zQe%R0gN1Gyb*JYt$-=a>=Y;J0b;6kqW6fKpJYo0MW zH2rZV#)TS*Lrz#{&VB{#qdarYSJQA7zYX0ZwV$RUFaTuh;aG;+wIh)vi7 z7g@L{AdM+(!U3e^DTI0%vQ* zH+nFRdF0`u8#mx?bmJs)D4-iNaFIbCo3Iv$NI4Paz@Eb>NPVUka?c`(5;mY4d8u;} zMJbU=3(Mx+ZlsY#9&;#Zts1qiT5>n*8E3s&|NGGGwE%bX&WmujT-)o_iSbrg3;jBRg+S}E{sT7 z>A5RD2BnP#j=vt9K;w#F()*UM-o$Tu@QTb6I!q$bBu0p-iR|Y(pi*X)p5+T zKqSd~@r`hC7A2&R#w=!;qrsd*@=gJvJAjM-n!rhPV;(uop&Lcy@nXCfQ%K_^=FyE3 zX5t( zhA@nNq%nkPoWYB68q*lUDfD3;Lzu@d9Ktai!6-7)UAwRwC$I%)kpbhRayW$q`mh=M zk&yfoNMa8%IEMuG;283Ze#8W(k;D;s&lL7x5c5c(AG`1%jvUO%$ltmi0lI41Sni*aP6U8C57NqPSf?8A&i z25{V1{yr3u#9`_8ajEYq>5uI==as{L;5eTNX?)oK^ZJ%O#l zcMN^+7rNtkneol44>)h~-tSg;t*?3=U!vlQQ0iFoEqj=G~_Z$Y39K3B8Tt7)FF%_+Ijb zob;cT$MM@D<5}4b$~U&-tjq^(Zwgz44{#oG{;*%PzqjCMpuXdnz$C_S2%9mpP!Cf0 zX=E^jF-%ImI3ITk{g5uTUE48;3G@p6kK#0TGG+?{7{D|pFn~dMdO*0@BqnfH+Dm?P zNcv+R4q+U<7{;LBls-OZp>Sl8=!;o%ZfB(_O^4Wn23S@~T?~F(e))%xMFwYZROH&Z0B(7<#=*SjoF^v~zbO8CHt9j@LEM(dK}-prFymry!zme` zApRm>CEup}R(_%Qjrgor##`m~6wY~cp!07Z_Fx}+JwA0Bqe3r=$B3sm-ur~F^kcK| zA(jPi*dVLld6h(dz?{hxzmvY(6tLpmi z@y3sQ!pGasE*~S^DnHHRkkngo?ii*$oIrk0Y9zhrxR*RRsd~#a@`A=xPLuDQ@brp# zum7?*A{r_2{b8@4sO_>JiTB9I6km`o4W4@D-mCNO@^qv4QuT@F0v{pPBF^;r1LxIQ zJcy%$>xfJDE;24EZw&5Nd~;6vpZW*+&Y-9NlrSg!B854@rNo2d*emp_`st|v4l?}q z+59!~p8SaOk?mmpiy|jEzb0$w4Sm=td}ANBU_Q*cBw$|!9Zg|Q;>8m(4!lq6+yn+adA?KhY~py81KSqKPrS&8^X-Bk_M#7I z!BhM_fg{LZ+#3hVZRX?nr5E&GM#HmTNGFE`kLf%emGRvz{QnU8rQPI9`*7NuhaC5F z$O~TOirw1<4^JX1_|$I~=lNFj;hgX$+VB=na|LgHp?|5*6Squ>jH*4~%{Wmnr(Spz zw~Ky2y@2}BE}=*2i2E>(0g(^;F)#gd1pNzo=%|=#*xw0hAN4BgA=Kn4mr{ZLNMIH- zHSj6>hx(u5Q|f`#6MJzKX&E2NrOh5*ox~ZLH^ir-f~&X+nflaG9Khyf;%SqA3sRom zsrm@Vllq6+XO3Yjh5~d;K0v*G40|yNuIrt|J`7<(^xq7Q2_0`k!W;j^+H1;5dgZX) zst1f=Ck8}&7(z)f;xrz_A+bZy{&Lu}%M1$MV88iv?&nu}qx>blp#D0B9bP-~qOTIq z524<{FJt&c~@AF@K9v{P~XCQ%YTNxKE@ zD3c$n(8U)SIsRItVuWKBc{$d}Xu-UszlZR2;BR<~!{) zoKK7EPe#5j%?E-wYO(n_7MPbmPQA_iqjp&RJo)Od*x_kMQayHJpm>`z-Zr=HoB<5b?a?eCj*K&Zl~)!C~~H zs6Af!v+4n=2X7N+7VjH7sF7pD6TTnnYU!sr;mcbEcTukMuAUHckIM zS`KZr^m|cHRDMESS&buZm-f)cJc3zl_w30A_vNG?_F^-N&)~QWil6S8t2g~P)Q@P} z_50x`>F-JLws1EhmPz{u?YXqS4GZodKj*yl`Iq8Ajx+t7gQ5=-FB`pfDLw2|g`P&e zEZ7e93+(rPb$;x47VYOj`vL29T5uHgO5%LtF!~pmrk+(f-#L${Z}9#u6ouZ1_kF*4 zu>5Dk4s7htY4L>-w+st^C!gJ3CI_yJFO~aG{dvd!;%-vJy=O5jbfk8!vqDE|C!u|4 zTH+2!XY&gab6UsAnqR_qAJM9=W;PKHlWcOl)Xen~qq?IS}%MWZ;0Q4C_A=qIPe zjzqrEhcj@on>%~q02uqSMSf|+@cHs``N1>OAEdbFT7OUvd?x#2Cx$SJ37Jnr7)L_v z0frtJLlS%+$4PNS&>p7vMaS9jorL)3h*KEXMLJWzl6hZkI^eid9`xgk*pVvvQMui> zwj86MkZ~Hup61KXaiQ12h50~?Lb`XQU8-*f`Jc+OO1tn-pgl&ur7UlB*r?m#@Je3E=7 z?fKjMxSC3PA>&0DuSGiqpQoLQaX@?z?N&O^)SeZzdy|fJJ}{n#b_LoE)DN{!XqI}J zjw|Oi=X}t=!gLM~*1S&pug{OP-wudAQtg+Z9Hw4s;*f}&RgN=mK>d-N0~#;ExGfVe zQC*+f^Nn`835ge>-ETtTRrU#gJBtCaFEM_G_FnoA8J99D@hsF0Xb0x+NkL#sl~b#K z4BvR}_=VIT!2VTzaaQP!c1_hYXFPwc&nGFbHGYTwBH{qro2mB^r|5jAT~+;}jMF3i zlHbygq<$t7*Ju2pw5My_hmO0}hxDd6gLL=Y;1gL-AEusl7-w+1XD4U88~x;pqla-w z=$3L#?ISv$sfYWxi*Xn_|EV8P|2!*^7mR<@H2aZ$P9MippDy>?jR^Kvdxqbi&*uAy z&*r7Qq>DqIJ)C@o?Fjl2n2+<7{wd-;g9GS|VSdJ|(r+^=QPLA0-(;HoL_LcAr}kW> zhpVe+WF*dtd~i-tlO|p@7;ixT zh{l1EZyXSA%lOY=9L70}iG18H{#feH%ZVqLLVgkdO6Buvzo-0Azwch*1Bb+)<&RUJ zq?MnlAB^~}A1Q1H_sz|SzDEf}`d~a2_wwB%aX*xkL&%oN)#cKmnRm1kT#0`}{gGx~ zUM{|m9@n{mJSTr6f23E2m|g9$wc?KkW&G5xaMJr_zU9OfUfyoK{CFqz>(E}T@$|uX ztLk|7w8XnHK3?Vgb0O~Ii120l0aU*xztOmM#eo_x&-qUJqrXZ0m02&pFOLnsVjKbO zzRG`y_sqDI^XaS4H%^N^l=YxjO>v^)eA-?8`Um6nEB(~Of9m&CIZio6eU<(_@>P{f zm3Wyl%Ew8J&!iuJdGkj3+?BAamCX0)Ob!mF4vC_RQlEUw<}rqLHkgx zc=zDEBmK~iPd&nq$6I=S5qH;$`%pfi^{j1YSN|^MVtKu4`D;}U(|$pmqH)-auhsPc z1`iuKN_~X-A>)4*^IJ_US#PD!etGXPFCK()gMOGw9H_W@Ui3JP)2ClW93GqMqjM|AAH#)hn z9)2pZ!~Fp3&}oW%Ek?SW0)Qr(C{$j4!1>fOvy+ z?fco3zp;H8iC3kamT{We4+ekvab@H;jLR{4IQ1s_>quuh4}yGXX*y<{3*Qrrn`gVI z&r@HX#EisaXnZYkCgm>e0n{gGAy=Gf)3ey<)_NghpQpWR8l`2%1+m|it`oxN{q)t1*H*oY@p_b(#QDZw%4Li`JqPVg0^S-qK%D-7eMtN%y)wO!4rT@H@7Pa#7b$ zsb0kKR5~R;BcCE3X2c%(0O^T-E3Ql)lXz+BD{9};`Kk0z97Ddm1Ea#n&APDl0l9B* zi`pak9Ql+hdMlAO$AkS(xv~6l@a-68T^j9KTz{nVBI)TJKK@`kw|Vg?YDW*ov-!B! zuP4{lu-#@|8}$ei_n$xpr#*aa*58m0ue==Azg9e%t~b$m8qPb$89i6#AMIJXZpiRQ z$`h4O98ZlKH2jX&4^_!+(iy*!G9dg@?V#o3Z`LiEaoy*we^UF-a`-3XOUUnO-_`g* z(i?I5)t|@Z`u2YDg{WPN`YZ7;^)t$6(w(2C&t$vc680f{)$XzopxxR_(5;(8u z@8Nu@j7Olig>;cwG~TJUeSq~>{K)lr)o~TPPdsDRvGih})I;M<*&aV#-k!zduAZ6p zFfN4h{-CEfmM|`MIN}-;$D{r}woCbdju*#4*J;!K%eY;ptK(%f-x1-zMo;ba-Zv`i z-&9|vT&Nrm>a9fojAP~eA#UM#5{Ho=(qF*qhQCrzQ@(C+jIJZ!9Gkc|X$@$aTu3+X)#T>VMSh8DGry z(jP^8pX&9zWmGd^6||Rz9crrF#6i{&PFF2tFtwBXmpq=TTYD zOulnO?7q}%D91?G#0AOA_CND+{fY9aaj#$ebs3i%zez71*r)UIaUgzBexd7Ns^|5t zWzzMrg>}M}`jv_IrQMk0MqE#NSXw`*q%ZEaR%qD z&a-m2tp8)a%rB*nSzh(siDyPZGCK-W$GK8TTIa zX9VpMsvmQWkm4Qvr9RGfGhUzkLhXuXKM~>yzEABjM$aTJ=Q>f^_Gr&j{|MurIZw#% z$S;f?g>-Ay2U1@$ek5aGR@`-U{Yk{@)DPHR-;Tp}E) zeyP4h++Rx`65lR`J892RdNz6~{Wrut#@|%(=9k7@6USZ-zZv>xxy1GS)p#efj(E9z zgzq%_T~x}k%6cwG@1p(Fv{U<&c0lD$w0|l8 z;rdJBJ??ivzdrl9oR3ic>N+s?hq1#fCZE;5Nt{AI9`%wS-{bm9#zT<4$anIB*VJAR z8GE zD{pr>|K+|D+$VtVVLTAmi>e-%$2sBK+ZXtr+6_2=b$u!A-5N*7Js!A@fP9|ijmiA; z_bD@c913@f1ET7zprHUEiwvrDZ+6)U3EXDDRTcS2j^g}$QI8ytK{7-Q`_c=3io;Zu~Eo!$QZs#~~UkSx!vU_1fm1AjRl>8k#&+&_W! zr2nmQTyz~F^(J zc)c7KG46)*S^alGey{V5aiAvtjP#ZZj3?9TH=15{Ka<|n4#S-xsCRRHxQUlgKDNC6 zI=-z_(Od^{Yt_4Y}Wg5oJk)hK6bfur2B4BE?+6m z(e?aih5xc&YSGXKX2?$#7kVyy0|^m=Lh9DlyrIw z17-5Y=a0)BXYy-CigLeYj*H7e(OgosdCy~Fal#ESyvnMZ_|Fmbp;yFN&f}?hnyGHa>&?! z7-dCyNW4p&uKs)eat*DoFHlP*eph{!{b1~ZDV!|pcj86Pe_#GrqXU+Bd#?P>=Vm=dJu-^@y zVZ2?X{M2>B!TH7g4``3pcsE05%FoKnW4)*cQNB?Q>b|8xysq=VTwZY=RF4Ds^78yD zx_ z2JnZmoBTBTCFr;O?~9iz@toR^4bG|Lzr@X4A)$DeK6&CE-FHmqA#qgDKS4V-?G#%* zKbVhyRUT4)ne}9h`y>wW^*9yrUjQzUk}nigT8S zXBoGqc<)N-|LWqU<h3j~FMZaf6EAHGYHRRBK<$^7%u2QfWt2{d9Ty zYc-z;>S_M^qUHCK^0Vd9AN9l`iEg2MHT&%Z@iE&+damS;q_qj`!$=vvTdq9d?kdaG zqssZNzu%3I4;9A}XOznw#-UNJseMf4-r{;Zh{`@Wh?UO+}#xqra@)_D0Oxy|ehF;ls(Z^l1moG*a)pX0gA4EUKh5mIv zoN{}CZ{HgA#=jPhD&H?PCH=d6yH;)bsHR`;%g%T&>W9X^QjW*`eZsB`r~3Qia9>!) z8IdmOf2Dq^>#rGaqV!Aoadr2Z(C?|RAL!RL-&-k{i~jdm*dE%GRqkC)|H5K%R-8w= zZ!5p^!F^)6uEf;atRvkg`U-Kc#)&YlmiU)^c3@$jcgEAxe?YlBDSlz%Sn@-Df{62H z@qKMMzGj^B3+wl5#cwXp?#Fn$L9s7UAEAH1_rr6)RN9O5KJA*d#*g`2VwRfsqRPWh1hW^>5N| zIW9K#0`>EldTsT}GjWVp(~dy>uDTz%e_t?;M&&p6;pYA*x5-*_Mssj}7G-5B_Y=?I z2=?F*3WBFA)0>g=p65JPd`}#r{DJY$e!PdSujzw}tzJAf*NrTuC#&C(algKPyc8d* z)`x@j;Qr&Zi!gqux*m!zYvm_e;JOvk4gExncK79*X(# z!(Mr$oAU8E?CqP)eTg^YbQ!(V?$7;>irT(fv zRQfL{-~4!M#U&>&gx<1s2TReHzdj}S9?Heq{YAe??fwed?YS<2xYFpo8gIgRVEi_; zo7SoqzjqOox9W#}uHY)hk!gPi`7`ahjHlFnT@+_%T(HL5Y8*G|UE}DCf0=TL<#61{ z!$=P-r_zq9{e89hy03@&c$#>M`ho8IYH$I^)$D^bx)8si>uim`&iLDXJa6JW7Ry(z z&rwb)KU4c>-uvzt`5EP_u0JsJ$N6RAtIhg)<@2cuy5)Kc#wXI=uW^Fo3;IRv{p_D# zM#o0(s9wbV>&PXQf3RNp0RNyKdG-pPS38E!f3EC#vtB@PE9D8FCmuHI49IV27o(nI z{7;qsEUr5@aSLjns_sYCzs+^i{t4+<#sL^UO}Sw9?dSLE)UHQ7%$vXWr_N<~V{^rNURO>y<@y9YQ zs1n}<{U{pmWaPGq|MAY=)yRC%1la{YP4_l%zy_Tr|>>EVdrp3ANC{&D?UL4UE)t0)H;r@?v1IDlY% zjK8~jJv6^Ed|K$i*bx{H7PPZ1{+%q1+Y0J?g zE_jb|m!=<-?{a*vu71h=$y9IAcu|f|@cW}olioG1qx^f029NVs?S+irA&#g0lD1^x zW3@9HysP`Ms2;IP_P5n_H`*UpQ_l>}ubCfvpACPV3-OM|&zpU7g8N{o9jbDl%G&*0 zYyXYK{1z;Cm#nkn+~&F(9aqM4k{|kMe*fFwAB1{X#xs$ljs9lVdoQov zyF|_p$hr*DM=wSe>~=cd!MHrNM^X&!OYMnMpft`Ti5bUOV|7wXX-; z!~R=*KUR%HTh8}+4Q^Gx^5Xt|^^+UCp#5@n{Tj9R8w>jNOnjm0pN!+G#<9eOCcZcr z-$?%g=Y@`o>icR(4$|G#=LZ@u#(BVXF03!@isk!N`2K0y1N1w=i+}&N4+WV|`!MCv z=W@TdSpA)}<={$w2P{~B-LJy8uLSLc#0mUvCiy&ZKlL(Whwq^G6ugTEevHVCCd;t-9S&^SweFOc>!@X8qn2`374_59M6-+Z9?Zj%&RC}bZx6t+e zjO*5Y`6~DMGV6XWFTbcY=zg4+-&dOZej2-JP+l)DE|+n0SF#`PQh1c(UA-TQ(O=lF zv_D+wx=W2$CjY1Ys{VM+TgAV@{X29&F=OXddgb_1uQPG{!Tk|wCsh7I`s8~wK3)Br z)DpO#35`y@uv3g z=|$od#_i@&uK3v|I+8VPWUIalc@fwb_A{uB|kOa9iji9-`Hf_2kC|1BcLCS z_7SclthGN6zeh*^5yw;4qXze7tgW|};|azclYemivgYUhG)IuCxNi2*U7DXRy&t;a zqf6@Ti! zU^_CNAANCql<{-wJ`0>bv{zH!YdQR0A?1R8-;TIm{Smq!FzM;)>zzIxUz1ny5drosT>`z$?=67G*pB2=L%H!=S`^V@{#C40kN9}IDznuH)Gr!us%I#-Cds_Ma zu|uBUP5U>PkLByS_T}}*5%Hh-bY2UeoAtNmm-{R$uH7#3UHt-w#V;1rKNwHQ@(f?x z?!AXG&$Z+_XPL@1Uk_D&t8oCt*%1XMYePv%o|6I;aSxLu>&2yGx?DpmI zgz-q6?@Gt2H*Jx93|MdKKjb6SHI;7$?YXokP+zBhLpve)uGt^txg%d#f8CF;d|t61 z7Vodc<%}6m|GPY9U99dCqJ9vAe{{dj^7slvpI1+x(Dm}mv$G!+y^DG)zmrP7wfKIZ zy1yUS@96i9bzK$v<)G~2J&A!pzx&^Rre0v?1IH`4Z>ai{{O^@8pV>#Soc{GYJW7Yg zU**evza8b@uU(FvHG#7-|4Hv-Lg(fCpc0??^b~9t?NyAcC?OW70IqxV( zXvZ^jq52f@9OtFRP0zrU{hVs=FBDu)v6MVidJopGTrO7L&jkFXi^;|1^&k1(a@s?G ze!uMKp!Qm|19Dvj`P0ZZGFgIiD<_rz)S9j_Y8%7VA&?3%O20hXgquAf(I}(h?u9S~UkIPc=w&mZG34Zrpar`-%7sRnUgg+as0$V)&n))i+MSREaa?uK-^874@<$LO*81&+!7%w$1{D$A9J?ni3XrJtB z7K}gPK2`ia7xj1DCxmj2arD}MweaTEjiWL43XbDsV4p{o@ANkX_c!7GF2q-P@h9u| zzx;Izy%KLrdycVRSB@jgo0TsZ^1BsRbN#I9J-*&cJ0aVpbRXP*N7sQ@$Hy?<)$pz0 z{$#WRkKzzcW7`tnaaoQW4aO%P_P!@YorUWvRet$#43+zT(7sLl<*&zPJGg%)**yLf6w`C zz6(gZ6LBg1BJ4lz14nz3`T=x*P{jlEM^b(%9xngRqrv6mLuTJu;#}eo<%7X^5cLEvjZ`YcK<~s&Uz27{qbfI=-+GUeMkHk~7w;KMWeu|=3 zKekuv=YQX$9Jf<0l8;j#sNQF!Htq_}r)oK_>kWTWyj9-5rEr$ccgDTYA3#4Par!=&s|GA7^C8Ns;3dklH>T$>Kd^_ivlUvPe^zPtGRc;?&1-eo&!x1wI1 zuD}!R0&Og>8BX)P<0Mdlo z**44iV(#;w6uE!KTbE9|)E4;$FWX$Z{^j;_pWZ1ig8QRhPA=&Bg(1w#JPyVeDc>#U zU#yqP8SZaBjxkKgJiNS|%!nOH*F70~HSKBiKNH^&@Az@76X?aT%sb+A^PPb5ecEYH zF#9gm?oZ$TxY#_@e&f0-zg_D8+lt`@x}qO=IqP(|UlIA1?h8S_$@X%8Tk2J;gZbX7 zwpZn}`p0YGo*)h;|1YNx{ay_FXZh`1uKcO3e^TqE-mp*jaB%#UPR+iKKAs4UqkgA# zF?xPxB02x()F&MhvnbRGxt>vT;1`~sOA4L`yO9y zzfCPXkyzjlzFc2Au5&SeD)|xj!KS=8TY>kcWWU;S`|@IO-iEJLj%&G{%P(LO9ul`Y@fO??r^P~EKKc9&s z(vs$P;y8}9>r%hg_`z~{ZTw{97bd>Yd>{8}+Rv!}8UGb&S#hHIo+rNpsrC$i-DA0Z zk=VrSmso2br4f;5W`BCFfA;s0@%zE-XHxq+!yB+hhsbZX+^&&Yh?dT8n=<=>m8eNW?5 zs@IMAa;MVH8r*l7_|5DKQJIhN;l!_uk5m0|u{dA$3*+zUA37ty#DmHYRgdI;0i6Hb zCqd``gJK0V-yv5VKsq(?XT$+IE*eL$nBRf z{8RTaq%lt~Dm-6oz>Au?Z*V8Vh`{EMkP+y&rc=NQx zS8_c&{gbuqL(7{l`rR{r|IPQC5U){QaGsd&?~=Z#XAp;w9}EZ`4hc@36#RLm>j{j1 znDnoHmg@aThfOn(zk6MxYE&R?_tzs6^(JYawE`*zxIX=E1d7qj0s z_1|(kCUG9=oBNqjzEhr(&M60~`BwQlGJQY!3ERQ?7DX=7ZxNg)YVTZLT;`#&^+Bee zIA6#|IG%fP$Xi#acBUV{o|o&da=c1ed-?Cv`SF;v%bI$te#iJuUB^+7_Nafy_>Z}c z-NbkL_S#zWpZ#v={;=$6tnpvmXOz~FVc8#p>(j_58Be3QiTs^>k#g(^#*xGAf&GyN zynP(UyziTFU82gL(|93p+x^&x9=r(m;|_G=KCH)Dya?U61NY(Xg**3Q9oFJzSPkPS zNPE$EGseZwH2^dYA}Mwc7ihj2gXj~_^8nD1!yHQ3h$2cTAdM`hVKr;YMG~{X#y(`x z4HsuI1s8dgWH)49%VA3FI0d*!Ne!`aFVZNX8%bm_k8YfXE59Y=yi$07ll;SN6UfRs zWX3Mg&y-r^O{`fr`1k+a{s!mzC*GF$AK!iA-#kCoe`^P&b-+TM;_WIvHckkq? zZC`nX9jy<4@6x49aYXEu?M{;o8*o~k)>fx|1+TR_?X6Bb(@kMmE9=AEaITAVTb6Sj zPj8UZ8+dw)oZb@ZMt!*3vg$+Ku$sE9c4w^?x-rxZJKAkIz0UT=?y%)-bZ&7rI&15? zZJw`f=I<>gd#D>NET$#g1Lry(Zs6gTP!H-uJ+MPPXtARqJ6dN)qY+rEobCuL=h{^v zz_z0ep{R3hn4J)|c(8d0@qmj!DC%^Fzklh{rSNwzUAh#Cw$+EC&UH?AsH1JC-O~2L zmX?mTKC5k@sXJWnohRy>x}$AFEiElA&LL-_zB{ama5vf_JRGg>4q481&UG!(Zrh5r z-52gbTO<^1+Z>9v-5iRxB|}liWnMZ+U2LV(?)__Znn+MiQwtibdij%=wbVtO8~lIaZmYij znyR!6-f*4e+`_{RA)tjVY>ARCFaBAi&R)gQx9pZWZ$zUypbg#VXuJ4XD~>w( zZ5s%8BaV7lZ5O}bT>N5GPFf<}XuJ4@C_)_WhHl=xuI=I%B2gayPkUFh+qMyeXDLyn z-D(l7kRm}zV?)G10@Sk7I)Ix<{%Pz00_@mH92Yti1}m>^q7m3k@TV!6<2p3JaV#(#M+}}9rRnfY&v6}`H3z4^3}rR}h9L&e5fco9a}x~Coq6lx zkgizGdTIN@vAH8O6N6_C7we)aG#j$d5dp`#D70yCJM*$J^sQk^gWGsA+PS=B+$JmB zl*}kYL9#L{!&Qsh!@*oMIH#>`XYh^T3nnxdP0Px0(G=@B0EXV=EXxqh(%?znVxn2F z3``jlK6V4%8D;6$UISy;A!3EdEvDM|D? z^-UK5t>4d78aVIz(Ih5GhKSHjCbavGWbAl9S0+a?%mMmAy8C+Zwx6l(7J9pw_^+>JzK?Loz6(>qncEgZ5|Ct84^cu$b#efSy zR0fv-U>k5{3AHDe9{w&5Y{ z&}U2@(BIV?a1VCqKF(d(ppnn=FaJKq9hb%70j_ytup}$gwhcZcbgvAnhXzEj2Ql?u zrBPT9VI`762>z&Y$>eGNM~1i$$7&zE>vj_9YtpHXe&*;d9>51+02}GAfTGW9VGZ@F zLPEjRsN%(Vq`M6Y5_*1q3=!#6p)&RhPy-H(yy=oIe)9`{6CKuL3V^C1tkJL)*(Ok- z`V9N75wA5+^#3>^+<_Q6&?jd5Z~)yQrxmiXF>J^B3ek@J$D^}t&5>W5kU&h&y~1b6 zo%ZguZ}T)}8B%(G72m_aC&|e($DzFZ>6hB#Q9G1}DoKMls(UvoRZps@74HVoe%*Vt zb$4dolWEtFcKtApRNd>V)LUFAPS*T1RqfBizC3D&QCjzUNpvf19jLaS&a{J85~uNA zccvA$Z~1Ava(Kg&Z9fY3RNCDhx3*B6lyU^t*i}(C==M)As#K9xGwA+aG;&bx(FtaRWEmf^v5_J27gHlV>pYIidU4ueqbtMr_x zA04Bf#92L3tsXL7Q->;)A#UrQpEjby_zRVIvKK73S}INJ-ku+(YUmV!%)WumkZNuA m1oztPCEP@JUcek*55mBG6J@ayff6yM7KQT_!&Bw8*2 literal 0 HcmV?d00001 diff --git a/installer.ps1 b/installer.ps1 index 4931081..b690824 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -37,7 +37,7 @@ Param( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.7" +$installerVersion = "v0.0.8" $configFiles = @("config.h", "myAutomation.h", "myHal.cpp", "mySetup.h") $wifiBoards = @("arduino:avr:mega", "esp32:esp32:esp32") $userDirectory = $env:USERPROFILE + "\" @@ -431,12 +431,12 @@ if ($PSBoundParameters.ContainsKey('configDirectory')) { If no config directory provided, prompt for display option ############################################> Write-Output "`r`nIf you have an LCD or OLED display connected, you can configure it here`r`n" - $displaySelect = 1 + Write-Output "1 - I have no display, skip this step" + $displaySelect = 2 foreach ($display in $displayList) { - Write-Output "$displaySelect - $($displayList[$displaySelect - 1].option)" + Write-Output "$displaySelect - $($displayList[$displaySelect - 2].option)" $displaySelect++ } - Write-Output "$($displayList.Count + 1) - I have no display" Write-Output "$($displayList.Count + 2) - Exit" do { [int]$displayChoice = Read-Host "`r`nSelect a display option" @@ -445,9 +445,9 @@ If no config directory provided, prompt for display option ) if ($displayChoice -eq ($displayList.Count + 2)) { Exit - } elseif ($displayChoice -le ($displayList.Count)) { + } elseif ($displayChoice -ge 2) { $configLines+= "// Display configuration" - $configLines+= "$($displayList[$displayChoice - 1].configLine)" + $configLines+= "$($displayList[$displayChoice - 2].configLine)" $configLines+= "#define SCROLLMODE 1 // Alternate between pages" } <############################################ @@ -535,3 +535,6 @@ if ($output.success -eq "True") { Write-Output "Builder result: $($output.builder_result)`r`n" } } + +Write-Output "`r`nPress any key to exit the installer" +[void][System.Console]::ReadKey($true) From 3e9537281664e50f4bacae7fb91e0c39555ce0bd Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:48:35 +1000 Subject: [PATCH 25/25] Rename installer exe --- ...ler.exe => EX-CommandStation-installer.exe | Bin 75264 -> 75264 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename installer.exe => EX-CommandStation-installer.exe (80%) diff --git a/installer.exe b/EX-CommandStation-installer.exe similarity index 80% rename from installer.exe rename to EX-CommandStation-installer.exe index f783eb2c325a77da38f9b6a62d96ec446f83d8b2..88f12a243dc84a5eda81c224c52548563bbd7f4c 100644 GIT binary patch delta 5399 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 delta 5297 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