Ready to test in Fab

This commit is contained in:
Mike Phares 2023-06-03 19:05:08 -07:00
parent 1f5deedc73
commit 2c38ecb399
46 changed files with 1159 additions and 347 deletions

30
.kanbn/index.md Normal file
View File

@ -0,0 +1,30 @@
---
startedColumns:
- 'In Progress'
completedColumns:
- Done
---
# Barcode-Host
## Backlog
- [update-os](tasks/update-os.md)
- [apt-get-install](tasks/apt-get-install.md)
- [configure-ufw](tasks/configure-ufw.md)
- [install-ubuntu-frame](tasks/install-ubuntu-frame.md)
- [netplan](tasks/netplan.md)
- [install-net-7-0](tasks/install-net-7-0.md)
- [pull-repo](tasks/pull-repo.md)
- [run-secrets-task](tasks/run-secrets-task.md)
- [install-vscode-extensions](tasks/install-vscode-extensions.md)
- [run-test-ports](tasks/run-test-ports.md)
- [publish](tasks/publish.md)
- [create-as-service](tasks/create-as-service.md)
- [setup-nginx](tasks/setup-nginx.md)
## Todo
## In Progress
## Done

View File

@ -0,0 +1,13 @@
---
---
# apt-get-install
sudo -i
apt-get install links unzip net-tools ufw nginx git -y
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

View File

@ -0,0 +1,72 @@
---
---
# configure-ufw
```bash
sudo -i
ufw allow from 192.168.0.0/24 to any port 22 comment "01) SSH"
ufw allow to 0.0.0.0/0 port 80 comment "02) HTTP"
ufw allow to 0.0.0.0/0 port 443 comment "03) HTTPS"
ufw allow to 0.0.0.0/0 port 9418 comment "04) Git"
ufw allow from 192.168.0.0/24 to any port 8005 comment "05) Pi-hole"
ufw allow from 192.168.0.0/24 to any port 8006 comment "06) Ajenti"
ufw allow from 192.168.0.0/24 to any port 8007 comment "07) code-server"
ufw allow from 192.168.0.0/24 to any port 8008 comment "08) Nginx"
ufw allow from 192.168.0.0/24 to any port 5002 comment "09) BaGet"
ufw allow to 0.0.0.0/0 port 5000 comment "10) .netCore"
ufw allow to 0.0.0.0/0 port 5001 comment "11) .netCore"
ufw allow from 192.168.0.0/24 to any port 53 comment "12) DNS"
ufw allow from 192.168.0.0/24 to any port 67 comment "13) Unknown"
ufw allow from 192.168.0.0/24 to any port 9654 comment "14) barcode-server"
ufw allow from 192.168.0.0/24 to any port 8009 comment "15) barcode-server-statistics"
ufw allow from 0.0.0.0/0 to any port 9400 comment "16) dashkiosk"
ufw delete 16
ufw allow from 0.0.0.0/0 to any port 8010 comment "16) Test"
ufw allow from 0.0.0.0/0 to any port 5052 comment "17) NGINdeX.io"
ufw allow from 0.0.0.0/0 to any port 3000 comment "18) gogs"
ufw allow from 0.0.0.0/0 to any port 4001 comment "19) gogs"
ufw delete 19
ufw allow from 192.168.0.0/24 to any port 4001 comment "19) photoview api"
ufw allow from 192.168.0.0/24 to any port 1234 comment "20) photoview ui"
ufw allow from 192.168.0.0/24 to any port 3306 comment "21) mysql"
ufw allow from 192.168.0.0/24 to any port 8011 comment "22) syncthing"
ufw allow from 0.0.0.0/0 to any port 5201 comment "23) iperf3"
ufw enable
ufw status numbered
```
```conf
Status: active
To Action From
-- ------ ----
[ 1] 22 ALLOW IN 192.168.0.0/24 # 01) SSH
[ 2] 80 ALLOW IN Anywhere # 02) HTTP
[ 3] 443 ALLOW IN Anywhere # 03) HTTPS
[ 4] 9418 ALLOW IN Anywhere # 04) Git
[ 5] 8005 ALLOW IN 192.168.0.0/24 # 05) Pi-hole
[ 6] 8006 ALLOW IN 192.168.0.0/24 # 06) Ajenti
[ 7] 8007 ALLOW IN 192.168.0.0/24 # 07) code-server
[ 8] 8008 ALLOW IN 192.168.0.0/24 # 08) Nginx
[ 9] 5002 ALLOW IN 192.168.0.0/24 # 09) BaGet
[10] 5000 ALLOW IN Anywhere # 10) .netCore
[11] 5001 ALLOW IN Anywhere # 11) .netCore
[12] 53 ALLOW IN 192.168.0.0/24 # 12) DNS
[13] 67 ALLOW IN 192.168.0.0/24 # 13) Unknown
[14] 9654 ALLOW IN 192.168.0.0/24 # 14) barcode-server
[15] 8009 ALLOW IN 192.168.0.0/24 # 15) barcode-server-statistics
[16] 8010 ALLOW IN Anywhere # 16) Test
[17] 5052 ALLOW IN Anywhere # 17) NGINdeX.io
[18] 3000 ALLOW IN Anywhere # 18) gogs
[19] 4001 ALLOW IN 192.168.0.0/24 # 19) photoview api
[20] 1234 ALLOW IN 192.168.0.0/24 # 20) photoview ui
[21] 3306 ALLOW IN 192.168.0.0/24 # 21) mysql
[22] 8011 ALLOW IN 192.168.0.0/24 # 22) syncthing
[23] 5201 ALLOW IN Anywhere # 23) iperf3
```
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

View File

@ -0,0 +1,40 @@
---
---
# Create as Service
```bash
sudo -i
echo >/etc/systemd/system/barcode-host.service && nano /etc/systemd/system/barcode-host.service
```
```conf
[Unit]
Description=Barcode Server
After=multi-user.target
[Service]
User=unity4
WorkingDirectory=/var/www/Barcode-Host/Server
ExecStart=/usr/local/bin/dotnet /var/www/Barcode-Host/Server/Barcode.Host.Server.dll
Restart=always
# Environment=ASPNETCORE_ENVIRONMENT=Development
# Environment=ASPNETCORE_ENVIRONMENT=Staging
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target
```
```bash
systemctl daemon-reload
systemctl enable barcode-host.service
systemctl start barcode-host.service
systemctl status barcode-host.service
systemctl stop barcode-host.service
journalctl -fu barcode-host.service
journalctl --rotate
journalctl --vacuum-time=1s
```
## Sub-tasks
- [ ] phares3757
- [x] unity4
- [x] unity5

View File

@ -0,0 +1,22 @@
---
---
# Install .net 7.0
```bask
# https://learn.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#scripted-install
cd /home/unity4
wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh
chmod +x ./dotnet-install.sh
./dotnet-install.sh --channel 7.0
export DOTNET_ROOT=$HOME/.dotnet
export PATH=$PATH:$HOME/.dotnet:$HOME/.dotnet/tools
ln -s /home/unity4/.dotnet/dotnet /usr/local/bin/dotnet
dotnet --info
```
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

View File

@ -0,0 +1,24 @@
---
---
# install-ubuntu-frame
```bash
apt-get install links unzip net-tools -y
snap install dashkiosk
timedatectl set-timezone America/Phoenix
snap restart dashkiosk
snap install ubuntu-frame wpe-webkit-mir-kiosk
snap set wpe-webkit-mir-kiosk daemon=true
snap set wpe-webkit-mir-kiosk url=http://localhost:9400/receiver
wpe-webkit-mir-kiosk.cog http://localhost:9400/receiver
/snap/wpe-webkit-mir-kiosk/current/bin/setup.sh
wpe-webkit-mir-kiosk.cog http://localhost:9400/receiver
snap set ubuntu-frame daemon=true
```
## Sub-tasks
- [ ] phares3757
- [x] unity4
- [x] unity5

View File

@ -0,0 +1,15 @@
---
---
# install-vscode-extensions
- .NET Watch Attach (trottero.dotnetwatchattach)
- C# (ms-dotnettools.csharp)
- Git Graph (mhutchie.git-graph)
- Live Preview (ms-vscode.live-server)
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

60
.kanbn/tasks/netplan.md Normal file
View File

@ -0,0 +1,60 @@
---
---
# netplan
```bash
ip link
```
```echo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 10
link/ether 6c:0b:84:e3:94:11 brd ff:ff:ff:ff:ff:ff
altname enp0s31f6
```
```bash
https://ubuntu.com/server/docs/network-configuration
ip a
ip address show eno1
echo >/etc/netplan/99_config.yaml && nano /etc/netplan/99_config.yaml
```
```bash
network:
version: 2
renderer: networkd
ethernets:
eno1:
addresses:
- 192.168.0.204/24
routes:
- to: default
via: 192.168.0.1
nameservers:
addresses: [192.168.0.1, 8.8.8.8, 4.4.4.4]
```
```bash
netplan apply
ip addr flush eno1
# soft reset
```
```bash
network:
version: 2
renderer: networkd
ethernets:
eno1:
addresses:
- 10.95.154.54/24
routes:
- to: default
via: 10.95.154.1
nameservers:
addresses: [10.95.128.11, 10.64.152.171, 8.8.8.8, 4.4.4.4]
```
## Sub-tasks
- [ ] phares3757
- [x] unity4
- [x] unity5

35
.kanbn/tasks/publish.md Normal file
View File

@ -0,0 +1,35 @@
---
---
# publish
```bash
sudo -i
systemctl stop barcode-host.service
rm -r /var/www/Barcode-Host/Server
mkdir /var/www
mkdir /var/www/Barcode-Host
mkdir /var/www/Barcode-Host/Server
cd /home/unity4/Barcode-Host/Server
dotnet publish --configuration Release --output /var/www/Barcode-Host/Server
cd /var/www/Barcode-Host/Server
dotnet /var/www/Barcode-Host/Server/Barcode.Host.Server.dll
```
```conf
2023-06-03 16:02:25.011 -07:00 [Information] (Barcode.Host.Server.Program.) () () Starting Web Application
2023-06-03 16:02:25.062 -07:00 [Information] (Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager.) () () User profile is available. Using '"/root/.aspnet/DataProtection-Keys"' as key repository; keys will not be encrypted at rest.
2023-06-03 16:02:25.093 -07:00 [Information] (Barcode.Host.Server.HostedService.TimedHostedService.StartAsync) () () Timed Hosted Service: 1234567:3070 running.
2023-06-03 16:02:25.175 -07:00 [Information] (Microsoft.Hosting.Lifetime.) () () Now listening on: "http://localhost:5003"
2023-06-03 16:02:25.175 -07:00 [Information] (Microsoft.Hosting.Lifetime.) () () Application started. Press Ctrl+C to shut down.
2023-06-03 16:02:25.176 -07:00 [Information] (Microsoft.Hosting.Lifetime.) () () Hosting environment: "Production"
2023-06-03 16:02:25.176 -07:00 [Information] (Microsoft.Hosting.Lifetime.) () () Content root path: "/var/www/Barcode-Host/Server"
```
```bash
links http://localhost:5003/api/lastScan
```
## Sub-tasks
- [ ] phares3757
- [x] unity4
- [x] unity5

17
.kanbn/tasks/pull-repo.md Normal file
View File

@ -0,0 +1,17 @@
---
---
# Pull Repo
```bash
cd /home/unity4
git clone http://76df8eca4a6c11fe29a58c3be37543c11389ab93@192.168.0.73:3000/mikepharesjr/Barcode-Host.git
cd Barcode-Host
code .
```
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

View File

@ -0,0 +1,15 @@
---
---
# run-secrets-task
```bash
cd Server
dotnet user-secrets set RootPassword 4hink
```
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

View File

@ -0,0 +1,22 @@
---
---
# run-test-ports
```bash
ls -l /dev/ttyUSB*
```
```echo
# crw-rw---- 1 root dialout 188, 0 Jun 3 14:54 /dev/ttyUSB0
```
```bash
sudo -i
adduser unity4 dialout
reboot
```
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

113
.kanbn/tasks/setup-nginx.md Normal file
View File

@ -0,0 +1,113 @@
---
---
# setup-nginx
```bash
echo >/etc/nginx/sites-available/default && nano /etc/nginx/sites-available/default
```
```conf
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
```
```bash
systemctl restart nginx
lsof -i -P -n | grep LISTEN
links http://192.168.0.204/
nginx -t
nginx -s reload
echo >/etc/nginx/sites-available/Barcode-Host-Server && nano /etc/nginx/sites-available/Barcode-Host-Server
```
```conf
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
```
```bash
echo >/home/unity4/localhost.conf && nano /home/unity4/localhost.conf
```
```conf
[req]
default_bits = 2048
default_keyfile = localhost.key
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_ca
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Arizona
localityName = Locality Name (eg, city)
localityName_default = Mesa
organizationName = Organization Name (eg, company)
organizationName_default = Infineon Technologies Americas Corp.
organizationalUnitName = organizationalunit
organizationalUnitName_default = Development
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = unity4
commonName_max = 64
[req_ext]
subjectAltName = @alt_names
[v3_ca]
subjectAltName = @alt_names
[alt_names]
DNS.1 = unity4
DNS.2 = localhost
DNS.3 = 127.0.0.1
```
```bash
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/localhost.key -out /etc/nginx/localhost.crt -config /home/unity4/localhost.conf
ls /etc/nginx
echo >/etc/nginx/sites-available/Barcode-Host-Server && nano /etc/nginx/sites-available/Barcode-Host-Server
```
```conf
server {
server_name _;
ssl_certificate 'localhost.crt';
listen 443 default_server ssl http2;
ssl_certificate_key 'localhost.key';
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
error_page 500 502 503 504 /50x.html;
listen [::]:443 default_server ssl http2;
location / {
proxy_pass http://localhost:5003;
}
}
```
```bash
ln -s /etc/nginx/sites-available/Barcode-Host-Server /etc/nginx/sites-enabled/Barcode-Host-Server
nginx -t
nginx -s reload
links https://localhost/api/lastscan
links https://unity4/api/lastscan
```
```conf
# C:\Windows\System32\drivers\etc\hosts
192.168.0.204 unity4
```
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

17
.kanbn/tasks/update-os.md Normal file
View File

@ -0,0 +1,17 @@
---
---
# update-os
```bash
apt update
apt upgrade -y
apt install update-manager-core
do-release-upgrade
```
## Sub-tasks
- [x] phares3757
- [x] unity4
- [x] unity5

11
.vscode/settings.json vendored
View File

@ -1,9 +1,12 @@
{
"cSpell.enabled": false,
"files.exclude": {
"**/.git": false
},
"thunder-client.saveToWorkspace": true,
"thunder-client.workspaceRelativePath": ".vscode",
"coverage-gutters.coverageBaseDir": "../.vscode/TestResults/*"
"coverage-gutters.coverageBaseDir": "../.vscode/TestResults/*",
"[markdown]": {
"editor.wordWrap": "off"
},
"files.exclude": {
"**/.git": false
}
}

View File

@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<VSTestLogger>trx</VSTestLogger>

View File

@ -1,5 +0,0 @@
namespace Server.Tests;
public class Class1
{
}

View File

@ -0,0 +1,78 @@
using Barcode.Host.Shared.DataModels;
using Barcode.Host.Shared.Models.Stateless;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
namespace Barcode.Host.Tests;
[TestClass]
public class UnitTestLastScanController
{
#pragma warning disable CS8618
private static ILogger _Logger;
private static string _ControllerName;
private static TestContext _TestContext;
private static WebApplicationFactory<Server.Program> _WebApplicationFactory;
#pragma warning restore
[ClassInitialize]
public static void ClassInitAsync(TestContext testContext)
{
_TestContext = testContext;
_Logger = Log.ForContext<UnitTestLastScanController>();
_WebApplicationFactory = new WebApplicationFactory<Server.Program>();
_ControllerName = nameof(Server.ApiControllers.LastScanController)[..^10];
}
private static void NonThrowTryCatch()
{
try
{ throw new Exception(); }
catch (Exception) { }
}
[TestMethod]
public void TestControllerName()
{
_Logger.Information("Starting Web Application");
Assert.AreEqual(ILastScanController<string>.GetRouteName(), _ControllerName);
_Logger.Information($"{_TestContext?.TestName} completed");
NonThrowTryCatch();
}
#if DEBUG
[Ignore]
#endif
[TestMethod]
public void GetScan()
{
_Logger.Information("Starting Web Application");
IServiceProvider serviceProvider = _WebApplicationFactory.Services.CreateScope().ServiceProvider;
ILastScanService lastScanService = serviceProvider.GetRequiredService<ILastScanService>();
Result<string> result = lastScanService.GetScan();
Assert.IsNotNull(result?.Results);
_Logger.Information($"{_TestContext?.TestName} completed");
NonThrowTryCatch();
}
#if DEBUG
[Ignore]
#endif
[TestMethod]
public async Task GetScanApi()
{
HttpClient httpClient = _WebApplicationFactory.CreateClient();
_Logger.Information("Starting Web Application");
string? json = await httpClient.GetStringAsync($"api/{_ControllerName}/O171927.1.37/");
File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetScan)}.json"), json);
Result<string>? result = System.Text.Json.JsonSerializer.Deserialize<Result<string>>(json);
Assert.IsNotNull(result?.Results);
_Logger.Information($"{_TestContext?.TestName} completed");
NonThrowTryCatch();
}
}

1
Server.Tests/Usings.cs Normal file
View File

@ -0,0 +1 @@
global using Microsoft.VisualStudio.TestTools.UnitTesting;

View File

@ -0,0 +1,20 @@
using Barcode.Host.Shared.Models.Stateless;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
namespace Barcode.Host.Server.ApiControllers;
[Route("api/[controller]")]
public class LastScanController : Controller, ILastScanController<IActionResult>
{
private readonly ILastScanService _LastScanService;
public LastScanController(ILastScanService lastScanService) =>
_LastScanService = lastScanService;
[HttpGet()]
public IActionResult GetScan() =>
Json(_LastScanService.GetScan(), new JsonSerializerOptions { PropertyNamingPolicy = null, WriteIndented = true });
}

View File

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<TargetFramework>net7.0</TargetFramework>
<UserSecretsId>02dce973-df1d-4325-962a-ed549af8d4c5</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
@ -22,6 +22,7 @@
<DefineConstants>Linux</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
@ -31,9 +32,18 @@
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
<PackageReference Include="System.Text.Json" Version="7.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\Barcode.Host.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,186 @@
using Barcode.Host.Server.Models;
using Barcode.Host.Shared.DataModels;
using Barcode.Host.Shared.KeyboardMouse;
using Barcode.Host.Shared.Models.Stateless;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog.Context;
namespace Barcode.Host.Server.HostedService;
public class TimedHostedService : IHostedService, IAggregateInputReader, IDisposable
{
public event InputReader.RaiseKeyPress? OnKeyPress;
private readonly int _ExecutionCount;
private readonly AppSettings _AppSettings;
private readonly ISerialService _SerialService;
private readonly ILastScanService _LastScanService;
private readonly ILogger<TimedHostedService> _Logger;
private readonly ILinuxGroupManager _LinuxGroupManager;
private readonly Dictionary<string, InputReader> _Readers;
private readonly Dictionary<EventCode, char> _CharToEventCodes;
private readonly List<(string MethodName, Timer Timer)> _Timers;
public TimedHostedService(ILogger<TimedHostedService> logger, AppSettings appSettings, ILinuxGroupManager linuxGroupManager, ILastScanService lastScanService, ISerialService serialService)
{
_Readers = new();
_Logger = logger;
_ExecutionCount = 0;
_CharToEventCodes = new();
_AppSettings = appSettings;
_SerialService = serialService;
_LastScanService = lastScanService;
_LinuxGroupManager = linuxGroupManager;
Timer writeTimer = new(Write, null, Timeout.Infinite, Timeout.Infinite);
Timer scanForNewInputsTimer = new(ScanForNewInputs, null, Timeout.Infinite, Timeout.Infinite);
_Timers = new List<(string, Timer)>() { (nameof(Write), writeTimer), (nameof(ScanForNewInputs), scanForNewInputsTimer) };
}
public Task StartAsync(CancellationToken stoppingToken)
{
string? methodName = IMethodName.GetActualAsyncMethodName();
using (LogContext.PushProperty("MethodName", methodName))
{
_Logger.LogInformation($"Timed Hosted Service: {_AppSettings.GitCommitSeven}:{Environment.ProcessId} running.");
_SerialService.Open();
if (!_LinuxGroupManager.IsInInputGroup().WaitAsync(stoppingToken).Result)
{
if (string.IsNullOrEmpty(_AppSettings.RootPassword))
throw new Exception($"Please check appsettings file(s) for <{nameof(_AppSettings.RootPassword)}>!");
_ = _LinuxGroupManager.AddUserToInputGroup(_AppSettings.RootPassword);
_ = _LinuxGroupManager.RebootSystem(_AppSettings.RootPassword);
}
List<(EventCode, char)> collection = _LastScanService.IncludeEventCodes();
foreach ((EventCode eventCode, char @char) in collection)
_CharToEventCodes.Add(eventCode, @char);
int dueTime = 0;
foreach ((string _, Timer timer) in _Timers)
{
dueTime += 300;
_ = timer.Change(dueTime, Timeout.Infinite);
}
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken stoppingToken)
{
string? methodName = IMethodName.GetActualAsyncMethodName();
using (LogContext.PushProperty("MethodName", methodName))
{
_Logger.LogInformation($"Timed Hosted Service: {_AppSettings.GitCommitSeven}:{Environment.ProcessId} is stopping.");
for (short i = 0; i < short.MaxValue; i++)
{
Thread.Sleep(500);
if (_ExecutionCount == 0)
break;
}
}
return Task.CompletedTask;
}
public void Dispose()
{
foreach ((string _, Timer timer) in _Timers)
timer.Dispose();
foreach (InputReader inputReader in _Readers.Values)
{
inputReader.OnKeyPress -= ReaderOnOnKeyPress;
inputReader.Dispose();
}
_Readers.Clear();
_SerialService.Close();
GC.SuppressFinalize(this);
}
private void ReaderOnOnKeyPress(KeyPressEvent e)
{
OnKeyPress?.Invoke(e);
if (e.TimeSpan.TotalMilliseconds > _AppSettings.ClearLastScanServiceAfter)
_LastScanService.Clear();
if (e.KeyState == KeyState.KeyUp && _CharToEventCodes.TryGetValue(e.EventCode, out char @char))
_LastScanService.Add(e.EventCode, @char);
}
private Timer? GetTimer(string methodName)
{
(string MethodName, Timer Timer)[] results = _Timers.Where(l => l.MethodName == methodName).ToArray();
return !results.Any() ? null : results.First().Timer;
}
private void ScanForNewInputs()
{
string fileName;
IEnumerable<LinuxDevice>? devices = null;
string[] files = Directory.GetFiles("/dev/input/", "event*");
foreach (string file in files)
{
if (_Readers is null || _Readers.ContainsKey(file))
continue;
devices ??= DeviceReader.Get(_AppSettings.LinuxDevicePath);
fileName = Path.GetFileName(file);
InputReader reader = new(file, _Logger);
if (devices.Any(l => !string.IsNullOrEmpty(l.Name) && l.Name.EndsWith(_AppSettings.DeviceNameEndsWith) && l.Handlers.Any(m => m == fileName)))
reader.OnKeyPress += ReaderOnOnKeyPress;
_Readers?.Add(file, reader);
}
IEnumerable<InputReader>? deadReaders = _Readers?.Values.Where(r => r.Faulted);
if (deadReaders is not null)
{
foreach (InputReader? inputReader in deadReaders)
{
_ = _Readers?.Remove(inputReader.Path);
inputReader.OnKeyPress -= ReaderOnOnKeyPress;
inputReader.Dispose();
}
}
}
private void ScanForNewInputs(object? sender)
{
try
{ ScanForNewInputs(); }
catch (Exception ex) { _Logger.LogError(ex, nameof(ScanForNewInputs)); }
try
{
Timer? timer = GetTimer(nameof(ScanForNewInputs));
if (timer is not null)
{
TimeSpan timeSpan = new(DateTime.Now.AddSeconds(30).Ticks - DateTime.Now.Ticks);
_ = timer.Change((int)timeSpan.TotalMilliseconds, Timeout.Infinite);
}
}
catch (Exception ex) { _Logger.LogError(ex, $"{nameof(ScanForNewInputs)}-{nameof(Timer)}.{nameof(Timer.Change)}"); }
}
private void Write()
{
int count = _LastScanService.GetCount();
if (count > 0)
{
Result<string> result = _LastScanService.GetScan();
if (!string.IsNullOrEmpty(result.Results))
_SerialService.SerialPortWrite(count, result.Results);
}
}
private void Write(object? sender)
{
try
{ Write(); }
catch (Exception ex) { _Logger.LogError(ex, nameof(Write)); }
try
{
Timer? timer = GetTimer(nameof(Write));
if (timer is not null)
{
TimeSpan timeSpan = new(DateTime.Now.AddMilliseconds(_AppSettings.WriteToSerialEvery).Ticks - DateTime.Now.Ticks);
_ = timer.Change((int)timeSpan.TotalMilliseconds, Timeout.Infinite);
}
}
catch (Exception ex) { _Logger.LogError(ex, $"{nameof(Write)}-{nameof(Timer)}.{nameof(Timer.Change)}"); }
}
}

View File

@ -2,26 +2,23 @@ using System.Text.Json;
namespace Barcode.Host.Server.Models;
public record AppSettings(string ApiExportPath,
string ApiLoggingContentTypes,
string ApiLoggingPathPrefixes,
string ApiLogPath,
string ApiUrl,
string AttachmentPath,
string BuildNumber,
public record AppSettings(string BuildNumber,
string Company,
string ConnectionString,
int ClearLastScanServiceAfter,
string DeviceNameEndsWith,
int ExpectedScanLengthA,
int ExpectedScanLengthB,
string GitCommitSeven,
string InboundApiAllowedIPList,
string LinuxDevicePath,
bool IsDevelopment,
bool IsStaging,
string MockRoot,
string MonAResource,
string MonASite,
string OI2SqlConnectionString,
string OIExportPath,
string RootPassword,
string URLs,
string WorkingDirectoryName)
string WorkingDirectoryName,
int WriteToSerialEvery)
{
public override string ToString()

View File

@ -9,26 +9,23 @@ public class AppSettings
#nullable disable
[Display(Name = "Api Export Path"), Required] public string ApiExportPath { get; set; }
[Display(Name = "Api Logging Content Types"), Required] public string ApiLoggingContentTypes { get; set; }
[Display(Name = "Api Logging Path Prefixes"), Required] public string ApiLoggingPathPrefixes { get; set; }
[Display(Name = "Api Log Path"), Required] public string ApiLogPath { get; set; }
[Display(Name = "Api URL"), Required] public string ApiUrl { get; set; }
[Display(Name = "Attachment Path"), Required] public string AttachmentPath { get; set; }
[Display(Name = "Build Number"), Required] public string BuildNumber { get; set; }
[Display(Name = "Company"), Required] public string Company { get; set; }
[Display(Name = "Connection String"), Required] public string ConnectionString { get; set; }
[Display(Name = "Last Scan Service Clear After"), Required] public int? ClearLastScanServiceAfter { get; set; }
[Display(Name = "Device Name Ends With"), Required] public string DeviceNameEndsWith { get; set; }
[Display(Name = "ExpectedScanLengthA"), Required] public int? ExpectedScanLengthA { get; set; }
[Display(Name = "ExpectedScanLengthB"), Required] public int? ExpectedScanLengthB { get; set; }
[Display(Name = "Git Commit Seven"), Required] public string GitCommitSeven { get; set; }
[Display(Name = "Inbound Api Allowed IP List"), Required] public string InboundApiAllowedIPList { get; set; }
[Display(Name = "Linux Device Path"), Required] public string LinuxDevicePath { get; set; }
[Display(Name = "Is Development"), Required] public bool? IsDevelopment { get; set; }
[Display(Name = "Is Staging"), Required] public bool? IsStaging { get; set; }
[Display(Name = "Mock Root"), Required] public string MockRoot { get; set; }
[Display(Name = "MonA Resource"), Required] public string MonAResource { get; set; }
[Display(Name = "MonA Site"), Required] public string MonASite { get; set; }
[Display(Name = "Oi 2 Sql Connection String"), Required] public string Oi2SqlConnectionString { get; set; }
[Display(Name = "OI Export Path"), Required] public string OIExportPath { get; set; }
[Display(Name = "RootPassword"), Required] public string RootPassword { get; set; }
[Display(Name = "URLs"), Required] public string URLs { get; set; }
[Display(Name = "Working Directory Name"), Required] public string WorkingDirectoryName { get; set; }
[Display(Name = "WriteToSerialEvery"), Required] public int? WriteToSerialEvery { get; set; }
#nullable restore
@ -43,28 +40,22 @@ public class AppSettings
Models.AppSettings result;
if (appSettings is null)
throw new NullReferenceException(nameof(appSettings));
if (appSettings.ApiExportPath is null)
throw new NullReferenceException(nameof(ApiExportPath));
if (appSettings.ApiLoggingContentTypes is null)
throw new NullReferenceException(nameof(ApiLoggingContentTypes));
if (appSettings.ApiLoggingPathPrefixes is null)
throw new NullReferenceException(nameof(ApiLoggingPathPrefixes));
if (appSettings.ApiLogPath is null)
throw new NullReferenceException(nameof(ApiLogPath));
if (appSettings.ApiUrl is null)
throw new NullReferenceException(nameof(ApiUrl));
if (appSettings.AttachmentPath is null)
throw new NullReferenceException(nameof(AttachmentPath));
if (appSettings.BuildNumber is null)
throw new NullReferenceException(nameof(BuildNumber));
if (appSettings.Company is null)
throw new NullReferenceException(nameof(Company));
if (appSettings.ConnectionString is null)
throw new NullReferenceException(nameof(ConnectionString));
if (appSettings.ClearLastScanServiceAfter is null)
throw new NullReferenceException(nameof(ClearLastScanServiceAfter));
if (appSettings.DeviceNameEndsWith is null)
throw new NullReferenceException(nameof(DeviceNameEndsWith));
if (appSettings.ExpectedScanLengthA is null)
throw new NullReferenceException(nameof(ExpectedScanLengthA));
if (appSettings.ExpectedScanLengthB is null)
throw new NullReferenceException(nameof(ExpectedScanLengthB));
if (appSettings.GitCommitSeven is null)
throw new NullReferenceException(nameof(GitCommitSeven));
if (appSettings.InboundApiAllowedIPList is null)
throw new NullReferenceException(nameof(InboundApiAllowedIPList));
if (appSettings.LinuxDevicePath is null)
throw new NullReferenceException(nameof(LinuxDevicePath));
if (appSettings.IsDevelopment is null)
throw new NullReferenceException(nameof(IsDevelopment));
if (appSettings.IsStaging is null)
@ -75,35 +66,32 @@ public class AppSettings
throw new NullReferenceException(nameof(MonAResource));
if (appSettings.MonASite is null)
throw new NullReferenceException(nameof(MonASite));
if (appSettings.Oi2SqlConnectionString is null)
throw new NullReferenceException(nameof(Oi2SqlConnectionString));
if (appSettings.OIExportPath is null)
throw new NullReferenceException(nameof(OIExportPath));
if (appSettings.RootPassword is null)
throw new NullReferenceException(nameof(RootPassword));
if (appSettings.URLs is null)
throw new NullReferenceException(nameof(URLs));
if (appSettings.WorkingDirectoryName is null)
throw new NullReferenceException(nameof(WorkingDirectoryName));
if (appSettings.WriteToSerialEvery is null)
throw new NullReferenceException(nameof(WriteToSerialEvery));
result = new(
appSettings.ApiExportPath,
appSettings.ApiLoggingContentTypes,
appSettings.ApiLoggingPathPrefixes,
appSettings.ApiLogPath,
appSettings.ApiUrl,
appSettings.AttachmentPath,
appSettings.BuildNumber,
appSettings.Company,
appSettings.ConnectionString,
appSettings.ClearLastScanServiceAfter.Value,
appSettings.DeviceNameEndsWith,
appSettings.ExpectedScanLengthA.Value,
appSettings.ExpectedScanLengthB.Value,
appSettings.GitCommitSeven,
appSettings.InboundApiAllowedIPList,
appSettings.LinuxDevicePath,
appSettings.IsDevelopment.Value,
appSettings.IsStaging.Value,
appSettings.MockRoot,
appSettings.MonAResource,
appSettings.MonASite,
appSettings.Oi2SqlConnectionString,
appSettings.OIExportPath,
appSettings.RootPassword,
appSettings.URLs,
appSettings.WorkingDirectoryName);
appSettings.WorkingDirectoryName,
appSettings.WriteToSerialEvery.Value);
return result;
}

View File

@ -1,7 +1,9 @@
using Barcode.Host.Server.Models;
using Barcode.Host.Server.HostedService;
using Barcode.Host.Server.Models;
using Barcode.Host.Server.Services;
using Barcode.Host.Shared.Models;
using Barcode.Host.Shared.Models.Stateless;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@ -35,7 +37,7 @@ public class Program
public static int Main(string[] args)
{
LoggerConfiguration loggerConfiguration = new();
(string assemblyName, WebApplicationOptions _) = Get(args);
(string assemblyName, _) = Get(args);
WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args);
_ = webApplicationBuilder.Configuration.AddUserSecrets<Program>();
AppSettings appSettings = Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration);
@ -49,44 +51,35 @@ public class Program
ILogger log = Log.ForContext<Program>();
try
{
_ = webApplicationBuilder.Services.AddMemoryCache();
_ = webApplicationBuilder.Services.Configure<ApiBehaviorOptions>(options => options.SuppressModelStateInvalidFilter = true);
if (appSettings.IsStaging && appSettings.IsDevelopment)
throw new NotSupportedException("Please check appsettings file(s)!");
if (appSettings.IsStaging != webApplicationBuilder.Environment.IsStaging())
throw new NotSupportedException("Please check appsettings file(s)!");
if (appSettings.IsDevelopment != webApplicationBuilder.Environment.IsDevelopment())
throw new NotSupportedException("Please check appsettings file(s)!");
_ = webApplicationBuilder.Services.AddControllersWithViews();
_ = webApplicationBuilder.Services.AddDistributedMemoryCache();
_ = webApplicationBuilder.Services.AddSingleton(_ => appSettings);
_ = webApplicationBuilder.Services.AddSingleton<ISerialService, SerialService>();
_ = webApplicationBuilder.Services.AddSingleton<ILastScanService, LastScanService>();
_ = webApplicationBuilder.Services.AddSingleton<ILinuxGroupManager, LinuxGroupManager>();
_ = webApplicationBuilder.Services.AddHostedService<TimedHostedService>();
_ = webApplicationBuilder.Services.AddSwaggerGen();
_ = webApplicationBuilder.Services.AddSession(sessionOptions =>
{
sessionOptions.IdleTimeout = TimeSpan.FromSeconds(2000);
sessionOptions.Cookie.HttpOnly = true;
sessionOptions.Cookie.IsEssential = true;
}
);
WebApplication webApplication = webApplicationBuilder.Build();
_ = webApplication.UseCors(corsPolicyBuilder => corsPolicyBuilder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
if (!webApplicationBuilder.Environment.IsDevelopment())
if (appSettings.IsDevelopment)
{
_ = webApplication.UseExceptionHandler("/Error");
_ = webApplication.UseHttpsRedirection();
_ = webApplication.UseHsts();
}
else
{
if (string.IsNullOrEmpty(appSettings.URLs))
{
Environment.ExitCode = -1;
webApplication.Lifetime.StopApplication();
}
_ = webApplication.UseSwagger();
_ = webApplication.UseDeveloperExceptionPage();
_ = webApplication.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server V1"));
}
if (!appSettings.IsDevelopment)
{
_ = webApplication.UseExceptionHandler("/Error");
_ = webApplication.UseHsts();
}
_ = webApplication.UseCors(corsPolicyBuilder => corsPolicyBuilder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
_ = webApplication.Lifetime.ApplicationStopped.Register(Log.CloseAndFlush);
_ = SerilogApplicationBuilderExtensions.UseSerilogRequestLogging(webApplication);
_ = webApplication.UseFileServer(enableDirectoryBrowsing: true);
_ = webApplication.UseStaticFiles();
_ = webApplication.UseSession();
_ = webApplication.MapControllers();
log.Information("Starting Web Application");
webApplication.Run();

View File

@ -0,0 +1,93 @@
using Barcode.Host.Shared.DataModels;
using Barcode.Host.Shared.KeyboardMouse;
using Barcode.Host.Shared.Models.Stateless;
namespace Barcode.Host.Server.Services;
public class LastScanService : ILastScanService
{
private readonly List<(EventCode EventCode, char Char)> _EventCodes;
public LastScanService() =>
_EventCodes = new();
void ILastScanService.Clear()
{
lock (_EventCodes)
_EventCodes.Clear();
}
void ILastScanService.Add(EventCode eventCode, char @char)
{
lock (_EventCodes)
_EventCodes.Add((eventCode, @char));
}
Result<string> ILastScanService.GetScan()
{
Result<string> result;
char[] chars;
lock (_EventCodes)
chars = _EventCodes.Select(l => l.Char).ToArray();
result = new() { Results = new string(chars), TotalRows = chars.Length };
return result;
}
int ILastScanService.GetCount()
{
int result;
lock (_EventCodes)
result = _EventCodes.Count;
return result;
}
List<(EventCode, char)> ILastScanService.IncludeEventCodes()
{
List<(EventCode, char)> results = new()
{
(EventCode.A, 'A'),
(EventCode.B, 'B'),
(EventCode.C, 'C'),
(EventCode.D, 'D'),
(EventCode.E, 'E'),
(EventCode.F, 'F'),
(EventCode.G, 'G'),
(EventCode.H, 'H'),
(EventCode.I, 'I'),
(EventCode.J, 'J'),
(EventCode.K, 'K'),
(EventCode.L, 'L'),
(EventCode.M, 'M'),
(EventCode.N, 'N'),
(EventCode.O, 'O'),
(EventCode.P, 'P'),
(EventCode.Q, 'Q'),
(EventCode.R, 'R'),
(EventCode.S, 'S'),
(EventCode.T, 'T'),
(EventCode.U, 'U'),
(EventCode.V, 'V'),
(EventCode.W, 'W'),
(EventCode.X, 'X'),
(EventCode.Y, 'Y'),
(EventCode.Z, 'Z'),
(EventCode.Num0, '0'),
(EventCode.Num1, '1'),
(EventCode.Num2, '2'),
(EventCode.Num3, '3'),
(EventCode.Num4, '4'),
(EventCode.Num5, '5'),
(EventCode.Num6, '6'),
(EventCode.Num7, '7'),
(EventCode.Num8, '8'),
(EventCode.Num9, '9'),
(EventCode.Minus, '-'),
(EventCode.Dot, '.'),
(EventCode.Slash, '/'),
(EventCode.Space, ' '),
};
return results;
}
}

View File

@ -1,24 +1,23 @@
using Barcode.Host.Shared.Models.Stateless;
using CliWrap;
using CliWrap.Buffered;
namespace Barcode.Host.Shared.Linux;
namespace Barcode.Host.Server.Services;
public class LinuxGroupManager : ILinuxGroupManager
{
public async Task<bool> IsInInputGroup()
{
BufferedCommandResult result = await Cli.Wrap("id")
.ExecuteBufferedAsync();
string output = result.StandardOutput;
const StringSplitOptions options = StringSplitOptions.RemoveEmptyEntries;
bool inInputGroup = output.Split(new[] { ' ' }, options)
.First(p => p.StartsWith("groups"))
.Remove(0, "groups".Length)
.Split(',', options)
.Any(p => p.Contains("input"));
return inInputGroup;
}
@ -26,7 +25,6 @@ public class LinuxGroupManager : ILinuxGroupManager
{
using CancellationTokenSource cts = new();
cts.CancelAfter(TimeSpan.FromSeconds(10));
_ = await Cli.Wrap("bash")
.WithArguments($"-c \"echo '{password}' | sudo -S gpasswd -a $USER input")
.ExecuteBufferedAsync(cts.Token);
@ -36,9 +34,9 @@ public class LinuxGroupManager : ILinuxGroupManager
{
using CancellationTokenSource cts = new();
cts.CancelAfter(TimeSpan.FromSeconds(10));
_ = await Cli.Wrap("bash")
.WithArguments($"-c \"echo '{password}' | sudo -S reboot\"")
.ExecuteBufferedAsync(cts.Token);
}
}

View File

@ -0,0 +1,44 @@
using Barcode.Host.Server.Models;
using Barcode.Host.Shared.Models.Stateless;
using System.Text;
namespace Barcode.Host.Server.Services;
public class SerialService : ISerialService
{
private string _LastRaw;
private readonly AppSettings _AppSettings;
private readonly System.IO.Ports.SerialPort _SerialPort;
public SerialService(AppSettings appSettings)
{
_LastRaw = string.Empty;
_AppSettings = appSettings;
_SerialPort = new("/dev/ttyUSB0", 9600) { ReadTimeout = 2 };
}
void ISerialService.Open() =>
_SerialPort.Open();
void ISerialService.Close() =>
_SerialPort.Close();
void ISerialService.SerialPortWrite(int count, string raw)
{
if (raw != _LastRaw)
{
string message;
if (count == _AppSettings.ExpectedScanLengthA)
message = $" {raw[2..]} {DateTime.Now:h:m tt}";
else if (count == _AppSettings.ExpectedScanLengthB)
message = $" {raw[2..]}";
else
message = $" {raw}";
byte[] bytes = Encoding.ASCII.GetBytes(message);
_SerialPort.Write(bytes, 0, bytes.Length);
_LastRaw = raw;
}
}
}

View File

@ -1,47 +1,13 @@
{
"ApiExportPath": "\\\\messdv002.na.infineon.com\\Candela",
"ApiUrl": "~/api",
"ConnectionString": "Data Source=MESSAD1001\\TEST1,59583;Integrated Security=True;Initial Catalog=Metrology;",
"ExpectedScanLengthA": 6,
"ExpectedScanLengthB": 9,
"Logging": {
"LogLevel": {
"Log4netProvider": "Debug"
}
},
"IsDevelopment": true,
"MockRoot": "",
"MonAResource": "OI_Metrology_Viewer_IFX",
"Oi2SqlConnectionString": "Data Source=MESSAD1001\\TEST1,59583;Initial Catalog=LSL2SQL;Persist Security Info=True;User ID=srpadmin;Password=0okm9ijn;",
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "Debug",
"Args": {
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}.{MethodName}) ({InstanceId}) ({RemoteIpAddress}) {Message}{NewLine}{Exception}"
}
},
{
"Name": "Console",
"Args": {
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}.{MethodName}) ({InstanceId}) ({RemoteIpAddress}) {Message}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "%workingDirectory% - Log/log-.txt",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}.{MethodName}) ({InstanceId}) ({RemoteIpAddress}) {Message}{NewLine}{Exception}",
"rollingInterval": "Hour"
"MinimumLevel": "Debug"
}
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithThreadId"
],
"Properties": {
"Application": "Sample"
}
},
"URLs": "https://localhost:7130;http://localhost:5126"
}

View File

@ -1,31 +1,26 @@
{
"AllowedHosts": "*",
"ApiExportPath": "\\\\messv02ecc1.ec.local\\EC_Metrology_Si",
"ApiLoggingContentTypes": "application/json",
"ApiLoggingPathPrefixes": "/api/inbound",
"ApiUrl": "~/api",
"ApiLogPath": "D:\\Metrology\\MetrologyAPILogs",
"AttachmentPath": "\\\\messv02ecc1.ec.local\\EC_Metrology_Si\\MetrologyAttachments",
"BuildNumber": "1",
"Company": "Infineon Technologies Americas Corp.",
"ConnectionString": "Data Source=messv01ec.ec.local\\PROD1,53959;Integrated Security=True;Initial Catalog=Metrology;",
"DeviceNameEndsWith": "Symbol Bar Code Scanner",
"ExpectedScanLengthA": 8,
"ExpectedScanLengthB": 14,
"ExpectedScanLengthBExample": "1TO172125.1.11",
"ClearLastScanServiceAfter": 250,
"GitCommitSeven": "1234567",
"LinuxDevicePath": "/proc/bus/input/devices",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Log4netProvider": "Debug",
"Log4netProvider": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"InboundApiAllowedIPList": "",
"IsDevelopment": false,
"IsStaging": false,
"MockRoot": "",
"MonAResource": "OI_Metrology_Viewer_EC",
"MonASite": "auc",
"Oi2SqlConnectionString": "Data Source=messv01ec.ec.local\\PROD1,53959;Initial Catalog=LSL2SQL;Persist Security Info=True;User ID=srpadmin;Password=0okm9ijn;",
"OIExportPath": "\\\\openinsight-db-srv.na.infineon.com\\apps\\Metrology\\Data",
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
@ -63,6 +58,8 @@
"Application": "Sample"
}
},
"URLs": "http://localhost:5002;",
"WorkingDirectoryName": "IFXApps"
"RootPassword": "",
"URLs": "http://localhost:5003;",
"WorkingDirectoryName": "IFXApps",
"WriteToSerialEvery": 750
}

View File

@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@ -19,7 +20,6 @@
<DefineConstants>Linux</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.3" />
<PackageReference Include="System.Text.Json" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />

View File

@ -0,0 +1,7 @@
namespace Barcode.Host.Shared.DataModels;
public class Result<T>
{
public T? Results { get; set; }
public long TotalRows { get; set; }
}

View File

@ -1,6 +0,0 @@
namespace Barcode.Host.Shared.KeyboardMouse.Abstract;
public interface IAggregateInputReader
{
event InputReader.RaiseKeyPress OnKeyPress;
}

View File

@ -1,72 +0,0 @@
using Barcode.Host.Shared.KeyboardMouse.Abstract;
using Microsoft.Extensions.Logging;
namespace Barcode.Host.Shared.KeyboardMouse;
public class AggregateInputReader : IDisposable, IAggregateInputReader
{
private readonly ILogger<InputReader> _InputReaderLogger;
private Dictionary<string, InputReader>? _Readers = new();
public event InputReader.RaiseKeyPress? OnKeyPress;
public AggregateInputReader(ILogger<InputReader> inputReaderLogger)
{
_InputReaderLogger = inputReaderLogger;
System.Timers.Timer timer = new()
{
Interval = 10 * 1000,
Enabled = true
};
timer.Elapsed += (_, _) => Scan();
timer.Start();
}
private void ReaderOnOnKeyPress(KeyPressEvent e) => OnKeyPress?.Invoke(e);
private void Scan()
{
string[] files = Directory.GetFiles("/dev/input/", "event*");
foreach (string file in files)
{
if (_Readers is not null && _Readers.ContainsKey(file))
{
continue;
}
InputReader reader = new(file, _InputReaderLogger);
reader.OnKeyPress += ReaderOnOnKeyPress;
_Readers?.Add(file, reader);
}
IEnumerable<InputReader>? deadReaders = _Readers?.Values.Where(r => r.Faulted);
if (deadReaders is not null)
{
foreach (InputReader? dr in deadReaders)
{
_ = _Readers?.Remove(dr.Path);
dr.OnKeyPress -= ReaderOnOnKeyPress;
dr.Dispose();
}
}
}
public void Dispose()
{
if (_Readers is not null)
{
foreach (InputReader d in _Readers.Values)
{
d.OnKeyPress -= ReaderOnOnKeyPress;
d.Dispose();
}
}
_Readers = null;
}
}

View File

@ -2,66 +2,52 @@ namespace Barcode.Host.Shared.KeyboardMouse;
public static class DeviceReader
{
public static IEnumerable<LinuxDevice> Get(string path = "/proc/bus/input/devices")
public static IEnumerable<LinuxDevice> Get(string path)
{
List<LinuxDevice> devices = new();
using FileStream filestream = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader reader = new(filestream);
LinuxDevice linuxDevice = new();
while (!reader.EndOfStream)
List<LinuxDevice> linuxDevices = new();
using FileStream filestream = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader streamReader = new(filestream);
while (!streamReader.EndOfStream)
{
string? line = reader.ReadLine();
string? line = streamReader.ReadLine();
if (string.IsNullOrWhiteSpace(line))
{
if (!string.IsNullOrWhiteSpace(linuxDevice.Name))
{
devices.Add(linuxDevice);
linuxDevices.Add(linuxDevice);
linuxDevice = new LinuxDevice();
}
continue;
}
if (line.StartsWith("I"))
ApplyIdentifier(line, linuxDevice);
else if (line.StartsWith("N"))
linuxDevice.Name = line.Substring(9, line.Length - 9 - 1);
else if (line.StartsWith("P"))
linuxDevice.PhysicalPath = line[8..];
else if (line.StartsWith("S"))
linuxDevice.SysFsPath = line[9..];
else if (line.StartsWith("U"))
linuxDevice.UniqueIdentificationCode = line[8..];
else if (line.StartsWith("H"))
linuxDevice.Handlers = line[12..]
.Split(" ")
.Where(h => !string.IsNullOrWhiteSpace(h))
.ToList();
else if (line.StartsWith("B"))
linuxDevice.Bitmaps.Add(line[3..]);
}
return devices;
return linuxDevices;
}
private static void ApplyIdentifier(string line, LinuxDevice linuxDevice)
{
string[] values = line[3..]
.Split(" ");
string[] values = line[3..].Split(" ");
foreach (string v in values)
{
string[] kvp = v.Split("=");
switch (kvp[0])
{
case "Bus":
@ -79,4 +65,5 @@ public static class DeviceReader
}
}
}
}

View File

@ -4,59 +4,42 @@ namespace Barcode.Host.Shared.KeyboardMouse;
public class InputReader : IDisposable
{
private readonly ILogger<InputReader> _Logger;
private long _Ticks;
private bool _Disposing;
private readonly int _PiOffset;
private const int _BufferLength = 24;
private static readonly int _PiOffset;
private readonly FileStream? _FileStream;
private readonly ILogger<IDisposable> _Logger;
private readonly byte[] _Buffer = new byte[_BufferLength];
private FileStream? _Stream;
private bool _Disposing;
public delegate void RaiseKeyPress(KeyPressEvent e);
public delegate void RaiseMouseMove(MouseMoveEvent e);
public event RaiseKeyPress? OnKeyPress;
public event RaiseMouseMove? OnMouseMove;
public string Path { get; }
public string Path { get; init; }
public bool Faulted { get; private set; }
static InputReader()
{
if (RunningOnRaspberryPi())
{
_PiOffset = -8;
}
}
public event RaiseKeyPress? OnKeyPress;
public event RaiseMouseMove? OnMouseMove;
public InputReader(
string path,
ILogger<InputReader> logger)
{
_Logger = logger;
public delegate void RaiseKeyPress(KeyPressEvent e);
public delegate void RaiseMouseMove(MouseMoveEvent e);
public InputReader(string path, ILogger<IDisposable> logger)
{
Path = path;
_Logger = logger;
if (RunningOnRaspberryPi())
_PiOffset = -8;
try
{
_Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
{ _FileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); }
catch (UnauthorizedAccessException ex)
{
_Logger.LogError(ex, "Current user doesn't have permissions to access input data. Add user to input group to correct this error");
logger.LogError(ex, "Current user doesn't have permissions to access input data. Add user to input group to correct this error");
Faulted = true;
}
catch (IOException ex)
{
_Logger.LogWarning(ex, $"Error occurred while trying to build stream for {path}");
logger.LogWarning(ex, $"Error occurred while trying to build stream for {path}");
Faulted = true;
}
_ = Task.Run(Run);
}
@ -66,33 +49,27 @@ public class InputReader : IDisposable
{
if (_Disposing)
break;
try
{
if (!Faulted && _Stream is not null)
{
_ = _Stream.Read(_Buffer, 0, _BufferLength);
}
if (!Faulted && _FileStream is not null)
_ = _FileStream.Read(_Buffer, 0, _BufferLength);
}
catch (IOException ex)
{
_Logger.LogInformation(ex, $"Error occured while trying to read from the stream for {Path}");
Faulted = true;
}
EventType type = GetEventType();
short code = GetCode();
int value = GetValue();
switch (type)
{
case EventType.EV_SYN:
_Ticks = DateTime.Now.Ticks;
break;
case EventType.EV_KEY:
HandleKeyPressEvent(code, value);
HandleKeyPressEvent();
break;
case EventType.EV_REL:
MouseAxis axis = (MouseAxis)code;
MouseMoveEvent e = new(axis, value);
OnMouseMove?.Invoke(e);
HandleMouseMoveEvent();
break;
}
}
@ -107,9 +84,7 @@ public class InputReader : IDisposable
_Buffer[22 + _PiOffset],
_Buffer[23 + _PiOffset]
};
int value = BitConverter.ToInt32(valueBits, 0);
return value;
}
@ -120,9 +95,7 @@ public class InputReader : IDisposable
_Buffer[18 + _PiOffset],
_Buffer[19 + _PiOffset]
};
short code = BitConverter.ToInt16(codeBits, 0);
return code;
}
@ -133,27 +106,35 @@ public class InputReader : IDisposable
_Buffer[16 + _PiOffset],
_Buffer[17 + _PiOffset]
};
short type = BitConverter.ToInt16(typeBits, 0);
EventType eventType = (EventType)type;
return eventType;
}
private void HandleKeyPressEvent(short code, int value)
private void HandleKeyPressEvent()
{
EventCode c = (EventCode)code;
KeyState s = (KeyState)value;
KeyPressEvent e = new(c, s);
OnKeyPress?.Invoke(e);
short code = GetCode();
int value = GetValue();
KeyState keyState = (KeyState)value;
EventCode eventCode = (EventCode)code;
KeyPressEvent keyPressEvent = new(eventCode, keyState, new TimeSpan(DateTime.Now.Ticks - _Ticks));
OnKeyPress?.Invoke(keyPressEvent);
}
private void HandleMouseMoveEvent()
{
short code = GetCode();
int value = GetValue();
MouseAxis axis = (MouseAxis)code;
MouseMoveEvent e = new(axis, value);
OnMouseMove?.Invoke(e);
}
public void Dispose()
{
_Disposing = true;
_Stream?.Dispose();
_Stream = null;
_FileStream?.Dispose();
GC.SuppressFinalize(this);
}
private static bool RunningOnRaspberryPi()
@ -163,4 +144,5 @@ public class InputReader : IDisposable
bool runningOnPi = text.Any(l => l.Contains("Raspberry Pi"));
return runningOnPi;
}
}

View File

@ -2,13 +2,16 @@ namespace Barcode.Host.Shared.KeyboardMouse;
public readonly struct KeyPressEvent
{
public KeyPressEvent(EventCode code, KeyState state)
public EventCode EventCode { get; init; }
public KeyState KeyState { get; init; }
public TimeSpan TimeSpan { get; init; }
public KeyPressEvent(EventCode eventCode, KeyState keyState, TimeSpan timeSpan)
{
Code = code;
State = state;
EventCode = eventCode;
KeyState = keyState;
TimeSpan = timeSpan;
}
public EventCode Code { get; }
public KeyState State { get; }
}

View File

@ -2,17 +2,14 @@ namespace Barcode.Host.Shared.KeyboardMouse;
public class LinuxDevice
{
public List<string> Bitmaps { get; set; } = new();
public List<string> Handlers { get; set; } = new();
public LinuxDeviceIdentifier Identifier { get; set; } = new();
public string? Name { get; set; }
public string? PhysicalPath { get; set; }
public string? SysFsPath { get; set; }
public string? UniqueIdentificationCode { get; set; }
public List<string> Handlers { get; set; } = new();
public List<string> Bitmaps { get; set; } = new();
}

View File

@ -2,11 +2,10 @@ namespace Barcode.Host.Shared.KeyboardMouse;
public class LinuxDeviceIdentifier
{
public string? Bus { get; set; }
public string? Vendor { get; set; }
public string? Product { get; set; }
public string? Vendor { get; set; }
public string? Version { get; set; }
}

View File

@ -2,13 +2,14 @@ namespace Barcode.Host.Shared.KeyboardMouse;
public readonly struct MouseMoveEvent
{
public int Amount { get; }
public MouseAxis Axis { get; }
public MouseMoveEvent(MouseAxis axis, int amount)
{
Axis = axis;
Amount = amount;
}
public MouseAxis Axis { get; }
public int Amount { get; }
}

View File

@ -0,0 +1,10 @@
using Barcode.Host.Shared.KeyboardMouse;
namespace Barcode.Host.Shared.Models.Stateless;
public interface IAggregateInputReader
{
event InputReader.RaiseKeyPress OnKeyPress;
}

View File

@ -0,0 +1,14 @@
namespace Barcode.Host.Shared.Models.Stateless;
public interface ILastScanController<T>
{
enum Action : int
{
Get = 0
}
static string GetRouteName() => nameof(ILastScanController<T>)[1..^10];
T GetScan();
}

View File

@ -0,0 +1,15 @@
using Barcode.Host.Shared.DataModels;
using Barcode.Host.Shared.KeyboardMouse;
namespace Barcode.Host.Shared.Models.Stateless;
public interface ILastScanService
{
void Clear();
int GetCount();
Result<string> GetScan();
void Add(EventCode eventCode, char @char);
List<(EventCode, char)> IncludeEventCodes();
}

View File

@ -1,10 +1,10 @@
namespace Barcode.Host.Shared.Linux;
namespace Barcode.Host.Shared.Models.Stateless;
public interface ILinuxGroupManager
{
Task<bool> IsInInputGroup();
Task<bool> IsInInputGroup();
Task RebootSystem(string password);
Task AddUserToInputGroup(string password);
Task RebootSystem(string password);
}

View File

@ -0,0 +1,10 @@
namespace Barcode.Host.Shared.Models.Stateless;
public interface ISerialService
{
void Open();
void Close();
void SerialPortWrite(int count, string raw);
}

View File

@ -32,7 +32,7 @@ internal abstract class WorkingDirectory
{
if (!Directory.Exists(result))
_ = Directory.CreateDirectory(result);
traceFile = string.Concat(result, @"\", DateTime.Now.Ticks, ".txt");
traceFile = Path.Combine(result, $"{DateTime.Now.Ticks}.txt");
File.WriteAllText(traceFile, traceFile);
File.Delete(traceFile);
break;