Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/build sd card #7

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/

Expand Down
40 changes: 40 additions & 0 deletions docs/Linux/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 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.

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'
- 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
30 changes: 30 additions & 0 deletions docs/Linux/SpyderTallies.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[Unit]
Description=Spyder Tally application by Derek Smithson (Knightware)
After=network.target

[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
Binary file added docs/UI Sandbox.vsdx
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(string fileName)
Expand All @@ -79,7 +90,7 @@ private T Load<T>(string fileName)

private void Save<T>(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()
{
Expand All @@ -93,7 +104,7 @@ private void Save<T>(string fileName, T value)

private async Task SaveAsync<T>(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()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Device.Gpio;
using System.Device.I2c;
using System.Threading;
Expand All @@ -10,19 +10,26 @@ 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;
private readonly Lcd1602 lcd;

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;
Expand All @@ -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)
Expand All @@ -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() ?? "<No IP>";

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() ?? "<No IP>";

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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
@{
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>
<p>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.</p>

<p>
Newer updates to this privacy policy, or the software on this device can be found by visiting <a target="_blank" href="https://github.com/dsmithson/SpyderTallyController">https://github.com/dsmithson/SpyderTallyController</a>.
</p>
Loading