Ready to test in Fab
This commit is contained in:
parent
1f5deedc73
commit
2c38ecb399
30
.kanbn/index.md
Normal file
30
.kanbn/index.md
Normal 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
|
13
.kanbn/tasks/apt-get-install.md
Normal file
13
.kanbn/tasks/apt-get-install.md
Normal 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
|
72
.kanbn/tasks/configure-ufw.md
Normal file
72
.kanbn/tasks/configure-ufw.md
Normal 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
|
40
.kanbn/tasks/create-as-service.md
Normal file
40
.kanbn/tasks/create-as-service.md
Normal 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
|
22
.kanbn/tasks/install-net-7-0.md
Normal file
22
.kanbn/tasks/install-net-7-0.md
Normal 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
|
24
.kanbn/tasks/install-ubuntu-frame.md
Normal file
24
.kanbn/tasks/install-ubuntu-frame.md
Normal 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
|
15
.kanbn/tasks/install-vscode-extensions.md
Normal file
15
.kanbn/tasks/install-vscode-extensions.md
Normal 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
60
.kanbn/tasks/netplan.md
Normal 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
35
.kanbn/tasks/publish.md
Normal 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
17
.kanbn/tasks/pull-repo.md
Normal 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
|
15
.kanbn/tasks/run-secrets-task.md
Normal file
15
.kanbn/tasks/run-secrets-task.md
Normal 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
|
22
.kanbn/tasks/run-test-ports.md
Normal file
22
.kanbn/tasks/run-test-ports.md
Normal 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
113
.kanbn/tasks/setup-nginx.md
Normal 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
17
.kanbn/tasks/update-os.md
Normal 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
11
.vscode/settings.json
vendored
@ -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
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -1,5 +0,0 @@
|
||||
namespace Server.Tests;
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
78
Server.Tests/UnitTestLastScanController.cs
Normal file
78
Server.Tests/UnitTestLastScanController.cs
Normal 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
1
Server.Tests/Usings.cs
Normal file
@ -0,0 +1 @@
|
||||
global using Microsoft.VisualStudio.TestTools.UnitTesting;
|
20
Server/ApiControllers/LastScanController.cs
Normal file
20
Server/ApiControllers/LastScanController.cs
Normal 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 });
|
||||
|
||||
}
|
@ -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>
|
186
Server/HostedService/TimedHostedService.cs
Normal file
186
Server/HostedService/TimedHostedService.cs
Normal 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)}"); }
|
||||
}
|
||||
|
||||
}
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
93
Server/Services/LastScanService.cs
Normal file
93
Server/Services/LastScanService.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
44
Server/Services/SerialService.cs
Normal file
44
Server/Services/SerialService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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"
|
||||
}
|
@ -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
|
||||
}
|
@ -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" />
|
||||
|
7
Shared/DataModels/Result.cs
Normal file
7
Shared/DataModels/Result.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Barcode.Host.Shared.DataModels;
|
||||
|
||||
public class Result<T>
|
||||
{
|
||||
public T? Results { get; set; }
|
||||
public long TotalRows { get; set; }
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
namespace Barcode.Host.Shared.KeyboardMouse.Abstract;
|
||||
|
||||
public interface IAggregateInputReader
|
||||
{
|
||||
event InputReader.RaiseKeyPress OnKeyPress;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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; }
|
||||
}
|
@ -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();
|
||||
}
|
@ -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; }
|
||||
|
||||
}
|
@ -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; }
|
||||
}
|
10
Shared/Models/Stateless/IAggregateInputReader.cs
Normal file
10
Shared/Models/Stateless/IAggregateInputReader.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using Barcode.Host.Shared.KeyboardMouse;
|
||||
|
||||
namespace Barcode.Host.Shared.Models.Stateless;
|
||||
|
||||
public interface IAggregateInputReader
|
||||
{
|
||||
|
||||
event InputReader.RaiseKeyPress OnKeyPress;
|
||||
|
||||
}
|
14
Shared/Models/Stateless/ILastScanController.cs
Normal file
14
Shared/Models/Stateless/ILastScanController.cs
Normal 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();
|
||||
|
||||
}
|
15
Shared/Models/Stateless/ILastScanService.cs
Normal file
15
Shared/Models/Stateless/ILastScanService.cs
Normal 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();
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
10
Shared/Models/Stateless/ISerialService.cs
Normal file
10
Shared/Models/Stateless/ISerialService.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Barcode.Host.Shared.Models.Stateless;
|
||||
|
||||
public interface ISerialService
|
||||
{
|
||||
|
||||
void Open();
|
||||
void Close();
|
||||
void SerialPortWrite(int count, string raw);
|
||||
|
||||
}
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user