From 703c6374c69fefb5099510f4f382b6f8a7c07e87 Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Mon, 31 Jan 2022 07:22:27 -0700 Subject: [PATCH 01/11] Added SD build script --- .gitignore | 4 + docs/UI Sandbox.vsdx | Bin 0 -> 25423 bytes .../Models/ConfigurationRepository.cs | 17 ++- src/Linux/build-sd-card.sh | 131 ++++++++++++++++++ 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 docs/UI Sandbox.vsdx create mode 100644 src/Linux/build-sd-card.sh diff --git a/.gitignore b/.gitignore index 0969583..3994162 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,11 @@ build/ bld/ [Bb]in/ [Oo]bj/ +sdBuild/ +# Build app local files +SpyderTallyApp.zip +config.txt # Visual Studo 2015 cache/options directory .vs/ diff --git a/docs/UI Sandbox.vsdx b/docs/UI Sandbox.vsdx new file mode 100644 index 0000000000000000000000000000000000000000..f9fa9697387908ff29bb84742ef56167c0bb20cd GIT binary patch literal 25423 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 literal 0 HcmV?d00001 diff --git a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/ConfigurationRepository.cs b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/ConfigurationRepository.cs index 13632e7..1b17701 100644 --- a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/ConfigurationRepository.cs +++ b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/ConfigurationRepository.cs @@ -52,7 +52,18 @@ public ConfigurationRepository() private string GetFullFilePath(string fileName) { - return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + string configPath = AppDomain.CurrentDomain.BaseDirectory; + + //Look to see if a path is specified in the app args, in which case look there first for the file + string[] args = Environment.GetCommandLineArgs(); + if(args != null && args.Length > 0) + { + if(Directory.Exists(args[0])) + { + configPath = args[0]; + } + } + return Path.Combine(configPath, fileName); } private T Load(string fileName) @@ -79,7 +90,7 @@ private T Load(string fileName) private void Save(string fileName, T value) { - string configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + string configFile = GetFullFilePath(fileName); using var stream = File.Create(configFile); JsonSerializer.Serialize(stream, value, new JsonSerializerOptions() { @@ -93,7 +104,7 @@ private void Save(string fileName, T value) private async Task SaveAsync(string fileName, T value) { - string configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + string configFile = GetFullFilePath(fileName); using var stream = File.Create(configFile); await JsonSerializer.SerializeAsync(stream, value, new JsonSerializerOptions() { diff --git a/src/Linux/build-sd-card.sh b/src/Linux/build-sd-card.sh new file mode 100644 index 0000000..59f1cfe --- /dev/null +++ b/src/Linux/build-sd-card.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +# Download the latest Raspberry pi lite image +# Instructions from https://geoffhudik.com/tech/2020/04/27/scripting-raspberry-pi-image-builds/ +loop_1_device=/dev/loop2p1 +loop_2_device=/dev/loop2p2 +image_path=./sdBuild/download +mount_path=./sdBuild/mount +netcore_gzip=$image_path/netcore.tar.gz +image_zip=$image_path/osImage.zip +image_iso=$image_path/osImage.img +image_output=$image_path/image.tar.gz +tally_app_zip=./SpyderTallyApp.zip + +# Ensure the app is built before continuing +if [ ! -f "$tally_app_zip" ]; then + echo "SpyderTallyApp.zip not found. Please build the app before continuing." + exit -1 +fi + +# Download the latest .Net Core image +if [ ! -f $image_zip ]; then + mkdir -p $image_path + echo "Downloading .Net core framework" + # curl often gave "error 18 - transfer closed with outstanding read data remaining" + #wget -O $netcore_gzip "https://download.visualstudio.microsoft.com/download/pr/72888385-910d-4ef3-bae2-c08c28e42af0/59be90572fdcc10766f1baf5ac39529a/dotnet-sdk-6.0.101-linux-arm.tar.gz" + wget -O $netcore_gzip "https://download.visualstudio.microsoft.com/download/pr/ff3b2714-0dee-4cf9-94ee-cb9f5ded285f/d6bfe8668428f9eb28acdf6b6f5a81bc/aspnetcore-runtime-6.0.1-linux-arm.tar.gz" + + if [ $? -ne 0 ]; then + echo "Download failed" ; exit -1; + fi +fi + +# Download Raspberry Pi image +if [ ! -f $image_zip ]; then + mkdir -p $image_path + echo "Downloading latest Raspbian lite image" + # curl often gave "error 18 - transfer closed with outstanding read data remaining" + wget -O $image_zip "https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2022-01-28/2022-01-28-raspios-bullseye-armhf-lite.zip" + + if [ $? -ne 0 ]; then + echo "Download failed" ; exit -1; + fi +fi + +echo "Extracting ${image_zip} ISO" +unzip -p $image_zip > $image_iso + +if [ $? -ne 0 ]; then + echo "Unzipping image ${image_zip} failed" ; exit -1; +fi + +# Now we're going to have to extend the image size to make room for the app +# 400Mb added below +echo "Extending image size..." +parted $image_iso print +dd if=/dev/zero bs=1M count=1000 >> $image_iso +parted $image_iso resizepart 2 100% + +# Find partitions on this image and many them available to the +# build server. +losetup --find -P $image_iso + +# Resize partition (take two) +echo "Resizing root partition to add space" +e2fsck -f $loop_2_device +resize2fs $loop_2_device + +echo "Completed extending image size" +parted $image_iso print + +# Mount the boot partition +echo "Mounting boot partition" +mkdir -p $mount_path +mount $loop_1_device $mount_path + +# turn on I2C for front panel display control +echo "Enabling I2C in config.txt" +cp $mount_path/config.txt $image_path/config.txt +echo "dtparam=i2c_arm=on" >> $image_path/config.txt +echo "dtparam=i2c1=on" >> $image_path/config.txt +cp $image_path/config.txt $mount_path/config.txt + +# Add default config files (experimental) +cp -r ../../docs/Linux/appConfig.json $image_path/appConfig.json +cp -r ../../docs/Linux/deviceConfig.json $image_path/deviceConfig.json + +echo "Unmounting boot partition" +umount $mount_path + +# Mount the root partition, and copy any files from filesToAdd +# to the partition. +echo "Mounting root partition" +mount $loop_2_device $mount_path + +# Install .Net Core +echo "Installing .Net Core to ~/dotnet" +local_dotnet_path=/home/pi/dotnet +dotnet_path=$mount_path$local_dotnet_path +mkdir -p $dotnet_path +tar -zxf $netcore_gzip -C $dotnet_path +echo "export PATH=$PATH:\${local_dotnet_path}" >> $mount_path/home/pi/.bashrc +echo "export DOTNET_ROOT=${local_dotnet_path}" >> $mount_path/home/pi/.bashrc + +# Install the app +local_app_path=/home/pi/SpyderTallyApp +app_path=$mount_path$local_app_path +echo "Installing Spyder Tally app to ${local_app_path}" +mkdir -p $app_path +unzip -q $tally_app_zip -d $app_path + +# Add default config files (will be used if not found in /boot) +cp -r ../../docs/Linux/appConfig.json $app_path/appConfig.json +cp -r ../../docs/Linux/deviceConfig.json $app_path/deviceConfig.json +chmod ugo+rw $app_path/*.json + +# Sending app parameter of /boot to the app, which will be where we try to load the config from +echo "Creating Crontab entry to run app on boot" +(crontab -l; echo "@reboot ${local_dotnet_path}/dotnet ${local_app_path}/SpyderTallyControllerWebApp.dll /boot &") | sort -u | crontab - + +echo "Unounting root partition" +umount $mount_path + +# Don't need the loopback device anymore, disconnect it. +losetup -D + +# Zip the output image +echo "Compressing image to ${image_output}" +tar -czf $image_output $image_iso + +echo "Finished building the image!" \ No newline at end of file From a52c7d683e29d9113b3a2cf210fd9acb7ef3d3c4 Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Sat, 17 Sep 2022 01:18:02 +0100 Subject: [PATCH 02/11] Updates to front panel - Added blinking cursor to top-right corner - Centering front panel tally icons - Changed front panel tally icons from coin shapes to circle shapes --- .../Models/DisplayRepository.cs | 100 +++++++++++++----- 1 file changed, 71 insertions(+), 29 deletions(-) diff --git a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/DisplayRepository.cs b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/DisplayRepository.cs index 69e77b5..6e05104 100644 --- a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/DisplayRepository.cs +++ b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/DisplayRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Device.Gpio; using System.Device.I2c; using System.Threading; @@ -10,8 +10,11 @@ namespace SpyderTallyControllerWebApp.Models { public class DisplayRepository : IDisplayRepository { + private const int displayWidth = 16; + private const int filledCircleLocation = 0; private const int emptyCircleLocation = 1; + private const int blinkIconLocation = 2; private readonly I2cDevice i2c; private readonly Pcf8574 driver; @@ -19,10 +22,14 @@ public class DisplayRepository : IDisplayRepository private readonly IRelayRepository relayRepository; + private readonly object displayLock = new object(); private DisplayMode displayMode = DisplayMode.Normal; private string manualTextLine1 = ""; private string manualTextLine2 = ""; + private bool isBlinkIconOn; + private Timer blinkTimer; + public DisplayRepository(IRelayRepository relayRepository) { this.relayRepository = relayRepository; @@ -42,34 +49,49 @@ public DisplayRepository(IRelayRepository relayRepository) //Create filled 0 charcater byte[] filledCircle = new byte[] { + 0x00, // 00000 + 0x00, // 00000 0x0E, // 0XXX0 0x1F, // XXXXX 0x1F, // XXXXX 0x1F, // XXXXX - 0x1F, // XXXXX - 0x1F, // XXXXX - 0x1F, // XXXXX 0x0E, // 0XXX0 + 0x00, // 00000 }; lcd.CreateCustomCharacter(filledCircleLocation, filledCircle); byte[] emptyCircle = new byte[] { + 0x00, // 00000 + 0x00, // 00000 0x0E, // 0XXX0 0x11, // X000X 0x11, // X000X 0x11, // X000X - 0x11, // X000X - 0x11, // X000X - 0x11, // X000X 0x0E, // 0XXX0 + 0x00, // 00000 }; lcd.CreateCustomCharacter(emptyCircleLocation, emptyCircle); + byte[] blinkIcon = new byte[] + { + 0x00, // 00000 + 0x00, // 00000 + 0x00, // 00000 + 0x06, // 00XX0 + 0x06, // 00XX0 + 0x00, // 00000 + 0x00, // 00000 + 0x00, // 00000 + }; + lcd.CreateCustomCharacter(blinkIconLocation, blinkIcon); + lcd.Clear(); lcd.DisplayOn = true; UpdateDisplay(); + + blinkTimer = new Timer(OnBlinkTimer, null, TimeSpan.FromSeconds(0.8), TimeSpan.FromSeconds(0.8)); } private void RelayRepository_RelayStatusChanged(object sender, EventArgs e) @@ -95,31 +117,51 @@ public void SetText(string line1, string line2) UpdateDisplay(); } - public void UpdateDisplay() + public void UpdateDisplay(bool blinkCursorOnly = false) { - string text1, text2; - if (displayMode == DisplayMode.Normal) - { - text1 = Dns.GetHostAddresses(Dns.GetHostName()) - .Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && ip.GetAddressBytes()[0] != 127) - .FirstOrDefault()? - .ToString() ?? ""; - - text2 = String.Join(null, relayRepository.GetRelayStatus().Select(isOn => isOn ? (char)filledCircleLocation : (char)emptyCircleLocation)); - - //Debug - Console.WriteLine(string.Join(Environment.NewLine, Dns.GetHostAddresses(Dns.GetHostName()).ToList())); - } - else + lock (displayLock) { - text1 = manualTextLine1; - text2 = manualTextLine2; - } - lcd.SetCursorPosition(0, 0); - lcd.Write(text1); + if (!blinkCursorOnly) + { + string text1, text2; + if (displayMode == DisplayMode.Normal) + { + text1 = Dns.GetHostAddresses(Dns.GetHostName()) + .Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && ip.GetAddressBytes()[0] != 127) + .FirstOrDefault()? + .ToString() ?? ""; + + var relays = relayRepository.GetRelayStatus(); + text2 = String.Join(null, relays.Select(isOn => isOn ? (char)filledCircleLocation : (char)emptyCircleLocation)) + .PadLeft((displayWidth + relays.Length) / 2); + } + else + { + text1 = manualTextLine1; + text2 = manualTextLine2; + } + + lcd.SetCursorPosition(0, 0); + lcd.Write(text1); + + lcd.SetCursorPosition(0, 1); + lcd.Write(text2); + } + + //Update blink icon + lcd.SetCursorPosition(displayWidth - 1, 0); + if (isBlinkIconOn) + lcd.Write(new char[] { (char)blinkIconLocation }); + else + lcd.Write(" "); + } + } - lcd.SetCursorPosition(0, 1); - lcd.Write(text2); + private void OnBlinkTimer(object state) + { + //Toggle blink + isBlinkIconOn = !isBlinkIconOn; + UpdateDisplay(true); } } } From ff080eef75dd5412adf97969673804dcc6ec5235 Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Sat, 17 Sep 2022 01:47:26 +0100 Subject: [PATCH 03/11] Added content to privacy policy page --- .../SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml index 2479fb7..73de8cf 100644 --- a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml +++ b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml @@ -1,6 +1,10 @@ -@{ +@{ ViewData["Title"] = "Privacy Policy"; }

@ViewData["Title"]

-

Use this page to detail your site's privacy policy.

+

This device assumes no internet connectivity and is designed to work in entirely offline environments. It does not collect or transmit personal or any other kind of information.

+ +

+ Newer updates to this privacy policy, or the software on this device can be found by visiting https://github.com/dsmithson/SpyderTallyController. +

From f6e4c11d8d3cb1e73e7938188dfee42eafe85679 Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Sat, 17 Sep 2022 04:52:41 +0100 Subject: [PATCH 04/11] Small update to privacy page --- .../SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml index 73de8cf..16c6924 100644 --- a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml +++ b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Views/Home/Privacy.cshtml @@ -6,5 +6,5 @@

This device assumes no internet connectivity and is designed to work in entirely offline environments. It does not collect or transmit personal or any other kind of information.

- Newer updates to this privacy policy, or the software on this device can be found by visiting https://github.com/dsmithson/SpyderTallyController. + Newer updates to this privacy policy, or the software on this device can be found by visiting https://github.com/dsmithson/SpyderTallyController.

From 04166a94c146e49e3dfe1bc95c9449e63fc713e6 Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Sat, 17 Sep 2022 05:12:05 +0100 Subject: [PATCH 05/11] Added Linux readme and service file --- docs/Linux/README.md | 38 ++++++++++++++++++++++++++++++++++++++ docs/SpyderTallies.service | 29 +++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 docs/Linux/README.md create mode 100755 docs/SpyderTallies.service diff --git a/docs/Linux/README.md b/docs/Linux/README.md new file mode 100644 index 0000000..6f5e73d --- /dev/null +++ b/docs/Linux/README.md @@ -0,0 +1,38 @@ +# Building the Linux application + +This document is mostly quick notes to help me remember how to build a disk image for the Raspbery pi. I have high hopes to get this image build process into a script that will run as a Github action when a new release branch is created, but I'm not there yet. + +## Stuff to install on a new Raspian Image +- dotnet ([Scripted Install](https://learn.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#scripted-install)) +- nginx (apt-get package install) +- git (sort of optional, depending on whether or not you clone the repo) +- From Raspi-Config + - Enable I2C + - Enable SSH + +## Files to copy over from this repo +- nginx.conf (copy to /etc/nginx/nginx.conf) +- SpyderTallies.service (copy to /etc/systemd/system/SpyderTallies.service) + +## Installing the app +- Clone the git repo: `git clone https://github.com/dsmithson/SpyderTallyController.git` +- cd to the '~/git/SpyderTallyController/src/Linux/SpyderTallyControllerLinux' folder (assuming you cloned into the ~/git folder) +- run `mkdir ~/app` to create a place to publish +- run `dotnet publish -p:PublishDir=/home/dsmithson/app -c Release ./SpyderTallyControllerLinux.sln` to build in release and publish to the ~/app directory + +## Installing the app as a service +- Edit SpyderTallies.service as needed (in repo username is dsmithson at the time of this writing) +- `sudo cp SpyderTallies.service /etc/systemd/system/SpyderTallies.service` +- `sudo systemctl daemon-reload` +- (Make sure the app is running with `sudo systemctl status SpyderTallies`) +- `sudo systemctl enable SpyderTallies` + +As needed, run `sudo systemctl [stop|start|restart] SpyderTallies` to manage service. + +## Cleaning up before/during building an SD card image after configuring +- Delete SSH keys (if added to edit/push code) +- Make sure your user password is 'spyder' +- Make sure the app starts automatically on reboot before sealing an image +- Delete, or at least clean, the repo cloned (if done) to save space +- DD clone the disk image +- Run [PiShrink](https://github.com/Drewsif/PiShrink) to shrink down the image size diff --git a/docs/SpyderTallies.service b/docs/SpyderTallies.service new file mode 100755 index 0000000..f422a5c --- /dev/null +++ b/docs/SpyderTallies.service @@ -0,0 +1,29 @@ +[Unit] +Description=Spyder Tally application by Derek Smithson (Knightware) + +[Service] +# systemd will run this executable to start the service +# if /usr/bin/dotnet doesn't work, use `which dotnet` to find correct dotnet executable path +WorkingDirectory=/home/dsmithson/app +ExecStart=/home/dsmithson/.dotnet/dotnet /home/dsmithson/app/SpyderTallyControllerWebApp.dll + +# to query logs using journalctl, set a logical name here +SyslogIdentifier=SpyderTallies + +# Use your username to keep things simple. +# If you pick a different user, make sure dotnet and all permissions are set correctly to run the app +# To update permissions, use 'chown yourusername -R /srv/HelloWorld' to take ownership of the folder and files, +# Use 'chmod +x /srv/HelloWorld/HelloWorld' to allow execution of the executable file +User=dsmithson + +# ensure the service restarts after crashing +#Restart=always +# amount of time to wait before restarting the service +#RestartSec=5 + +# This environment variable is necessary when dotnet isn't loaded for the specified user. +# To figure out this value, run 'env | grep DOTNET_ROOT' when dotnet has been loaded into your shell. +Environment=DOTNET_ROOT=/home/dsmithson/.dotnet + +[Install] +WantedBy=multi-user.target From 5fbe255b02409a28cc00feb219fd77a52c9ab9db Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Sat, 17 Sep 2022 05:46:12 +0100 Subject: [PATCH 06/11] Service definition updated to wait for network and auto restart on fail --- docs/Linux/README.md | 2 ++ docs/{ => Linux}/SpyderTallies.service | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) rename docs/{ => Linux}/SpyderTallies.service (96%) diff --git a/docs/Linux/README.md b/docs/Linux/README.md index 6f5e73d..9fe63ea 100644 --- a/docs/Linux/README.md +++ b/docs/Linux/README.md @@ -29,6 +29,8 @@ This document is mostly quick notes to help me remember how to build a disk imag As needed, run `sudo systemctl [stop|start|restart] SpyderTallies` to manage service. +Also as needed, run `sudo journalctl -u SpyderTallies -f` to tail the service output log for troubleshooting purposes. + ## Cleaning up before/during building an SD card image after configuring - Delete SSH keys (if added to edit/push code) - Make sure your user password is 'spyder' diff --git a/docs/SpyderTallies.service b/docs/Linux/SpyderTallies.service similarity index 96% rename from docs/SpyderTallies.service rename to docs/Linux/SpyderTallies.service index f422a5c..e160381 100755 --- a/docs/SpyderTallies.service +++ b/docs/Linux/SpyderTallies.service @@ -1,5 +1,6 @@ [Unit] Description=Spyder Tally application by Derek Smithson (Knightware) +After=network.target [Service] # systemd will run this executable to start the service @@ -17,9 +18,9 @@ SyslogIdentifier=SpyderTallies User=dsmithson # ensure the service restarts after crashing -#Restart=always +Restart=always # amount of time to wait before restarting the service -#RestartSec=5 +RestartSec=5 # This environment variable is necessary when dotnet isn't loaded for the specified user. # To figure out this value, run 'env | grep DOTNET_ROOT' when dotnet has been loaded into your shell. From 6da99594bff5db247845375dbfb4ffe53bd8748f Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Sat, 17 Sep 2022 05:46:45 +0100 Subject: [PATCH 07/11] Added DrawingData throttling to 100ms max update rate for overall health/performance --- .../SpyderTallyControllerWebApp/Program.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Program.cs b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Program.cs index 48c2052..f316f5f 100644 --- a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Program.cs +++ b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Program.cs @@ -2,6 +2,11 @@ using SpyderTallyControllerWebApp.Models; var spyderManager = new Spyder.Client.SpyderClientManager(); +spyderManager.ServerListChanged += async (s, e) => +{ + foreach (var server in (await spyderManager.GetServers())) + server.DrawingDataThrottleInterval = TimeSpan.FromMilliseconds(100); +}; await spyderManager.StartupAsync(); var builder = WebApplication.CreateBuilder(args); From ba3db6648d66a57e3c55a6bd4531e06ddcb29524 Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Mon, 31 Jan 2022 07:22:27 -0700 Subject: [PATCH 08/11] Added SD build script --- .gitignore | 4 + docs/UI Sandbox.vsdx | Bin 0 -> 25423 bytes .../Models/ConfigurationRepository.cs | 17 ++- src/Linux/build-sd-card.sh | 131 ++++++++++++++++++ 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 docs/UI Sandbox.vsdx create mode 100644 src/Linux/build-sd-card.sh diff --git a/.gitignore b/.gitignore index 0969583..3994162 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,11 @@ build/ bld/ [Bb]in/ [Oo]bj/ +sdBuild/ +# Build app local files +SpyderTallyApp.zip +config.txt # Visual Studo 2015 cache/options directory .vs/ diff --git a/docs/UI Sandbox.vsdx b/docs/UI Sandbox.vsdx new file mode 100644 index 0000000000000000000000000000000000000000..f9fa9697387908ff29bb84742ef56167c0bb20cd GIT binary patch literal 25423 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 literal 0 HcmV?d00001 diff --git a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/ConfigurationRepository.cs b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/ConfigurationRepository.cs index 13632e7..1b17701 100644 --- a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/ConfigurationRepository.cs +++ b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerWebApp/Models/ConfigurationRepository.cs @@ -52,7 +52,18 @@ public ConfigurationRepository() private string GetFullFilePath(string fileName) { - return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + string configPath = AppDomain.CurrentDomain.BaseDirectory; + + //Look to see if a path is specified in the app args, in which case look there first for the file + string[] args = Environment.GetCommandLineArgs(); + if(args != null && args.Length > 0) + { + if(Directory.Exists(args[0])) + { + configPath = args[0]; + } + } + return Path.Combine(configPath, fileName); } private T Load(string fileName) @@ -79,7 +90,7 @@ private T Load(string fileName) private void Save(string fileName, T value) { - string configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + string configFile = GetFullFilePath(fileName); using var stream = File.Create(configFile); JsonSerializer.Serialize(stream, value, new JsonSerializerOptions() { @@ -93,7 +104,7 @@ private void Save(string fileName, T value) private async Task SaveAsync(string fileName, T value) { - string configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + string configFile = GetFullFilePath(fileName); using var stream = File.Create(configFile); await JsonSerializer.SerializeAsync(stream, value, new JsonSerializerOptions() { diff --git a/src/Linux/build-sd-card.sh b/src/Linux/build-sd-card.sh new file mode 100644 index 0000000..59f1cfe --- /dev/null +++ b/src/Linux/build-sd-card.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +# Download the latest Raspberry pi lite image +# Instructions from https://geoffhudik.com/tech/2020/04/27/scripting-raspberry-pi-image-builds/ +loop_1_device=/dev/loop2p1 +loop_2_device=/dev/loop2p2 +image_path=./sdBuild/download +mount_path=./sdBuild/mount +netcore_gzip=$image_path/netcore.tar.gz +image_zip=$image_path/osImage.zip +image_iso=$image_path/osImage.img +image_output=$image_path/image.tar.gz +tally_app_zip=./SpyderTallyApp.zip + +# Ensure the app is built before continuing +if [ ! -f "$tally_app_zip" ]; then + echo "SpyderTallyApp.zip not found. Please build the app before continuing." + exit -1 +fi + +# Download the latest .Net Core image +if [ ! -f $image_zip ]; then + mkdir -p $image_path + echo "Downloading .Net core framework" + # curl often gave "error 18 - transfer closed with outstanding read data remaining" + #wget -O $netcore_gzip "https://download.visualstudio.microsoft.com/download/pr/72888385-910d-4ef3-bae2-c08c28e42af0/59be90572fdcc10766f1baf5ac39529a/dotnet-sdk-6.0.101-linux-arm.tar.gz" + wget -O $netcore_gzip "https://download.visualstudio.microsoft.com/download/pr/ff3b2714-0dee-4cf9-94ee-cb9f5ded285f/d6bfe8668428f9eb28acdf6b6f5a81bc/aspnetcore-runtime-6.0.1-linux-arm.tar.gz" + + if [ $? -ne 0 ]; then + echo "Download failed" ; exit -1; + fi +fi + +# Download Raspberry Pi image +if [ ! -f $image_zip ]; then + mkdir -p $image_path + echo "Downloading latest Raspbian lite image" + # curl often gave "error 18 - transfer closed with outstanding read data remaining" + wget -O $image_zip "https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2022-01-28/2022-01-28-raspios-bullseye-armhf-lite.zip" + + if [ $? -ne 0 ]; then + echo "Download failed" ; exit -1; + fi +fi + +echo "Extracting ${image_zip} ISO" +unzip -p $image_zip > $image_iso + +if [ $? -ne 0 ]; then + echo "Unzipping image ${image_zip} failed" ; exit -1; +fi + +# Now we're going to have to extend the image size to make room for the app +# 400Mb added below +echo "Extending image size..." +parted $image_iso print +dd if=/dev/zero bs=1M count=1000 >> $image_iso +parted $image_iso resizepart 2 100% + +# Find partitions on this image and many them available to the +# build server. +losetup --find -P $image_iso + +# Resize partition (take two) +echo "Resizing root partition to add space" +e2fsck -f $loop_2_device +resize2fs $loop_2_device + +echo "Completed extending image size" +parted $image_iso print + +# Mount the boot partition +echo "Mounting boot partition" +mkdir -p $mount_path +mount $loop_1_device $mount_path + +# turn on I2C for front panel display control +echo "Enabling I2C in config.txt" +cp $mount_path/config.txt $image_path/config.txt +echo "dtparam=i2c_arm=on" >> $image_path/config.txt +echo "dtparam=i2c1=on" >> $image_path/config.txt +cp $image_path/config.txt $mount_path/config.txt + +# Add default config files (experimental) +cp -r ../../docs/Linux/appConfig.json $image_path/appConfig.json +cp -r ../../docs/Linux/deviceConfig.json $image_path/deviceConfig.json + +echo "Unmounting boot partition" +umount $mount_path + +# Mount the root partition, and copy any files from filesToAdd +# to the partition. +echo "Mounting root partition" +mount $loop_2_device $mount_path + +# Install .Net Core +echo "Installing .Net Core to ~/dotnet" +local_dotnet_path=/home/pi/dotnet +dotnet_path=$mount_path$local_dotnet_path +mkdir -p $dotnet_path +tar -zxf $netcore_gzip -C $dotnet_path +echo "export PATH=$PATH:\${local_dotnet_path}" >> $mount_path/home/pi/.bashrc +echo "export DOTNET_ROOT=${local_dotnet_path}" >> $mount_path/home/pi/.bashrc + +# Install the app +local_app_path=/home/pi/SpyderTallyApp +app_path=$mount_path$local_app_path +echo "Installing Spyder Tally app to ${local_app_path}" +mkdir -p $app_path +unzip -q $tally_app_zip -d $app_path + +# Add default config files (will be used if not found in /boot) +cp -r ../../docs/Linux/appConfig.json $app_path/appConfig.json +cp -r ../../docs/Linux/deviceConfig.json $app_path/deviceConfig.json +chmod ugo+rw $app_path/*.json + +# Sending app parameter of /boot to the app, which will be where we try to load the config from +echo "Creating Crontab entry to run app on boot" +(crontab -l; echo "@reboot ${local_dotnet_path}/dotnet ${local_app_path}/SpyderTallyControllerWebApp.dll /boot &") | sort -u | crontab - + +echo "Unounting root partition" +umount $mount_path + +# Don't need the loopback device anymore, disconnect it. +losetup -D + +# Zip the output image +echo "Compressing image to ${image_output}" +tar -czf $image_output $image_iso + +echo "Finished building the image!" \ No newline at end of file From 3a2eede8520a18e1c5dc1f160612f24944161069 Mon Sep 17 00:00:00 2001 From: "derek.smithson" Date: Tue, 6 Sep 2022 18:46:56 -0700 Subject: [PATCH 09/11] Updated UniversalWindowsPlatorm Nuget package --- .../SpyderTallyController/SpyderTallyController.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WindowsIotCore/SpyderTallyController/SpyderTallyController.csproj b/src/WindowsIotCore/SpyderTallyController/SpyderTallyController.csproj index bb73196..ca072df 100644 --- a/src/WindowsIotCore/SpyderTallyController/SpyderTallyController.csproj +++ b/src/WindowsIotCore/SpyderTallyController/SpyderTallyController.csproj @@ -129,7 +129,7 @@ - 6.2.13 + 6.2.14 4.1.0 From 0169cc58ebb71faf47b54940635f46566fbadbf4 Mon Sep 17 00:00:00 2001 From: "derek.smithson" Date: Thu, 15 Sep 2022 21:15:02 -0700 Subject: [PATCH 10/11] Updated Nuget packages and added ARM32 build --- .../SpyderTallyControllerLinux.sln | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerLinux.sln b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerLinux.sln index 6388671..f3188f6 100644 --- a/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerLinux.sln +++ b/src/Linux/SpyderTallyControllerLinux/SpyderTallyControllerLinux.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpyderTallyControllerWebApp", "SpyderTallyControllerWebApp\SpyderTallyControllerWebApp.csproj", "{1E5F6F1B-9473-4A89-A69D-5014519E8002}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpyderTallyControllerWebApp", "SpyderTallyControllerWebApp\SpyderTallyControllerWebApp.csproj", "{1E5F6F1B-9473-4A89-A69D-5014519E8002}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C6C53870-AD2A-4954-9991-5A70E45E3B7D}" ProjectSection(SolutionItems) = preProject @@ -13,13 +13,19 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|ARM32 = Debug|ARM32 Release|Any CPU = Release|Any CPU + Release|ARM32 = Release|ARM32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1E5F6F1B-9473-4A89-A69D-5014519E8002}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1E5F6F1B-9473-4A89-A69D-5014519E8002}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E5F6F1B-9473-4A89-A69D-5014519E8002}.Debug|ARM32.ActiveCfg = Debug|ARM32 + {1E5F6F1B-9473-4A89-A69D-5014519E8002}.Debug|ARM32.Build.0 = Debug|ARM32 {1E5F6F1B-9473-4A89-A69D-5014519E8002}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E5F6F1B-9473-4A89-A69D-5014519E8002}.Release|Any CPU.Build.0 = Release|Any CPU + {1E5F6F1B-9473-4A89-A69D-5014519E8002}.Release|ARM32.ActiveCfg = Release|ARM32 + {1E5F6F1B-9473-4A89-A69D-5014519E8002}.Release|ARM32.Build.0 = Release|ARM32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From f574097030c3e45e0c1f43f6deda7771feb41ad1 Mon Sep 17 00:00:00 2001 From: Derek Smithson Date: Fri, 16 Sep 2022 21:53:12 -0700 Subject: [PATCH 11/11] Added nginx.conf file that goes with the Linux image --- src/Linux/nginx.conf | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/Linux/nginx.conf diff --git a/src/Linux/nginx.conf b/src/Linux/nginx.conf new file mode 100644 index 0000000..55e0b3d --- /dev/null +++ b/src/Linux/nginx.conf @@ -0,0 +1,47 @@ +user www-data; +worker_processes auto; +pid /run/nginx.pid; +include /etc/nginx/modules-enabled/*.conf; + +events { + worker_connections 768; +} + +http { + # Basic Settings + sendfile on; + tcp_nopush on; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # SSL Settings + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE + ssl_prefer_server_ciphers on; + + # Logging Settings + # access_log /var/log/nginx/access.log; + # error_log /var/log/nginx/error.log; + + # Gzip Settings + gzip on; + + # gzip_vary on; + # gzip_proxied any; + # gzip_comp_level 6; + # gzip_buffers 16 8k; + # gzip_http_version 1.1; + # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + server { + listen 80 default_server; + location / { + proxy_pass http://127.0.0.1:5000; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + } + } +} +