Первый коммит

This commit is contained in:
2025-11-27 14:02:00 +03:00
commit 5ff0384def
28 changed files with 4606 additions and 0 deletions
+487
View File
@@ -0,0 +1,487 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from `dotnet new gitignore`
*.db
.vscode
# dotenv files
.env
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
.idea/
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# Vim temporary swap files
*.swp
@@ -0,0 +1,6 @@
namespace GSS2.Core.Analysis.AmineContent;
public class AmineContentAnalyzerService
{
}
@@ -0,0 +1,6 @@
namespace GSS2.Core.Analysis.AmineContent;
public class AmineContentCalibrationDataService
{
}
@@ -0,0 +1,6 @@
namespace GSS2.Core.Analysis.AmineContent;
public class AmineContentCalibrationService
{
}
@@ -0,0 +1,184 @@
// <auto-generated />
using System;
using GSS2.Core.Analysis.AmineContent.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace GSS2.Core.Analysis.AmineContent.Database
{
[DbContext(typeof(AmineContentCalibrationContext))]
[Migration("20251125054200_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
modelBuilder.Entity("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+ImageData", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasComment("Уникальный идентификатор");
b.Property<string>("ImagePath")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Путь к файлу изображения");
b.Property<string>("ImageType")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Тип изображения (sample - проба или separator - сепаратор)");
b.Property<double>("Uv254Intensity")
.HasColumnType("REAL")
.HasComment("Интенсивность УФ излучения 254 нм (0..1)");
b.Property<double>("Uv365Intensity")
.HasColumnType("REAL")
.HasComment("Интенсивность УФ излучения 365 нм (0..1)");
b.Property<double>("VisibleIntensity")
.HasColumnType("REAL")
.HasComment("Интенсивность видимого света (0..1)");
b.HasKey("Id");
b.ToTable("Images");
});
modelBuilder.Entity("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+Record", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasComment("Уникальный идентификатор");
b.Property<double>("AirHumidity")
.HasColumnType("REAL")
.HasComment("Относительная влажность воздуха (%)");
b.Property<double>("AirTemperature")
.HasColumnType("REAL")
.HasComment("Температура воздуха (℃)");
b.Property<double>("MixtureActualRate")
.HasColumnType("REAL")
.HasComment("Истинный расход кондиционирующей смеси (кг/т)");
b.Property<double>("MixtureAmineContent")
.HasColumnType("REAL")
.HasComment("Содержание аминов в кондиционирующей смеси (%)");
b.Property<string>("MixtureName")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Название используемой кондиционирующей смеси");
b.Property<double>("MixtureNormalRate")
.HasColumnType("REAL")
.HasComment("Норма расхода кондиционирующей смеси (кг/т)");
b.Property<string>("SampleBrand")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Марка пробы");
b.Property<string>("SampleComment")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Дополнительная информация о пробе");
b.Property<double>("SampleMass")
.HasColumnType("REAL")
.HasComment("Масса пробы (кг)");
b.Property<string>("SampleName")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Название пробы");
b.Property<DateTime>("SamplingDateTime")
.HasColumnType("TEXT")
.HasComment("Дата и время отбора пробы");
b.Property<string>("SamplingPlace")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Место отбора пробы");
b.HasKey("Id");
b.ToTable("Records");
});
modelBuilder.Entity("ImageDataRecord", b =>
{
b.Property<int>("SampleImagesId")
.HasColumnType("INTEGER");
b.Property<int>("SampleRecordsId")
.HasColumnType("INTEGER");
b.HasKey("SampleImagesId", "SampleRecordsId");
b.HasIndex("SampleRecordsId");
b.ToTable("SampleImages", (string)null);
});
modelBuilder.Entity("ImageDataRecord1", b =>
{
b.Property<int>("SeparatorImagesId")
.HasColumnType("INTEGER");
b.Property<int>("SeparatorRecordsId")
.HasColumnType("INTEGER");
b.HasKey("SeparatorImagesId", "SeparatorRecordsId");
b.HasIndex("SeparatorRecordsId");
b.ToTable("SeparatorImages", (string)null);
});
modelBuilder.Entity("ImageDataRecord", b =>
{
b.HasOne("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+ImageData", null)
.WithMany()
.HasForeignKey("SampleImagesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+Record", null)
.WithMany()
.HasForeignKey("SampleRecordsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ImageDataRecord1", b =>
{
b.HasOne("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+ImageData", null)
.WithMany()
.HasForeignKey("SeparatorImagesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+Record", null)
.WithMany()
.HasForeignKey("SeparatorRecordsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,130 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace GSS2.Core.Analysis.AmineContent.Database
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Images",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false, comment: "Уникальный идентификатор")
.Annotation("Sqlite:Autoincrement", true),
VisibleIntensity = table.Column<double>(type: "REAL", nullable: false, comment: "Интенсивность видимого света (0..1)"),
Uv365Intensity = table.Column<double>(type: "REAL", nullable: false, comment: "Интенсивность УФ излучения 365 нм (0..1)"),
Uv254Intensity = table.Column<double>(type: "REAL", nullable: false, comment: "Интенсивность УФ излучения 254 нм (0..1)"),
ImageType = table.Column<string>(type: "TEXT", nullable: false, comment: "Тип изображения (sample - проба или separator - сепаратор)"),
ImagePath = table.Column<string>(type: "TEXT", nullable: false, comment: "Путь к файлу изображения")
},
constraints: table =>
{
table.PrimaryKey("PK_Images", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Records",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false, comment: "Уникальный идентификатор")
.Annotation("Sqlite:Autoincrement", true),
SampleName = table.Column<string>(type: "TEXT", nullable: false, comment: "Название пробы"),
SampleComment = table.Column<string>(type: "TEXT", nullable: false, comment: "Дополнительная информация о пробе"),
SampleBrand = table.Column<string>(type: "TEXT", nullable: false, comment: "Марка пробы"),
SampleMass = table.Column<double>(type: "REAL", nullable: false, comment: "Масса пробы (кг)"),
SamplingDateTime = table.Column<DateTime>(type: "TEXT", nullable: false, comment: "Дата и время отбора пробы"),
SamplingPlace = table.Column<string>(type: "TEXT", nullable: false, comment: "Место отбора пробы"),
MixtureName = table.Column<string>(type: "TEXT", nullable: false, comment: "Название используемой кондиционирующей смеси"),
MixtureAmineContent = table.Column<double>(type: "REAL", nullable: false, comment: "Содержание аминов в кондиционирующей смеси (%)"),
MixtureNormalRate = table.Column<double>(type: "REAL", nullable: false, comment: "Норма расхода кондиционирующей смеси (кг/т)"),
MixtureActualRate = table.Column<double>(type: "REAL", nullable: false, comment: "Истинный расход кондиционирующей смеси (кг/т)"),
AirTemperature = table.Column<double>(type: "REAL", nullable: false, comment: "Температура воздуха (℃)"),
AirHumidity = table.Column<double>(type: "REAL", nullable: false, comment: "Относительная влажность воздуха (%)")
},
constraints: table =>
{
table.PrimaryKey("PK_Records", x => x.Id);
});
migrationBuilder.CreateTable(
name: "SampleImages",
columns: table => new
{
SampleImagesId = table.Column<int>(type: "INTEGER", nullable: false),
SampleRecordsId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SampleImages", x => new { x.SampleImagesId, x.SampleRecordsId });
table.ForeignKey(
name: "FK_SampleImages_Images_SampleImagesId",
column: x => x.SampleImagesId,
principalTable: "Images",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_SampleImages_Records_SampleRecordsId",
column: x => x.SampleRecordsId,
principalTable: "Records",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "SeparatorImages",
columns: table => new
{
SeparatorImagesId = table.Column<int>(type: "INTEGER", nullable: false),
SeparatorRecordsId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SeparatorImages", x => new { x.SeparatorImagesId, x.SeparatorRecordsId });
table.ForeignKey(
name: "FK_SeparatorImages_Images_SeparatorImagesId",
column: x => x.SeparatorImagesId,
principalTable: "Images",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_SeparatorImages_Records_SeparatorRecordsId",
column: x => x.SeparatorRecordsId,
principalTable: "Records",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_SampleImages_SampleRecordsId",
table: "SampleImages",
column: "SampleRecordsId");
migrationBuilder.CreateIndex(
name: "IX_SeparatorImages_SeparatorRecordsId",
table: "SeparatorImages",
column: "SeparatorRecordsId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "SampleImages");
migrationBuilder.DropTable(
name: "SeparatorImages");
migrationBuilder.DropTable(
name: "Images");
migrationBuilder.DropTable(
name: "Records");
}
}
}
@@ -0,0 +1,78 @@
using Microsoft.EntityFrameworkCore;
namespace GSS2.Core.Analysis.AmineContent.Database;
public class AmineContentCalibrationContext : DbContext
{
public class Record
{
[Comment("Уникальный идентификатор")]
public int Id { get; set; }
[Comment("Название пробы")]
public string SampleName { get; set; } = string.Empty;
[Comment("Дополнительная информация о пробе")]
public string SampleComment { get; set; } = string.Empty;
[Comment("Марка пробы")]
public string SampleBrand { get; set; } = string.Empty;
[Comment("Масса пробы (кг)")]
public double SampleMass { get; set; } = double.NaN;
[Comment("Дата и время отбора пробы")]
public DateTime SamplingDateTime { get; set; } = DateTime.Now;
[Comment("Место отбора пробы")]
public string SamplingPlace { get; set; } = string.Empty;
[Comment("Название используемой кондиционирующей смеси")]
public string MixtureName { get; set; } = string.Empty;
[Comment("Содержание аминов в кондиционирующей смеси (%)")]
public double MixtureAmineContent { get; set; } = double.NaN;
[Comment("Норма расхода кондиционирующей смеси (кг/т)")]
public double MixtureNormalRate { get; set; } = double.NaN;
[Comment("Истинный расход кондиционирующей смеси (кг/т)")]
public double MixtureActualRate { get; set; } = double.NaN;
[Comment("Изображения пробы")]
public List<ImageData> SampleImages { get; set; } = new();
[Comment("Изображения пустого сепаратора")]
public List<ImageData> SeparatorImages { get; set; } = new();
[Comment("Температура воздуха (℃)")]
public double AirTemperature { get; set; } = new();
[Comment("Относительная влажность воздуха (%)")]
public double AirHumidity { get; set; } = new();
}
public class ImageData
{
[Comment("Уникальный идентификатор")]
public int Id { get; set; }
[Comment("Интенсивность видимого света (0..1)")]
public double VisibleIntensity { get; set; } = double.NaN;
[Comment("Интенсивность УФ излучения 365 нм (0..1)")]
public double Uv365Intensity { get; set; } = double.NaN;
[Comment("Интенсивность УФ излучения 254 нм (0..1)")]
public double Uv254Intensity { get; set; } = double.NaN;
[Comment("Тип изображения (sample - проба или separator - сепаратор)")]
public string ImageType { get; set; } = string.Empty;
[Comment("Путь к файлу изображения")]
public string ImagePath { get; set; } = string.Empty;
[Comment("Записи в которых это изображение используется как изображение пробы")]
public List<Record> SampleRecords { get; set; } = new();
[Comment("Записи в которых это изображение используется как изображение сепаратора")]
public List<Record> SeparatorRecords { get; set; } = new();
}
public DbSet<Record> Records => Set<Record>();
public DbSet<ImageData> Images => Set<ImageData>();
public AmineContentCalibrationContext(DbContextOptions<AmineContentCalibrationContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Record>()
.HasMany(r => r.SampleImages)
.WithMany(i => i.SampleRecords)
.UsingEntity(j => j.ToTable("SampleImages"));
modelBuilder.Entity<Record>()
.HasMany(r => r.SeparatorImages)
.WithMany(i => i.SeparatorRecords)
.UsingEntity(j => j.ToTable("SeparatorImages"));
}
}
@@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
namespace GSS2.Core.Analysis.AmineContent.Database;
public class AmineContentCalibrationContextFactory : IDesignTimeDbContextFactory<AmineContentCalibrationContext>
{
public AmineContentCalibrationContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<AmineContentCalibrationContext>();
// строка подключения для миграций (в design-time)
optionsBuilder.UseSqlite("Data Source=AmineContentCalibration.db");
return new AmineContentCalibrationContext(optionsBuilder.Options);
}
}
@@ -0,0 +1,181 @@
// <auto-generated />
using System;
using GSS2.Core.Analysis.AmineContent.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace GSS2.Core.Analysis.AmineContent.Database
{
[DbContext(typeof(AmineContentCalibrationContext))]
partial class AmineContentCalibrationContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
modelBuilder.Entity("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+ImageData", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasComment("Уникальный идентификатор");
b.Property<string>("ImagePath")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Путь к файлу изображения");
b.Property<string>("ImageType")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Тип изображения (sample - проба или separator - сепаратор)");
b.Property<double>("Uv254Intensity")
.HasColumnType("REAL")
.HasComment("Интенсивность УФ излучения 254 нм (0..1)");
b.Property<double>("Uv365Intensity")
.HasColumnType("REAL")
.HasComment("Интенсивность УФ излучения 365 нм (0..1)");
b.Property<double>("VisibleIntensity")
.HasColumnType("REAL")
.HasComment("Интенсивность видимого света (0..1)");
b.HasKey("Id");
b.ToTable("Images");
});
modelBuilder.Entity("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+Record", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasComment("Уникальный идентификатор");
b.Property<double>("AirHumidity")
.HasColumnType("REAL")
.HasComment("Относительная влажность воздуха (%)");
b.Property<double>("AirTemperature")
.HasColumnType("REAL")
.HasComment("Температура воздуха (℃)");
b.Property<double>("MixtureActualRate")
.HasColumnType("REAL")
.HasComment("Истинный расход кондиционирующей смеси (кг/т)");
b.Property<double>("MixtureAmineContent")
.HasColumnType("REAL")
.HasComment("Содержание аминов в кондиционирующей смеси (%)");
b.Property<string>("MixtureName")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Название используемой кондиционирующей смеси");
b.Property<double>("MixtureNormalRate")
.HasColumnType("REAL")
.HasComment("Норма расхода кондиционирующей смеси (кг/т)");
b.Property<string>("SampleBrand")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Марка пробы");
b.Property<string>("SampleComment")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Дополнительная информация о пробе");
b.Property<double>("SampleMass")
.HasColumnType("REAL")
.HasComment("Масса пробы (кг)");
b.Property<string>("SampleName")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Название пробы");
b.Property<DateTime>("SamplingDateTime")
.HasColumnType("TEXT")
.HasComment("Дата и время отбора пробы");
b.Property<string>("SamplingPlace")
.IsRequired()
.HasColumnType("TEXT")
.HasComment("Место отбора пробы");
b.HasKey("Id");
b.ToTable("Records");
});
modelBuilder.Entity("ImageDataRecord", b =>
{
b.Property<int>("SampleImagesId")
.HasColumnType("INTEGER");
b.Property<int>("SampleRecordsId")
.HasColumnType("INTEGER");
b.HasKey("SampleImagesId", "SampleRecordsId");
b.HasIndex("SampleRecordsId");
b.ToTable("SampleImages", (string)null);
});
modelBuilder.Entity("ImageDataRecord1", b =>
{
b.Property<int>("SeparatorImagesId")
.HasColumnType("INTEGER");
b.Property<int>("SeparatorRecordsId")
.HasColumnType("INTEGER");
b.HasKey("SeparatorImagesId", "SeparatorRecordsId");
b.HasIndex("SeparatorRecordsId");
b.ToTable("SeparatorImages", (string)null);
});
modelBuilder.Entity("ImageDataRecord", b =>
{
b.HasOne("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+ImageData", null)
.WithMany()
.HasForeignKey("SampleImagesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+Record", null)
.WithMany()
.HasForeignKey("SampleRecordsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ImageDataRecord1", b =>
{
b.HasOne("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+ImageData", null)
.WithMany()
.HasForeignKey("SeparatorImagesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("GSS2.Core.Analysis.AmineContent.Database.AmineContentCalibrationContext+Record", null)
.WithMany()
.HasForeignKey("SeparatorRecordsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
+26
View File
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="System.Device.Gpio" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GSS2.LightsControl\CSharpClient\GSS2.LightsControl.CSharpClient.csproj" />
</ItemGroup>
</Project>
View File
+221
View File
@@ -0,0 +1,221 @@
using System;
using System.Device.Gpio;
using System.Device.Pwm;
using System.Device.Pwm.Drivers;
using System.IO;
using System.Threading;
using Microsoft.Extensions.Logging;
namespace GSS2.Core.Hardware;
public class IlluminatorService : IDisposable
{
public class IlluminatorServiceConfiguration
{
public static IlluminatorServiceConfiguration Default => new IlluminatorServiceConfiguration
{
WhitePwmPin = 13,
Uv254PwmPin = 18,
Uv365PwmPin = 12,
WhiteRelayPin = 16,
Uv254RelayPin = 15,
Uv365RelayPin = 20,
FanPin = 21,
PwmFrequency = 200,
MaxWhitePwmDuty = 1.0,
MaxUv254PwmDuty = 1.0,
MaxUv365PwmDuty = 1.0
};
public int WhitePwmPin { get; set; }
public int Uv254PwmPin { get; set; }
public int Uv365PwmPin { get; set; }
public int WhiteRelayPin { get; set; }
public int Uv254RelayPin { get; set; }
public int Uv365RelayPin { get; set; }
public int FanPin { get; set; }
public int PwmFrequency { get; set; }
public double MaxWhitePwmDuty { get; set; }
public double MaxUv254PwmDuty { get; set; }
public double MaxUv365PwmDuty { get; set; }
}
private static readonly Dictionary<int, int> PinPwmChips = new()
{
{ 12, 0 },
{ 13, 0 },
{ 14, 0 },
{ 15, 0 },
{ 18, 0 },
{ 19, 0 },
};
private static readonly Dictionary<int, int> PinPwmChannels = new()
{
{ 12, 0 },
{ 13, 1 },
{ 14, 2 },
{ 15, 3 },
{ 18, 2 },
{ 19, 3 }
};
private bool _disposedValue;
private readonly ILogger<IlluminatorService> _logger;
private readonly IlluminatorServiceConfiguration _configuration;
private readonly GpioController _gpio;
private readonly PwmChannel _whitePwm;
private readonly PwmChannel _uv254Pwm;
private readonly PwmChannel _uv365Pwm;
private readonly GpioPin _whiteRelay;
private readonly GpioPin _uv254Relay;
private readonly GpioPin _uv365Relay;
private readonly GpioPin _fanRelay;
public IlluminatorService(ILogger<IlluminatorService> logger, IlluminatorServiceConfiguration? configuration = null)
{
_logger = logger;
_logger.LogInformation("Illuminator initialization");
if (configuration is null)
{
_logger.LogWarning("Illuminator configuration not set. Using default configuration.");
_configuration = IlluminatorServiceConfiguration.Default;
}
else
{
_configuration = configuration;
}
_logger.LogDebug(@$"Illuminator use following configuration:
WhitePwmPin: {_configuration.WhitePwmPin}
Uv254PwmPin: {_configuration.Uv254PwmPin}
Uv365PwmPin: {_configuration.Uv365PwmPin}
WhiteRelayPin: {_configuration.WhiteRelayPin}
Uv254RelayPin: {_configuration.Uv254RelayPin}
Uv365RelayPin: {_configuration.Uv365RelayPin}
FanPin: {_configuration.FanPin}
PwmFrequency: {_configuration.PwmFrequency}
MaxWhitePwmDuty: {_configuration.MaxWhitePwmDuty}
MaxUv254PwmDuty: {_configuration.MaxUv254PwmDuty}
MaxUv365PwmDuty: {_configuration.MaxUv365PwmDuty}
");
CheckPwmPin(_configuration.WhitePwmPin);
CheckPwmPin(_configuration.Uv254PwmPin);
CheckPwmPin(_configuration.Uv365PwmPin);
try
{
_gpio = new GpioController();
_whitePwm = PwmChannel.Create(PinPwmChips[_configuration.WhitePwmPin], PinPwmChannels[_configuration.WhitePwmPin], _configuration.PwmFrequency, CalculateDuty(0, _configuration.MaxWhitePwmDuty));
_uv254Pwm = PwmChannel.Create(PinPwmChips[_configuration.Uv254PwmPin], PinPwmChannels[_configuration.Uv254PwmPin], _configuration.PwmFrequency, CalculateDuty(0, _configuration.MaxUv254PwmDuty));
_uv365Pwm = PwmChannel.Create(PinPwmChips[_configuration.Uv365PwmPin], PinPwmChannels[_configuration.Uv365PwmPin], _configuration.PwmFrequency, CalculateDuty(0, _configuration.MaxUv365PwmDuty));
_whiteRelay = _gpio.OpenPin(_configuration.WhiteRelayPin, PinMode.Output, PinValue.Low);
_uv254Relay = _gpio.OpenPin(_configuration.Uv254RelayPin, PinMode.Output, PinValue.Low);
_uv365Relay = _gpio.OpenPin(_configuration.Uv365RelayPin, PinMode.Output, PinValue.Low);
_fanRelay = _gpio.OpenPin(_configuration.FanPin, PinMode.Output, PinValue.Low);
}
catch (Exception e)
{
_logger.LogCritical(e, "An exception occurred while illuminator initialization");
throw;
}
}
public void SetIntensity(double? white = null, double? uv365 = null, double? uv254 = null)
{
try
{
if (white is not null) _whitePwm.DutyCycle = CalculateDuty(white.Value, _configuration.MaxWhitePwmDuty);
if (uv365 is not null) _uv365Pwm.DutyCycle = CalculateDuty(uv365.Value, _configuration.MaxUv365PwmDuty);
if (uv254 is not null) _uv254Pwm.DutyCycle = CalculateDuty(uv254.Value, _configuration.MaxUv254PwmDuty);
}
catch (Exception e)
{
_logger.LogError(e, "An exception occurred while setting illuminator intensity");
}
}
public void TurnOn()
{
try
{
_fanRelay.Write(PinValue.High);
_whitePwm.Start();
_uv254Pwm.Start();
_uv365Pwm.Start();
_whiteRelay.Write(PinValue.High);
_uv254Relay.Write(PinValue.High);
_uv365Relay.Write(PinValue.High);
}
catch (Exception e)
{
_logger.LogError(e, "An exception occurred while turning on illuminator");
}
}
public void TurnOff()
{
try
{
_whitePwm.Stop();
_uv254Pwm.Stop();
_uv365Pwm.Stop();
_whiteRelay.Write(PinValue.Low);
_uv254Relay.Write(PinValue.Low);
_uv365Relay.Write(PinValue.Low);
_fanRelay.Write(PinValue.Low);
}
catch (Exception e)
{
_logger.LogError(e, "An exception occurred while turning off illuminator");
}
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
try
{
TurnOff();
}
catch { }
_whiteRelay?.Dispose();
_uv254Relay?.Dispose();
_uv365Relay?.Dispose();
_fanRelay?.Dispose();
_whitePwm?.Dispose();
_uv254Pwm?.Dispose();
_uv365Pwm?.Dispose();
_gpio?.Dispose();
}
_disposedValue = true;
}
}
void IDisposable.Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
private static void CheckPwmPin(int pin)
{
if (!PinPwmChips.TryGetValue(pin, out var _) || !PinPwmChannels.TryGetValue(pin, out var _))
throw new ArgumentException($"Unsupported PWM pin {pin}");
}
private static double CalculateDuty(double intensity, double maxDuty) => Math.Clamp(intensity, 0.0, 1.0) * Math.Clamp(maxDuty, 0.0, 1.0);
}
+445
View File
@@ -0,0 +1,445 @@
using System.Drawing;
using System.Runtime.CompilerServices;
using Grpc.Core;
using Grpc.Net.Client;
using GSS2.LightsControl.CSharpClient;
using Microsoft.Extensions.Logging;
namespace GSS2.Core.Hardware;
public class LightsService
{
public class LightsServiceConfiguration()
{
public static LightsServiceConfiguration Default => new LightsServiceConfiguration
{
ServerAddress = "localhost:50051"
};
public string ServerAddress { get; set; } = string.Empty;
}
private readonly ILogger<LightsService> _logger;
private readonly LightsServiceConfiguration _configuration;
private readonly Lights.LightsClient _client;
public LightsService(ILogger<LightsService> logger, LightsServiceConfiguration? configuration = null)
{
_logger = logger;
_logger.LogInformation("Lights initialization");
if (configuration is null)
{
_logger.LogWarning("Lights configuration not set. Using default configuration.");
_configuration = LightsServiceConfiguration.Default;
}
else
{
_configuration = configuration;
}
_logger.LogDebug(@$"Lights use following configuration:
ServerAddress: {_configuration.ServerAddress}
");
_client = new Lights.LightsClient(GrpcChannel.ForAddress(_configuration.ServerAddress));
}
public async Task Run(CancellationToken cancellationToken)
{
_logger.LogInformation("LightsService started");
while (!cancellationToken.IsCancellationRequested)
{
await PingAsync(cancellationToken);
try
{
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
}
catch (TaskCanceledException)
{
break;
}
}
_logger.LogInformation("LightsService stopped");
}
private void SendRequest<T>(Func<T, Metadata, DateTime?, CancellationToken, Empty> method, T request, CancellationToken cancellationToken = default, [CallerMemberName] string sender = "")
{
try
{
#pragma warning disable CS8625
method.Invoke(request, null, null, cancellationToken); ;
#pragma warning restore CS8625
_logger.LogTrace("Request \"{}\" sent", sender);
}
catch (RpcException ex)
{
_logger.LogWarning(ex, "Request \"{}\" RPC error", sender);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Request \"{}\" Unexpected error", method);
}
}
private async Task SendRequestAsync<T>(Func<T, Metadata, DateTime?, CancellationToken, AsyncUnaryCall<Empty>> method, T request, CancellationToken cancellationToken = default, [CallerMemberName] string sender = "")
{
try
{
#pragma warning disable CS8625
await method.Invoke(request, null, null, cancellationToken); ;
#pragma warning restore CS8625
_logger.LogTrace("Request \"{}\" sent", sender);
}
catch (RpcException ex)
{
_logger.LogWarning(ex, "Request \"{}\" RPC error", sender);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Request \"{}\" Unexpected error", method);
}
}
public void Ping(CancellationToken cancellationToken = default) => SendRequest(_client.Ping, new Empty(), cancellationToken);
public void Off(CancellationToken cancellationToken = default) => SendRequest(_client.SetOff, new Empty(), cancellationToken);
public void StaticColor(double brightness, Color color, CancellationToken cancellationToken = default) => SendRequest(_client.SetStaticColor, new StaticColorRequest
{
Brightness = brightness,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public void StaticGradient2(double brightness, Color leftColor, Color rightColor, CancellationToken cancellationToken = default) => SendRequest(_client.SetStaticGradient2, new StaticGradient2Request
{
Brightness = brightness,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public void StaticGradient3(double brightness, Color leftColor, Color middleColor, Color rightColor, CancellationToken cancellationToken = default) => SendRequest(_client.SetStaticGradient3, new StaticGradient3Request
{
Brightness = brightness,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
MiddleR = middleColor.R,
MiddleG = middleColor.G,
MiddleB = middleColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public void StaticRainbow(double brightness, CancellationToken cancellationToken = default) => SendRequest(_client.SetStaticRainbow, new StaticRainbowRequest
{
Brightness = brightness
}, cancellationToken);
public void BreathingColor(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => SendRequest(_client.SetBreathingColor, new BreathingColorRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public void BreathingGradient2(double brightness, double speed, Color leftColor, Color rightColor, CancellationToken cancellationToken = default) => SendRequest(_client.SetBreathingGradient2, new BreathingGradient2Request
{
Brightness = brightness,
Speed = speed,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public void BreathingGradient3(double brightness, double speed, Color leftColor, Color middleColor, Color rightColor, CancellationToken cancellationToken = default) => SendRequest(_client.SetBreathingGradient3, new BreathingGradient3Request
{
Brightness = brightness,
Speed = speed,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
MiddleR = middleColor.R,
MiddleG = middleColor.G,
MiddleB = middleColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public void BreathingRainbow(double brightness, double speed, CancellationToken cancellationToken = default) => SendRequest(_client.SetBreathingRainbow, new BreathingRainbowRequest
{
Brightness = brightness,
Speed = speed
}, cancellationToken);
public void RunningColor(double brightness, double speed, int length, int fade, Color color, CancellationToken cancellationToken = default) => SendRequest(_client.SetRunningColor, new RunningColorRequest
{
Brightness = brightness,
Speed = speed,
Length = length,
Fade = fade,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public void RunningGradient2(double brightness, double speed, Color leftColor, Color rightColor, CancellationToken cancellationToken = default) => SendRequest(_client.SetRunningGradient2, new RunningGradient2Request
{
Brightness = brightness,
Speed = speed,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public void RunningGradient3(double brightness, double speed, Color leftColor, Color middleColor, Color rightColor, CancellationToken cancellationToken = default) => SendRequest(_client.SetRunningGradient3, new RunningGradient3Request
{
Brightness = brightness,
Speed = speed,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
MiddleR = middleColor.R,
MiddleG = middleColor.G,
MiddleB = middleColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public void RunningRainbow(double brightness, double speed, CancellationToken cancellationToken = default) => SendRequest(_client.SetRunningRainbow, new RunningRainbowRequest
{
Brightness = brightness,
Speed = speed
}, cancellationToken);
public void Snake(double brightness, double speed, int headLength, int tailLength, Color headColor, Color tailColor, CancellationToken cancellationToken = default) => SendRequest(_client.SetSnake, new SnakeRequest
{
Brightness = brightness,
Speed = speed,
HeadLength = headLength,
TailLength = tailLength,
HeadR = headColor.R,
HeadG = headColor.G,
HeadB = headColor.B,
TailR = tailColor.R,
TailG = tailColor.G,
TailB = tailColor.B
}, cancellationToken);
public void Fire(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => SendRequest(_client.SetFire, new FireRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public void Snowfall(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => SendRequest(_client.SetSnowfall, new SnowfallRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public void Flash(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => SendRequest(_client.SetFlash, new FlashRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public void Load(double brightness, double speed, double percent, double lastPercent, Color color, CancellationToken cancellationToken = default) => SendRequest(_client.SetLoad, new LoadRequest
{
Brightness = brightness,
Speed = speed,
Percent = percent,
LastPercent = lastPercent,
R = color.R,
G = color.G,
B = color.B,
}, cancellationToken);
public void CpuLoad(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => SendRequest(_client.SetCpuLoad, new CpuLoadRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public async Task PingAsync(CancellationToken cancellationToken = default) => await SendRequestAsync(_client.PingAsync, new Empty(), cancellationToken);
public async Task OffAsync(CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetOffAsync, new Empty(), cancellationToken);
public async Task StaticColorAsync(double brightness, Color color, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetStaticColorAsync, new StaticColorRequest
{
Brightness = brightness,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public async Task StaticGradient2Async(double brightness, Color leftColor, Color rightColor, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetStaticGradient2Async, new StaticGradient2Request
{
Brightness = brightness,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public async Task StaticGradient3Async(double brightness, Color leftColor, Color middleColor, Color rightColor, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetStaticGradient3Async, new StaticGradient3Request
{
Brightness = brightness,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
MiddleR = middleColor.R,
MiddleG = middleColor.G,
MiddleB = middleColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public async Task StaticRainbowAsync(double brightness, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetStaticRainbowAsync, new StaticRainbowRequest
{
Brightness = brightness
}, cancellationToken);
public async Task BreathingColorAsync(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetBreathingColorAsync, new BreathingColorRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public async Task BreathingGradient2Async(double brightness, double speed, Color leftColor, Color rightColor, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetBreathingGradient2Async, new BreathingGradient2Request
{
Brightness = brightness,
Speed = speed,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public async Task BreathingGradient3Async(double brightness, double speed, Color leftColor, Color middleColor, Color rightColor, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetBreathingGradient3Async, new BreathingGradient3Request
{
Brightness = brightness,
Speed = speed,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
MiddleR = middleColor.R,
MiddleG = middleColor.G,
MiddleB = middleColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public async Task BreathingRainbowAsync(double brightness, double speed, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetBreathingRainbowAsync, new BreathingRainbowRequest
{
Brightness = brightness,
Speed = speed
}, cancellationToken);
public async Task RunningColorAsync(double brightness, double speed, int length, int fade, Color color, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetRunningColorAsync, new RunningColorRequest
{
Brightness = brightness,
Speed = speed,
Length = length,
Fade = fade,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public async Task RunningGradient2Async(double brightness, double speed, Color leftColor, Color rightColor, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetRunningGradient2Async, new RunningGradient2Request
{
Brightness = brightness,
Speed = speed,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public async Task RunningGradient3Async(double brightness, double speed, Color leftColor, Color middleColor, Color rightColor, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetRunningGradient3Async, new RunningGradient3Request
{
Brightness = brightness,
Speed = speed,
LeftR = leftColor.R,
LeftG = leftColor.G,
LeftB = leftColor.B,
MiddleR = middleColor.R,
MiddleG = middleColor.G,
MiddleB = middleColor.B,
RightR = rightColor.R,
RightG = rightColor.G,
RightB = rightColor.B
}, cancellationToken);
public async Task RunningRainbowAsync(double brightness, double speed, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetRunningRainbowAsync, new RunningRainbowRequest
{
Brightness = brightness,
Speed = speed
}, cancellationToken);
public async Task SnakeAsync(double brightness, double speed, int headLength, int tailLength, Color headColor, Color tailColor, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetSnakeAsync, new SnakeRequest
{
Brightness = brightness,
Speed = speed,
HeadLength = headLength,
TailLength = tailLength,
HeadR = headColor.R,
HeadG = headColor.G,
HeadB = headColor.B,
TailR = tailColor.R,
TailG = tailColor.G,
TailB = tailColor.B
}, cancellationToken);
public async Task FireAsync(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetFireAsync, new FireRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public async Task SnowfallAsync(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetSnowfallAsync, new SnowfallRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public async Task FlashAsync(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetFlashAsync, new FlashRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
public async Task LoadAsync(double brightness, double speed, double percent, double lastPercent, Color color, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetLoadAsync, new LoadRequest
{
Brightness = brightness,
Speed = speed,
Percent = percent,
LastPercent = lastPercent,
R = color.R,
G = color.G,
B = color.B,
}, cancellationToken);
public async Task CpuLoadAsync(double brightness, double speed, Color color, CancellationToken cancellationToken = default) => await SendRequestAsync(_client.SetCpuLoadAsync, new CpuLoadRequest
{
Brightness = brightness,
Speed = speed,
R = color.R,
G = color.G,
B = color.B
}, cancellationToken);
}
@@ -0,0 +1,174 @@
using System.Device.I2c;
using Microsoft.Extensions.Logging;
namespace GSS2.Core.Hardware;
public class TemperatureHumidityService : IDisposable
{
public class TemperatureHumidityServiceConfiguration
{
public static TemperatureHumidityServiceConfiguration Default => new TemperatureHumidityServiceConfiguration
{
Bus = 0,
Address = 0x44
};
public int Bus { get; set; }
public int Address { get; set; }
}
private bool _disposedValue;
private readonly ILogger<TemperatureHumidityService> _logger;
private readonly TemperatureHumidityServiceConfiguration _configuration;
private readonly I2cDevice _sensor;
public TemperatureHumidityService(ILogger<TemperatureHumidityService> logger, TemperatureHumidityServiceConfiguration? configuration = null)
{
_logger = logger;
_logger.LogInformation("Temperature and humidity sensor initialization");
if (configuration is null)
{
_logger.LogWarning("Temperature and humidity sensor configuration not set. Using default configuration.");
_configuration = TemperatureHumidityServiceConfiguration.Default;
}
else
{
_configuration = configuration;
}
_logger.LogDebug(@$"Temperature and humidity sensor use following configuration:
Bus: {_configuration.Bus}
Address: {_configuration.Address}
");
try
{
_sensor = I2cDevice.Create(new I2cConnectionSettings(_configuration.Bus, _configuration.Address));
}
catch (Exception e)
{
_logger.LogCritical(e, "An exception occurred while temperature and humidity sensor initialization");
throw;
}
}
public async Task<(double temperature, double relativeHumidity)> MeasureAsync(int measurementsCount = 1, CancellationToken cancellationToken = default)
{
// В соответствии с даташитом на SHT45-AD1B-R2 псевдокод измерения с высокой точностью следующий:
// i2c_write(i2c_addr=0x44, tx_bytes=[0xFD])
// wait_seconds(0.01)
// rx_bytes = i2c_read(i2c_addr=0x44, number_of_bytes=6)
// t_ticks = rx_bytes[0] * 256 + rx_bytes[1]
// checksum_t = rx_bytes[2]
// rh_ticks = rx_bytes[3] * 256 + rx_bytes[4]
// checksum_rh = rx_bytes[5]
// t_degC = -45 + 175 * t_ticks/65535
// rh_pRH = -6 + 125 * rh_ticks/65535
// if (rh_pRH > 100):
// rh_pRH = 100
// if (rh_pRH < 0):
// rh_pRH = 0
if (measurementsCount <= 0)
throw new ArgumentOutOfRangeException(nameof(measurementsCount));
double sumTemperature = 0;
double sumRelativeHumidity = 0;
int successMeasurementsCount = 0;
Span<byte> command = stackalloc byte[] { 0xFD };
Span<byte> data = stackalloc byte[6];
for (int i = 0; i < measurementsCount; i++)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
// Отправляем команду
_sensor.Write(command);
// 10 мс ожидания
await Task.Delay(10, cancellationToken);
// Читаем данные
_sensor.Read(data);
// Распаковываем данные [2 * 8-bit T-data; 8-bit CRC; 2 * 8-bit RH-data; 8-bit CRC]
ushort temperatureTicks = BitConverter.ToUInt16(data.ToArray(), 0);
byte temperatureChecksum = data[2];
int relativeHumidityTicks = BitConverter.ToUInt16(data.ToArray(), 3);
byte relativeHumidityChecksum = data[5];
// Проверка по CRC
if (!CheckCrc(data.Slice(0, 2), temperatureChecksum) ||
!CheckCrc(data.Slice(3, 2), relativeHumidityChecksum))
{
_logger.LogWarning("Temperature and humidity sensor CRC error");
continue;
}
// Пересчёт
double temperature = -45 + 175 * (temperatureTicks / 65535.0);
double relativeHumidity = -6 + 125 * (relativeHumidityTicks / 65535.0);
relativeHumidity = Math.Clamp(relativeHumidity, 0, 100);
// Сумма для усреднения
sumTemperature += temperature;
sumRelativeHumidity += relativeHumidity;
successMeasurementsCount++;
}
catch (Exception e)
{
_logger.LogWarning(e, "Temperature and humidity sensor not available on I2C bus");
}
}
if (successMeasurementsCount == 0)
{
_logger.LogError("Temperature and humidity sensor: no successful measurements");
return (double.NaN, double.NaN);
}
return (sumTemperature / successMeasurementsCount, sumRelativeHumidity / successMeasurementsCount);
}
private static bool CheckCrc(Span<byte> data, byte expectedCrc)
{
byte crc = 0xFF;
foreach (var b in data)
{
crc ^= b;
for (int i = 0; i < 8; i++)
{
if ((crc & 0x80) != 0)
crc = (byte)((crc << 1) ^ 0x31);
else
crc <<= 1;
}
}
return crc == expectedCrc;
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
try
{
_sensor?.Dispose();
}
catch { }
}
_disposedValue = true;
}
}
void IDisposable.Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Protobuf Include="..\lights.proto" GrpcServices="Client" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.18.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.52.0" />
<PackageReference Include="Grpc.Tools" Version="2.40.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
+10
View File
@@ -0,0 +1,10 @@
LED_COUNT=9
LED_PIN=23
GAMMA=2.2
TIMEOUT=5
IDLE_BRIGHTNESS=0.30
IDLE_R=0
IDLE_G=96
IDLE_B=175
+1
View File
@@ -0,0 +1 @@
!.env
+932
View File
@@ -0,0 +1,932 @@
import os, time, threading, colorsys, math, random, datetime
from abc import ABC, abstractmethod
from typing import List, Tuple
from concurrent import futures
from dotenv import load_dotenv
import grpc
import board
import neopixel
import psutil
import lights_pb2
import lights_pb2_grpc
Color = Tuple[int, int , int]
load_dotenv()
def get_env(name) -> str:
value = os.getenv(name)
if value is None:
raise RuntimeError(f"Environment variable not found: {name}")
return value
def get_env_int(name) -> int:
return int(get_env(name))
def get_env_float(name) -> float:
return float(get_env(name))
def get_env_str(name) -> str:
return get_env(name)
def hasattr_all(obj: object, names: List[str]) -> bool:
return all([hasattr(obj, name) for name in names])
LED_COUNT = get_env_int("LED_COUNT")
LED_PIN_NUM = get_env_int("LED_PIN")
LED_PIN = getattr(board, f"D{LED_PIN_NUM}")
GAMMA = get_env_float("GAMMA")
TIMEOUT = get_env_float("TIMEOUT")
IDLE_SPEED = get_env_float("IDLE_SPEED")
IDLE_BRIGHTNESS = get_env_float("IDLE_BRIGHTNESS")
IDLE_COLOR = (
get_env_int("IDLE_R"),
get_env_int("IDLE_G"),
get_env_int("IDLE_B")
)
class Animation(ABC):
@abstractmethod
def get_pixels(self, time: float, count: int) -> List[Color]:
raise NotImplementedError()
class OffAnimation(Animation):
def get_pixels(self, time: float, count: int) -> List[Color]:
return [(0, 0, 0)] * count
class StaticColorAnimation(Animation):
def __init__(self, color: Color):
self._color = color
def get_pixels(self, time: float, count: int) -> List[Color]:
return [self._color] * count
class StaticGradient2Animation(Animation):
def __init__(self, left_color: Color, right_color: Color):
self._left_color = left_color
self._right_color = right_color
def get_pixels(self, time: float, count: int) -> List[Color]:
if count <= 0:
return []
if count == 1:
return [self._left_color]
out = []
(r1, g1, b1) = self._left_color
(r2, g2, b2) = self._right_color
for i in range(count):
pos = i / (count - 1)
r = int(r1 * (1 - pos) + r2 * pos)
g = int(g1 * (1 - pos) + g2 * pos)
b = int(b1 * (1 - pos) + b2 * pos)
out.append((r, g, b))
return out
class StaticGradient3Animation(Animation):
def __init__(self, left_color: Color, middle_color: Color, right_color: Color):
self._left_color = left_color
self._middle_color = middle_color
self._right_color = right_color
def get_pixels(self, time: float, count: int) -> List[Color]:
if count <= 0:
return []
if count == 1:
return [self._left_color]
out = []
(c1r, c1g, c1b) = self._left_color
(c2r, c2g, c2b) = self._middle_color
(c3r, c3g, c3b) = self._right_color
for i in range(count):
pos = i / (count - 1)
if pos < 0.5:
f = pos * 2
r = int(c1r * (1 - f) + c2r * f)
g = int(c1g * (1 - f) + c2g * f)
b = int(c1b * (1 - f) + c2b * f)
else:
f = (pos - 0.5) * 2
r = int(c2r * (1 - f) + c3r * f)
g = int(c2g * (1 - f) + c3g * f)
b = int(c2b * (1 - f) + c3b * f)
out.append((r, g, b))
return out
class StaticRainbowAnimation(Animation):
def get_pixels(self, time: float, count: int) -> List[Color]:
if count == 0:
return []
if count == 1:
return [(255, 0, 0)]
out = []
for i in range(count):
hue = i / count
r, g, b = colorsys.hsv_to_rgb(hue, 1.0, 1.0)
out.append((int(r * 255), int(g * 255), int(b * 255)))
return out
class BreathingColorAnimation(Animation):
def __init__(self, speed: float, color: Color):
self._speed = speed
self._color = color
def get_pixels(self, time: float, count: int) -> List[Color]:
if count <= 0:
return []
intensity = (math.sin(time * self._speed) + 1) / 2
r = int(self._color[0] * intensity)
g = int(self._color[1] * intensity)
b = int(self._color[2] * intensity)
return [(r, g, b)] * count
class BreathingGradient2Animation(Animation):
def __init__(self, speed: float, left_color: Color, right_color: Color):
self._speed = speed
self._left_color = left_color
self._right_color = right_color
def get_pixels(self, time: float, count: int) -> List[Color]:
intensity = (math.sin(time * self._speed) + 1) / 2
if count <= 0:
return []
if count == 1:
return [(
int(self._left_color[0] * intensity),
int(self._left_color[1] * intensity),
int(self._left_color[2] * intensity)
)]
out = []
(r1, g1, b1) = self._left_color
(r2, g2, b2) = self._right_color
for i in range(count):
pos = i / (count - 1)
r = int((r1 * (1 - pos) + r2 * pos) * intensity)
g = int((g1 * (1 - pos) + g2 * pos) * intensity)
b = int((b1 * (1 - pos) + b2 * pos) * intensity)
out.append((r, g, b))
return out
class BreathingGradient3Animation(Animation):
def __init__(self, speed: float, left_color: Color, middle_color: Color, right_color: Color):
self._speed = speed
self._left_color = left_color
self._middle_color = middle_color
self._right_color = right_color
def get_pixels(self, time: float, count: int) -> List[Color]:
intensity = (math.sin(time * self._speed) + 1) / 2
if count == 0:
return []
if count == 1:
return [(
int(self._left_color[0] * intensity),
int(self._left_color[1] * intensity),
int(self._left_color[2] * intensity)
)]
out = []
(c1r, c1g, c1b) = self._left_color
(c2r, c2g, c2b) = self._middle_color
(c3r, c3g, c3b) = self._right_color
for i in range(count):
pos = i / (count - 1)
if pos < 0.5:
f = pos * 2
r = int((c1r * (1 - f) + c2r * f) * intensity)
g = int((c1g * (1 - f) + c2g * f) * intensity)
b = int((c1b * (1 - f) + c2b * f) * intensity)
else:
f = (pos - 0.5) * 2
r = int((c2r * (1 - f) + c3r * f) * intensity)
g = int((c2g * (1 - f) + c3g * f) * intensity)
b = int((c2b * (1 - f) + c3b * f) * intensity)
out.append((r, g, b))
return out
class BreathingRainbowAnimation(Animation):
def __init__(self, speed: float):
self._speed = speed
def get_pixels(self, time: float, count: int) -> List[Color]:
intensity = (math.sin(time * self._speed) + 1) / 2
if count == 0:
return []
if count <= 1:
return [(255 * intensity, 0, 0)]
out = []
for i in range(count):
hue = i / count
r, g, b = colorsys.hsv_to_rgb(hue, 1.0, 1.0)
out.append((int(r * 255 * intensity), int(g * 255 * intensity), int(b * 255 * intensity)))
return out
class RunningColorAnimation(Animation):
def __init__(self, speed: float, length: int, fade: int, color: Color):
self._speed = speed
self._length = length
self._fade = fade
self._color = color
def get_pixels(self, time: float, count: int) -> List[Color]:
if count <= 0:
return []
out = []
center = (time * self._speed) % count
for i in range(count):
raw_dist = abs(i - center)
dist = min(raw_dist, count - raw_dist)
if dist <= self._length:
intensity = 1.0
elif dist <= self._length + self._fade:
fade_pos = dist - self._length
intensity = 1.0 - fade_pos / self._fade
else:
intensity = 0.0
r = int(self._color[0] * intensity)
g = int(self._color[1] * intensity)
b = int(self._color[2] * intensity)
out.append((r, g, b))
return out
class RunningGradient2Animation(Animation):
def __init__(self, speed: float, left_color: Color, right_color: Color):
self._speed = speed
self._left_color = left_color
self._right_color = right_color
def get_pixels(self, time: float, count: int) -> List[Color]:
if count <= 0:
return []
(r1, g1, b1) = self._left_color
(r2, g2, b2) = self._right_color
shift = (time * self._speed) / (count - 1)
out = []
for i in range(count):
pos = (i / (count - 1) + shift) % 1.0
r = int(r1 * (1 - pos) + r2 * pos)
g = int(g1 * (1 - pos) + g2 * pos)
b = int(b1 * (1 - pos) + b2 * pos)
out.append((r, g, b))
return out
class RunningGradient3Animation(Animation):
def __init__(self, speed: float, left_color: Color, middle_color: Color, right_color: Color):
self._speed = speed
self._left_color = left_color
self._middle_color = middle_color
self._right_color = right_color
def get_pixels(self, time: float, count: int) -> List[Color]:
if count <= 0:
return []
(c1r, c1g, c1b) = self._left_color
(c2r, c2g, c2b) = self._middle_color
(c3r, c3g, c3b) = self._right_color
shift = (time * self._speed) / (count - 1)
out = []
for i in range(count):
pos = (i / (count - 1) + shift) % 1.0
if pos < 0.5:
f = pos * 2
r = int(c1r * (1 - f) + c2r * f)
g = int(c1g * (1 - f) + c2g * f)
b = int(c1b * (1 - f) + c2b * f)
else:
f = (pos - 0.5) * 2
r = int(c2r * (1 - f) + c3r * f)
g = int(c2g * (1 - f) + c3g * f)
b = int(c2b * (1 - f) + c3b * f)
out.append((r, g, b))
return out
class RunningRainbowAnimation(Animation):
def __init__(self, speed: float):
self._speed = speed
def get_pixels(self, time: float, count: int) -> List[Color]:
if count <= 0:
return []
shift = (time * self._speed)
out = []
for i in range(count):
hue = (i + shift) % 1.0
r, g, b = colorsys.hsv_to_rgb(hue, 1.0, 1.0)
out.append((int(r * 255), int(g * 255), int(b * 255)))
return out
class SnakeAnimation(Animation):
def __init__(self, speed: float, head_length: int, tail_length: int, head_color: Color, tail_color: Color):
self._speed = speed
self._head_length = head_length
self._tail_length = tail_length
self._head_color = head_color
self._tail_color = tail_color
def get_pixels(self, time: float, count: int) -> List[Color]:
if count == 0:
return []
out = [(0, 0, 0)] * count
head_pos = (time * self._speed) % count
for i in range(count):
dist = (i - head_pos) % count
if 0 <= dist < self._head_length:
factor = 1.0
elif self._head_length <= dist < self._head_length + self._tail_length:
t = dist - self._head_length
factor = max(0.0, 1.0 - t / self._tail_length)
else:
continue
(hr, hg, hb) = self._head_color
(tr, tg, tb) = self._tail_color
r = int(hr * factor + tr * (1 - factor))
g = int(hg * factor + tg * (1 - factor))
b = int(hb * factor + tb * (1 - factor))
out[i] = (r, g, b)
return out
class FireAnimation(Animation):
def __init__(self, speed: float, color: Color):
self._speed = speed
self._color = color
def get_pixels(self, time: float, count: int) -> List[Color]:
if count == 0:
return []
(r0, g0, b0) = self._color
out = []
for i in range(count):
flicker = math.sin(i * 0.25 + time * self._speed)
flicker = 0.5 + 0.5 * flicker # -> [0..1]
flicker *= 0.7 + 0.3 * random.random()
r = int(r0 * flicker)
g = int(g0 * flicker)
b = int(b0 * flicker)
out.append((r, g, b))
return out
class SnowfallAnimation(Animation):
def __init__(self, speed: float, color: Color):
self._speed = speed
self._color = color
self._state = []
def get_pixels(self, time: float, count: int) -> List[Color]:
if len(self._state) != count:
self._state = [0.0] * count
(r0, g0, b0) = self._color
out = []
for i in range(count):
if self._state[i] <= 0 and random.random() < self._speed * 0.02:
self._state[i] = 1.0
self._state[i] = max(0.0, self._state[i] - 0.02 * self._speed)
brightness = self._state[i]
r = int(r0 * brightness)
g = int(g0 * brightness)
b = int(b0 * brightness)
out.append((r, g, b))
return out
class FlashAnimation(Animation):
def __init__(self, speed: float, color: Color):
self._speed = speed
self._color = color
self._start_time = None
def get_pixels(self, time: float, count: int) -> List[Color]:
if count == 0:
return []
if self._start_time == None:
self._start_time = time
(r0, g0, b0) = self._color
brightness = math.exp(-(time - self._start_time) * self._speed)
brightness = max(0.0, min(1.0, brightness))
r = int(r0 * brightness)
g = int(g0 * brightness)
b = int(b0 * brightness)
return [(r, g, b)] * count
class LoadAnimation(Animation):
def __init__(self, speed: float, percent: float, last_percent: float, color: Color):
self._speed = speed
self._percent = max(0.0, min(100.0, percent))
self._last_percent = max(0.0, min(100.0, last_percent))
self._color = color
self._last_time = None
def get_pixels(self, time: float, count: int) -> List[Color]:
if count <= 0:
return []
if self._last_time == None:
self._last_time = time
dt = time - self._last_time
self._last_time = time
smoothed_percent = self._last_percent + (self._percent - self._last_percent) * min(1.0, self._speed * dt)
(r0, g0, b0) = self._color
filled_float = (smoothed_percent / 100.0) * count
filled = int(filled_float)
fractional = filled_float - filled
out = []
for i in range(count):
if i < filled:
out.append((r0, g0, b0))
elif i == filled and fractional > 0:
r = int(r0 * fractional)
g = int(g0 * fractional)
b = int(b0 * fractional)
out.append((r, g, b))
else:
out.append((0, 0, 0))
# фиксируем новое сглаженное значение как last_percent
self._last_percent = smoothed_percent
return out
class CpuLoadAnimation(LoadAnimation):
def __init__(self, speed: float, color: Color):
super().__init__(speed, 0.0, 0.0, color)
def get_pixels(self, time: float, count: int) -> List[Color]:
self._percent = psutil.cpu_percent(interval=0.0)
pixels = super().get_pixels(time, count)
return pixels
class IdleAnimationUsual(RunningColorAnimation):
def __init__(self):
super().__init__(IDLE_SPEED, 1, 4, IDLE_COLOR)
def get_pixels(self, time: float, count: int) -> List[Color]:
return super().get_pixels(time, count)
class IdleAnimationNewYear(RunningRainbowAnimation): # Новогодняя пасхалка
def __init__(self):
super().__init__(IDLE_SPEED / 10)
def get_pixels(self, time: float, count: int) -> List[Color]:
return super().get_pixels(time, count)
today = datetime.datetime.now()
if (today.month == 12 and today.day == 31) or (today.month == 1 and today.day == 1):
IdleAnimation = IdleAnimationNewYear
else:
IdleAnimation = IdleAnimationUsual
class LightsDaemon(lights_pb2_grpc.LightsServicer):
def __init__(self):
self._running = True
self._lock = threading.Lock()
with self._lock:
self._pixels = neopixel.NeoPixel(
LED_PIN,
LED_COUNT,
brightness=IDLE_BRIGHTNESS,
auto_write=False
)
self._gamma = [int((i / 255) ** GAMMA * 255 + 0.5) for i in range(256)]
self._color_clamp = lambda x: max(0, min(255, x))
self._control_mode = False
self._last_ping = 0
self._animation = IdleAnimation()
self._worker = threading.Thread(target=self.loop, daemon=True)
self._worker.start()
def stop(self):
self._running = False
self._worker.join()
for i in range(len(self._pixels)):
self._pixels[i] = (0, 0, 0)
self._pixels.show()
def loop(self):
while self._running:
with self._lock:
if self._control_mode and time.time() - self._last_ping > TIMEOUT:
self._control_mode = False
self._pixels.brightness = IDLE_BRIGHTNESS
self._animation = IdleAnimation()
self._run_animation()
time.sleep(0.02)
def _run_animation(self):
new_colors = self._animation.get_pixels(time.time(), LED_COUNT)
for i in range(LED_COUNT):
self._pixels[i] = new_colors[i]
self._pixels.show()
def _update_last_ping(self):
with self._lock:
self._control_mode = True
self._last_ping = time.time()
def Ping(self, request, context):
self._update_last_ping()
return lights_pb2.Empty()
def SetOff(self, request, context):
self._update_last_ping()
with self._lock:
self._control_mode = True
self._animation = OffAnimation()
return lights_pb2.Empty()
def SetStaticColor(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "r", "g", "b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
color = (
self._gamma[self._color_clamp(request.r)],
self._gamma[self._color_clamp(request.g)],
self._gamma[self._color_clamp(request.b)],
)
self._animation = StaticColorAnimation(color)
return lights_pb2.Empty()
def SetStaticGradient2(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "left_r", "left_g", "left_b", "right_r", "right_g", "right_b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
left_color = (
self._gamma[self._color_clamp(request.left_r)],
self._gamma[self._color_clamp(request.left_g)],
self._gamma[self._color_clamp(request.left_b)],
)
right_color = (
self._gamma[self._color_clamp(request.right_r)],
self._gamma[self._color_clamp(request.right_g)],
self._gamma[self._color_clamp(request.right_b)],
)
self._animation = StaticGradient2Animation(left_color, right_color)
return lights_pb2.Empty()
def SetStaticGradient3(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "left_r", "left_g", "left_b", "middle_r", "middle_g", "middle_b", "right_r", "right_g", "right_b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
left_color = (
self._gamma[self._color_clamp(request.left_r)],
self._gamma[self._color_clamp(request.left_g)],
self._gamma[self._color_clamp(request.left_b)],
)
middle_color = (
self._gamma[self._color_clamp(request.middle_r)],
self._gamma[self._color_clamp(request.middle_g)],
self._gamma[self._color_clamp(request.middle_b)],
)
right_color = (
self._gamma[self._color_clamp(request.right_r)],
self._gamma[self._color_clamp(request.right_g)],
self._gamma[self._color_clamp(request.right_b)],
)
self._animation = StaticGradient3Animation(left_color, middle_color, right_color)
return lights_pb2.Empty()
def SetStaticRainbow(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
self._animation = StaticRainbowAnimation()
return lights_pb2.Empty()
def SetBreathingColor(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "r", "g", "b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
color = (
self._gamma[self._color_clamp(request.r)],
self._gamma[self._color_clamp(request.g)],
self._gamma[self._color_clamp(request.b)],
)
self._animation = BreathingColorAnimation(request.speed, color)
return lights_pb2.Empty()
def SetBreathingGradient2(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "left_r", "left_g", "left_b", "right_r", "right_g", "right_b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
left_color = (
self._gamma[self._color_clamp(request.left_r)],
self._gamma[self._color_clamp(request.left_g)],
self._gamma[self._color_clamp(request.left_b)],
)
right_color = (
self._gamma[self._color_clamp(request.right_r)],
self._gamma[self._color_clamp(request.right_g)],
self._gamma[self._color_clamp(request.right_b)],
)
self._animation = BreathingGradient2Animation(request.speed, left_color, right_color)
return lights_pb2.Empty()
def SetBreathingGradient3(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "left_r", "left_g", "left_b", "middle_r", "middle_g", "middle_b", "right_r", "right_g", "right_b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
left_color = (
self._gamma[self._color_clamp(request.left_r)],
self._gamma[self._color_clamp(request.left_g)],
self._gamma[self._color_clamp(request.left_b)],
)
middle_color = (
self._gamma[self._color_clamp(request.middle_r)],
self._gamma[self._color_clamp(request.middle_g)],
self._gamma[self._color_clamp(request.middle_b)],
)
right_color = (
self._gamma[self._color_clamp(request.right_r)],
self._gamma[self._color_clamp(request.right_g)],
self._gamma[self._color_clamp(request.right_b)],
)
self._animation = BreathingGradient3Animation(request.speed, left_color, middle_color, right_color)
return lights_pb2.Empty()
def SetBreathingRainbow(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
self._animation = BreathingRainbowAnimation(request.speed)
return lights_pb2.Empty()
def SetRunningColor(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "length", "fade", "r", "g", "b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
color = (
self._gamma[self._color_clamp(request.r)],
self._gamma[self._color_clamp(request.g)],
self._gamma[self._color_clamp(request.b)],
)
self._animation = RunningColorAnimation(request.speed, request.length, request.fade, color)
return lights_pb2.Empty()
def SetRunningGradient2(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "left_r", "left_g", "left_b", "right_r", "right_g", "right_b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
left_color = (
self._gamma[self._color_clamp(request.left_r)],
self._gamma[self._color_clamp(request.left_g)],
self._gamma[self._color_clamp(request.left_b)],
)
right_color = (
self._gamma[self._color_clamp(request.right_r)],
self._gamma[self._color_clamp(request.right_g)],
self._gamma[self._color_clamp(request.right_b)],
)
self._animation = RunningGradient2Animation(request.speed, left_color, right_color)
return lights_pb2.Empty()
def SetRunningGradient3(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "left_r", "left_g", "left_b", "middle_r", "middle_g", "middle_b", "right_r", "right_g", "right_b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
left_color = (
self._gamma[self._color_clamp(request.left_r)],
self._gamma[self._color_clamp(request.left_g)],
self._gamma[self._color_clamp(request.left_b)],
)
middle_color = (
self._gamma[self._color_clamp(request.middle_r)],
self._gamma[self._color_clamp(request.middle_g)],
self._gamma[self._color_clamp(request.middle_b)],
)
right_color = (
self._gamma[self._color_clamp(request.right_r)],
self._gamma[self._color_clamp(request.right_g)],
self._gamma[self._color_clamp(request.right_b)],
)
self._animation = RunningGradient3Animation(request.speed, left_color, middle_color, right_color)
return lights_pb2.Empty()
def SetRunningRainbow(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
self._animation = RunningRainbowAnimation(request.speed)
return lights_pb2.Empty()
def SetSnake(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "head_length", "tail_length", "head_r", "head_g", "head_b", "tail_r", "tail_g", "tail_b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
head_color = (
self._gamma[self._color_clamp(request.head_r)],
self._gamma[self._color_clamp(request.head_g)],
self._gamma[self._color_clamp(request.head_b)],
)
tail_color = (
self._gamma[self._color_clamp(request.tail_r)],
self._gamma[self._color_clamp(request.tail_g)],
self._gamma[self._color_clamp(request.tail_b)],
)
self._animation = SnakeAnimation(request.speed, request.head_length, request.tail_length, head_color, tail_color)
return lights_pb2.Empty()
def SetFire(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "r", "g", "b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
color = (
self._gamma[self._color_clamp(request.r)],
self._gamma[self._color_clamp(request.g)],
self._gamma[self._color_clamp(request.b)],
)
self._animation = FireAnimation(request.speed, color)
return lights_pb2.Empty()
def SetSnowfall(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "r", "g", "b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
color = (
self._gamma[self._color_clamp(request.r)],
self._gamma[self._color_clamp(request.g)],
self._gamma[self._color_clamp(request.b)],
)
self._animation = SnowfallAnimation(request.speed, color)
return lights_pb2.Empty()
def SetFlash(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "r", "g", "b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
color = (
self._gamma[self._color_clamp(request.r)],
self._gamma[self._color_clamp(request.g)],
self._gamma[self._color_clamp(request.b)],
)
self._animation = FlashAnimation(request.speed, color)
return lights_pb2.Empty()
def SetLoad(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "percent", "last_percent", "r", "g", "b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
color = (
self._gamma[self._color_clamp(request.r)],
self._gamma[self._color_clamp(request.g)],
self._gamma[self._color_clamp(request.b)],
)
self._animation = LoadAnimation(request.speed, request.percent, request.last_percent, color)
return lights_pb2.Empty()
def SetCpuLoad(self, request, context):
self._update_last_ping()
if not hasattr_all(request, ["brightness", "speed", "r", "g", "b"]):
return lights_pb2.Empty()
with self._lock:
self._control_mode = True
self._pixels.brightness = request.brightness
color = (
self._gamma[self._color_clamp(request.r)],
self._gamma[self._color_clamp(request.g)],
self._gamma[self._color_clamp(request.b)],
)
self._animation = CpuLoadAnimation(request.speed, color)
return lights_pb2.Empty()
def serve():
daemon = LightsDaemon()
server = grpc.server(futures.ThreadPoolExecutor(max_workers=1))
lights_pb2_grpc.add_LightsServicer_to_server(daemon, server)
server.add_insecure_port('localhost:50051')
server.start()
print("Lights daemon started on port 50051")
try:
server.wait_for_termination()
except KeyboardInterrupt:
print("Shutting down")
server.stop(0)
daemon.stop()
print("Lights daemon stopped")
if __name__ == "__main__":
serve()
File diff suppressed because one or more lines are too long
@@ -0,0 +1,914 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import warnings
import lights_pb2 as lights__pb2
GRPC_GENERATED_VERSION = '1.76.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False
try:
from grpc._utilities import first_version_is_lower
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
except ImportError:
_version_not_supported = True
if _version_not_supported:
raise RuntimeError(
f'The grpc package installed is at version {GRPC_VERSION},'
+ ' but the generated code in lights_pb2_grpc.py depends on'
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
)
class LightsStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Ping = channel.unary_unary(
'/GSS2.LightsControl.Lights/Ping',
request_serializer=lights__pb2.Empty.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetOff = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetOff',
request_serializer=lights__pb2.Empty.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetStaticColor = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetStaticColor',
request_serializer=lights__pb2.StaticColorRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetStaticGradient2 = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetStaticGradient2',
request_serializer=lights__pb2.StaticGradient2Request.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetStaticGradient3 = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetStaticGradient3',
request_serializer=lights__pb2.StaticGradient3Request.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetStaticRainbow = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetStaticRainbow',
request_serializer=lights__pb2.StaticRainbowRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetBreathingColor = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetBreathingColor',
request_serializer=lights__pb2.BreathingColorRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetBreathingGradient2 = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetBreathingGradient2',
request_serializer=lights__pb2.BreathingGradient2Request.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetBreathingGradient3 = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetBreathingGradient3',
request_serializer=lights__pb2.BreathingGradient3Request.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetBreathingRainbow = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetBreathingRainbow',
request_serializer=lights__pb2.BreathingRainbowRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetRunningColor = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetRunningColor',
request_serializer=lights__pb2.RunningColorRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetRunningGradient2 = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetRunningGradient2',
request_serializer=lights__pb2.RunningGradient2Request.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetRunningGradient3 = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetRunningGradient3',
request_serializer=lights__pb2.RunningGradient3Request.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetRunningRainbow = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetRunningRainbow',
request_serializer=lights__pb2.RunningRainbowRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetSnake = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetSnake',
request_serializer=lights__pb2.SnakeRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetFire = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetFire',
request_serializer=lights__pb2.FireRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetSnowfall = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetSnowfall',
request_serializer=lights__pb2.SnowfallRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetFlash = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetFlash',
request_serializer=lights__pb2.FlashRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetLoad = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetLoad',
request_serializer=lights__pb2.LoadRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
self.SetCpuLoad = channel.unary_unary(
'/GSS2.LightsControl.Lights/SetCpuLoad',
request_serializer=lights__pb2.CpuLoadRequest.SerializeToString,
response_deserializer=lights__pb2.Empty.FromString,
_registered_method=True)
class LightsServicer(object):
"""Missing associated documentation comment in .proto file."""
def Ping(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetOff(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetStaticColor(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetStaticGradient2(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetStaticGradient3(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetStaticRainbow(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetBreathingColor(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetBreathingGradient2(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetBreathingGradient3(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetBreathingRainbow(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetRunningColor(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetRunningGradient2(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetRunningGradient3(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetRunningRainbow(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetSnake(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetFire(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetSnowfall(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetFlash(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetLoad(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def SetCpuLoad(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_LightsServicer_to_server(servicer, server):
rpc_method_handlers = {
'Ping': grpc.unary_unary_rpc_method_handler(
servicer.Ping,
request_deserializer=lights__pb2.Empty.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetOff': grpc.unary_unary_rpc_method_handler(
servicer.SetOff,
request_deserializer=lights__pb2.Empty.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetStaticColor': grpc.unary_unary_rpc_method_handler(
servicer.SetStaticColor,
request_deserializer=lights__pb2.StaticColorRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetStaticGradient2': grpc.unary_unary_rpc_method_handler(
servicer.SetStaticGradient2,
request_deserializer=lights__pb2.StaticGradient2Request.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetStaticGradient3': grpc.unary_unary_rpc_method_handler(
servicer.SetStaticGradient3,
request_deserializer=lights__pb2.StaticGradient3Request.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetStaticRainbow': grpc.unary_unary_rpc_method_handler(
servicer.SetStaticRainbow,
request_deserializer=lights__pb2.StaticRainbowRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetBreathingColor': grpc.unary_unary_rpc_method_handler(
servicer.SetBreathingColor,
request_deserializer=lights__pb2.BreathingColorRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetBreathingGradient2': grpc.unary_unary_rpc_method_handler(
servicer.SetBreathingGradient2,
request_deserializer=lights__pb2.BreathingGradient2Request.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetBreathingGradient3': grpc.unary_unary_rpc_method_handler(
servicer.SetBreathingGradient3,
request_deserializer=lights__pb2.BreathingGradient3Request.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetBreathingRainbow': grpc.unary_unary_rpc_method_handler(
servicer.SetBreathingRainbow,
request_deserializer=lights__pb2.BreathingRainbowRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetRunningColor': grpc.unary_unary_rpc_method_handler(
servicer.SetRunningColor,
request_deserializer=lights__pb2.RunningColorRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetRunningGradient2': grpc.unary_unary_rpc_method_handler(
servicer.SetRunningGradient2,
request_deserializer=lights__pb2.RunningGradient2Request.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetRunningGradient3': grpc.unary_unary_rpc_method_handler(
servicer.SetRunningGradient3,
request_deserializer=lights__pb2.RunningGradient3Request.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetRunningRainbow': grpc.unary_unary_rpc_method_handler(
servicer.SetRunningRainbow,
request_deserializer=lights__pb2.RunningRainbowRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetSnake': grpc.unary_unary_rpc_method_handler(
servicer.SetSnake,
request_deserializer=lights__pb2.SnakeRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetFire': grpc.unary_unary_rpc_method_handler(
servicer.SetFire,
request_deserializer=lights__pb2.FireRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetSnowfall': grpc.unary_unary_rpc_method_handler(
servicer.SetSnowfall,
request_deserializer=lights__pb2.SnowfallRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetFlash': grpc.unary_unary_rpc_method_handler(
servicer.SetFlash,
request_deserializer=lights__pb2.FlashRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetLoad': grpc.unary_unary_rpc_method_handler(
servicer.SetLoad,
request_deserializer=lights__pb2.LoadRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
'SetCpuLoad': grpc.unary_unary_rpc_method_handler(
servicer.SetCpuLoad,
request_deserializer=lights__pb2.CpuLoadRequest.FromString,
response_serializer=lights__pb2.Empty.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'GSS2.LightsControl.Lights', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
server.add_registered_method_handlers('GSS2.LightsControl.Lights', rpc_method_handlers)
# This class is part of an EXPERIMENTAL API.
class Lights(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def Ping(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/Ping',
lights__pb2.Empty.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetOff(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetOff',
lights__pb2.Empty.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetStaticColor(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetStaticColor',
lights__pb2.StaticColorRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetStaticGradient2(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetStaticGradient2',
lights__pb2.StaticGradient2Request.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetStaticGradient3(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetStaticGradient3',
lights__pb2.StaticGradient3Request.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetStaticRainbow(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetStaticRainbow',
lights__pb2.StaticRainbowRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetBreathingColor(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetBreathingColor',
lights__pb2.BreathingColorRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetBreathingGradient2(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetBreathingGradient2',
lights__pb2.BreathingGradient2Request.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetBreathingGradient3(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetBreathingGradient3',
lights__pb2.BreathingGradient3Request.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetBreathingRainbow(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetBreathingRainbow',
lights__pb2.BreathingRainbowRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetRunningColor(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetRunningColor',
lights__pb2.RunningColorRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetRunningGradient2(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetRunningGradient2',
lights__pb2.RunningGradient2Request.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetRunningGradient3(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetRunningGradient3',
lights__pb2.RunningGradient3Request.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetRunningRainbow(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetRunningRainbow',
lights__pb2.RunningRainbowRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetSnake(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetSnake',
lights__pb2.SnakeRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetFire(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetFire',
lights__pb2.FireRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetSnowfall(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetSnowfall',
lights__pb2.SnowfallRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetFlash(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetFlash',
lights__pb2.FlashRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetLoad(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetLoad',
lights__pb2.LoadRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetCpuLoad(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/GSS2.LightsControl.Lights/SetCpuLoad',
lights__pb2.CpuLoadRequest.SerializeToString,
lights__pb2.Empty.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
Binary file not shown.
+287
View File
@@ -0,0 +1,287 @@
import grpc, time
import lights_pb2, lights_pb2_grpc
stub: lights_pb2_grpc.LightsStub = None
def connect():
global stub
channel = grpc.insecure_channel('localhost:50051')
stub = lights_pb2_grpc.LightsStub(channel)
stub.Ping(lights_pb2.Empty())
print("Пинг отправлен")
def disconnect():
global stub
stub.SetOff(lights_pb2.Empty())
print("Светодиоды выключены")
def StaticColor():
print("StaticColor")
global stub
request = lights_pb2.StaticColorRequest(brightness=0.5, r=255, g=0, b=0)
stub.SetStaticColor(request)
print("Красный")
time.sleep(1)
request = lights_pb2.StaticColorRequest(brightness=0.5, r=0, g=0, b=255)
stub.SetStaticColor(request)
print("Синий")
time.sleep(1)
request = lights_pb2.StaticColorRequest(brightness=0.5, r=0, g=255, b=0)
stub.SetStaticColor(request)
print("Зелёный")
time.sleep(1)
def StaticGradient2():
print("StaticGradient2")
global stub
request = lights_pb2.StaticGradient2Request(brightness=0.5, left_r=255, left_g=0, left_b=0, right_r=0, right_g=0, right_b=255)
stub.SetStaticGradient2(request)
print("Красный-Синий")
time.sleep(1)
request = lights_pb2.StaticGradient2Request(brightness=0.5, left_r=0, left_g=0, left_b=255, right_r=0, right_g=255, right_b=0)
stub.SetStaticGradient2(request)
print("Синий-Зелёный")
time.sleep(1)
request = lights_pb2.StaticGradient2Request(brightness=0.5, left_r=0, left_g=255, left_b=0, right_r=255, right_g=0, right_b=0)
stub.SetStaticGradient2(request)
print("Зелёный-Красный")
time.sleep(1)
def StaticGradient3():
print("StaticGradient3")
global stub
request = lights_pb2.StaticGradient3Request(brightness=0.5, left_r=255, left_g=0, left_b=0, middle_r=0, middle_g=255, middle_b=0, right_r=0, right_g=0, right_b=255)
stub.SetStaticGradient3(request)
print("Красный-Зелёный-Синий")
time.sleep(1)
request = lights_pb2.StaticGradient3Request(brightness=0.5, left_r=0, left_g=255, left_b=0, middle_r=0, middle_g=0, middle_b=255, right_r=255, right_g=0, right_b=0)
stub.SetStaticGradient3(request)
print("Зелёный-Синий-Красный")
time.sleep(1)
request = lights_pb2.StaticGradient3Request(brightness=0.5, left_r=0, left_g=0, left_b=255, middle_r=255, middle_g=0, middle_b=0, right_r=0, right_g=255, right_b=0)
stub.SetStaticGradient3(request)
print("Синий-Красный-Зелёный")
time.sleep(1)
def StaticRainbow():
print("StaticRainbow")
global stub
request = lights_pb2.StaticRainbowRequest(brightness=0.5)
stub.SetStaticRainbow(request)
print("Радуга")
time.sleep(2)
def BreathingColor():
print("BreathingColor")
global stub
request = lights_pb2.BreathingColorRequest(brightness=0.5, speed=4, r=255, g=0, b=0)
stub.SetBreathingColor(request)
print("Красный")
time.sleep(3)
request = lights_pb2.BreathingColorRequest(brightness=0.5, speed=4, r=0, g=0, b=255)
stub.SetBreathingColor(request)
print("Синий")
time.sleep(3)
request = lights_pb2.BreathingColorRequest(brightness=0.5, speed=4, r=0, g=255, b=0)
stub.SetBreathingColor(request)
print("Зелёный")
time.sleep(3)
def BreathingGradient2():
print("BreathingGradient2")
global stub
request = lights_pb2.BreathingGradient2Request(brightness=0.5, speed=4, left_r=255, left_g=0, left_b=0, right_r=0, right_g=0, right_b=255)
stub.SetBreathingGradient2(request)
print("Красный-Синий")
time.sleep(3)
request = lights_pb2.BreathingGradient2Request(brightness=0.5, speed=4, left_r=0, left_g=0, left_b=255, right_r=0, right_g=255, right_b=0)
stub.SetBreathingGradient2(request)
print("Синий-Зелёный")
time.sleep(3)
request = lights_pb2.BreathingGradient2Request(brightness=0.5, speed=4, left_r=0, left_g=255, left_b=0, right_r=255, right_g=0, right_b=0)
stub.SetBreathingGradient2(request)
print("Зелёный-Красный")
time.sleep(3)
def BreathingGradient3():
print("BreathingGradient3")
global stub
request = lights_pb2.BreathingGradient3Request(brightness=0.5, speed=4, left_r=255, left_g=0, left_b=0, middle_r=0, middle_g=255, middle_b=0, right_r=0, right_g=0, right_b=255)
stub.SetBreathingGradient3(request)
print("Красный-Зелёный-Синий")
time.sleep(3)
request = lights_pb2.BreathingGradient3Request(brightness=0.5, speed=4, left_r=0, left_g=255, left_b=0, middle_r=0, middle_g=0, middle_b=255, right_r=255, right_g=0, right_b=0)
stub.SetBreathingGradient3(request)
print("Зелёный-Синий-Красный")
time.sleep(3)
request = lights_pb2.BreathingGradient3Request(brightness=0.5, speed=4, left_r=0, left_g=0, left_b=255, middle_r=255, middle_g=0, middle_b=0, right_r=0, right_g=255, right_b=0)
stub.SetBreathingGradient3(request)
print("Синий-Красный-Зелёный")
time.sleep(3)
def BreathingRainbow():
print("BreathingRainbow")
global stub
request = lights_pb2.BreathingRainbowRequest(brightness=0.5, speed=4)
stub.SetBreathingRainbow(request)
print("Радуга")
time.sleep(2)
def RunningColor():
print("RunningColor")
global stub
request = lights_pb2.RunningColorRequest(brightness=0.5, speed=4, length=1, fade=4, r=125, g=0, b=0)
stub.SetRunningColor(request)
print("Красный")
time.sleep(3)
request = lights_pb2.RunningColorRequest(brightness=0.5, speed=4, length=1, fade=4, r=0, g=0, b=125)
stub.SetRunningColor(request)
print("Синий")
time.sleep(3)
request = lights_pb2.RunningColorRequest(brightness=0.5, speed=4, length=1, fade=4, r=0, g=125, b=0)
stub.SetRunningColor(request)
print("Зелёный")
time.sleep(3)
def RunningGradient2():
print("RunningGradient2")
global stub
request = lights_pb2.RunningGradient2Request(brightness=0.5, speed=4, left_r=255, left_g=0, left_b=0, right_r=0, right_g=0, right_b=255)
stub.SetRunningGradient2(request)
print("Красный-Синий")
time.sleep(3)
request = lights_pb2.RunningGradient2Request(brightness=0.5, speed=4, left_r=0, left_g=0, left_b=255, right_r=0, right_g=255, right_b=0)
stub.SetRunningGradient2(request)
print("Синий-Зелёный")
time.sleep(3)
request = lights_pb2.RunningGradient2Request(brightness=0.5, speed=4, left_r=0, left_g=255, left_b=0, right_r=255, right_g=0, right_b=0)
stub.SetRunningGradient2(request)
print("Зелёный-Красный")
time.sleep(3)
def RunningGradient3():
print("RunningGradient3")
global stub
request = lights_pb2.RunningGradient3Request(brightness=0.5, speed=4, left_r=255, left_g=0, left_b=0, middle_r=0, middle_g=255, middle_b=0, right_r=0, right_g=0, right_b=255)
stub.SetRunningGradient3(request)
print("Красный-Зелёный-Синий")
time.sleep(3)
request = lights_pb2.RunningGradient3Request(brightness=0.5, speed=4, left_r=0, left_g=255, left_b=0, middle_r=0, middle_g=0, middle_b=255, right_r=255, right_g=0, right_b=0)
stub.SetRunningGradient3(request)
print("Зелёный-Синий-Красный")
time.sleep(3)
request = lights_pb2.RunningGradient3Request(brightness=0.5, speed=4, left_r=0, left_g=0, left_b=255, middle_r=255, middle_g=0, middle_b=0, right_r=0, right_g=255, right_b=0)
stub.SetRunningGradient3(request)
print("Синий-Красный-Зелёный")
time.sleep(3)
def Snake():
print("Snake")
global stub
request = lights_pb2.SnakeRequest(brightness=0.5, speed=2, head_length=1, tail_length=3, head_r=255, head_g=0, head_b=0, tail_r=0, tail_g=255, tail_b=0)
stub.SetSnake(request)
print("Красный-Зелёный")
time.sleep(3)
request = lights_pb2.SnakeRequest(brightness=0.5, speed=2, head_length=1, tail_length=3, head_r=0, head_g=0, head_b=255, tail_r=255, tail_g=0, tail_b=0)
stub.SetSnake(request)
print("Зелёный-Красный")
time.sleep(3)
request = lights_pb2.SnakeRequest(brightness=0.5, speed=2, head_length=1, tail_length=3, head_r=0, head_g=255, head_b=0, tail_r=0, tail_g=0, tail_b=255)
stub.SetSnake(request)
print("Зелёный-Синий")
time.sleep(3)
def RunningRainbow():
print("RunningRainbow")
global stub
request = lights_pb2.RunningRainbowRequest(brightness=0.5, speed=1)
stub.SetRunningRainbow(request)
print("Радуга")
time.sleep(2)
def Fire():
print("Fire")
global stub
request = lights_pb2.FireRequest(brightness=0.5, speed=4, r=255, g=0, b=0)
stub.SetFire(request)
print("Красный")
time.sleep(3)
request = lights_pb2.FireRequest(brightness=0.5, speed=4, r=0, g=0, b=255)
stub.SetFire(request)
print("Синий")
time.sleep(3)
request = lights_pb2.FireRequest(brightness=0.5, speed=4, r=0, g=255, b=0)
stub.SetFire(request)
print("Зелёный")
time.sleep(3)
def Snowfall():
print("Snowfall")
global stub
request = lights_pb2.SnowfallRequest(brightness=0.5, speed=1, r=255, g=0, b=0)
stub.SetSnowfall(request)
print("Красный")
time.sleep(3)
request = lights_pb2.SnowfallRequest(brightness=0.5, speed=1, r=0, g=0, b=255)
stub.SetSnowfall(request)
print("Синий")
time.sleep(3)
request = lights_pb2.SnowfallRequest(brightness=0.5, speed=1, r=0, g=255, b=0)
stub.SetSnowfall(request)
print("Зелёный")
time.sleep(3)
def Flash():
print("Flash")
global stub
request = lights_pb2.FlashRequest(brightness=0.5, speed=6, r=255, g=0, b=0)
stub.SetFlash(request)
print("Красный")
time.sleep(3)
request = lights_pb2.FlashRequest(brightness=0.5, speed=6, r=0, g=0, b=255)
stub.SetFlash(request)
print("Синий")
time.sleep(3)
request = lights_pb2.FlashRequest(brightness=0.5, speed=6, r=0, g=255, b=0)
stub.SetFlash(request)
print("Зелёный")
time.sleep(3)
def Load():
print("Load")
global stub
last_percent = 0
for p in range(0, 20):
request = lights_pb2.LoadRequest(brightness=0.5, speed=1, percent=float(p * 5), last_percent=last_percent, r=255, g=255, b=255)
last_percent = float(p * 5)
stub.SetLoad(request)
time.sleep(0.2)
def CpuLoad():
print("CpuLoad")
global stub
request = lights_pb2.CpuLoadRequest(brightness=0.5, speed=1, r=255, g=255, b=255)
stub.SetCpuLoad(request)
time.sleep(5)
if __name__ == "__main__":
connect()
# StaticColor()
# StaticGradient2()
# StaticGradient3()
# StaticRainbow()
# BreathingColor()
# BreathingGradient2()
# BreathingGradient3()
# BreathingRainbow()
# RunningColor()
# RunningGradient2() #fix right
# RunningGradient3() #fix right
# RunningRainbow() #fix run not change
# Snake()
# Fire()
# Snowfall()
# Flash()
# Load()
# CpuLoad()
disconnect()
+189
View File
@@ -0,0 +1,189 @@
syntax = "proto3";
option csharp_namespace = "GSS2.LightsControl.CSharpClient";
package GSS2.LightsControl;
service Lights {
rpc Ping (Empty) returns (Empty);
rpc SetOff (Empty) returns (Empty);
rpc SetStaticColor (StaticColorRequest) returns (Empty);
rpc SetStaticGradient2 (StaticGradient2Request) returns (Empty);
rpc SetStaticGradient3 (StaticGradient3Request) returns (Empty);
rpc SetStaticRainbow (StaticRainbowRequest) returns (Empty);
rpc SetBreathingColor (BreathingColorRequest) returns (Empty);
rpc SetBreathingGradient2 (BreathingGradient2Request) returns (Empty);
rpc SetBreathingGradient3 (BreathingGradient3Request) returns (Empty);
rpc SetBreathingRainbow (BreathingRainbowRequest) returns (Empty);
rpc SetRunningColor (RunningColorRequest) returns (Empty);
rpc SetRunningGradient2 (RunningGradient2Request) returns (Empty);
rpc SetRunningGradient3 (RunningGradient3Request) returns (Empty);
rpc SetRunningRainbow (RunningRainbowRequest) returns (Empty);
rpc SetSnake (SnakeRequest) returns (Empty);
rpc SetFire (FireRequest) returns (Empty);
rpc SetSnowfall (SnowfallRequest) returns (Empty);
rpc SetFlash (FlashRequest) returns (Empty);
rpc SetLoad (LoadRequest) returns (Empty);
rpc SetCpuLoad (CpuLoadRequest) returns (Empty);
}
message Empty {}
message StaticColorRequest {
double brightness = 1;
int32 r = 2;
int32 g = 3;
int32 b = 4;
}
message StaticGradient2Request {
double brightness = 1;
int32 left_r = 2;
int32 left_g = 3;
int32 left_b = 4;
int32 right_r = 5;
int32 right_g = 6;
int32 right_b = 7;
}
message StaticGradient3Request {
double brightness = 1;
int32 left_r = 2;
int32 left_g = 3;
int32 left_b = 4;
int32 middle_r = 5;
int32 middle_g = 6;
int32 middle_b = 7;
int32 right_r = 8;
int32 right_g = 9;
int32 right_b = 10;
}
message StaticRainbowRequest {
double brightness = 1;
}
message BreathingColorRequest {
double brightness = 1;
double speed = 2;
int32 r = 3;
int32 g = 4;
int32 b = 5;
}
message BreathingGradient2Request {
double brightness = 1;
double speed = 2;
int32 left_r = 3;
int32 left_g = 4;
int32 left_b = 5;
int32 right_r = 6;
int32 right_g = 7;
int32 right_b = 8;
}
message BreathingGradient3Request {
double brightness = 1;
double speed = 2;
int32 left_r = 3;
int32 left_g = 4;
int32 left_b = 5;
int32 middle_r = 6;
int32 middle_g = 7;
int32 middle_b = 8;
int32 right_r = 9;
int32 right_g = 10;
int32 right_b = 11;
}
message BreathingRainbowRequest {
double brightness = 1;
double speed = 2;
}
message RunningColorRequest {
double brightness = 1;
double speed = 2;
int32 length = 3;
int32 fade = 4;
int32 r = 5;
int32 g = 6;
int32 b = 7;
}
message RunningGradient2Request {
double brightness = 1;
double speed = 2;
int32 left_r = 3;
int32 left_g = 4;
int32 left_b = 5;
int32 right_r = 6;
int32 right_g = 7;
int32 right_b = 8;
}
message RunningGradient3Request {
double brightness = 1;
double speed = 2;
int32 left_r = 3;
int32 left_g = 4;
int32 left_b = 5;
int32 middle_r = 6;
int32 middle_g = 7;
int32 middle_b = 8;
int32 right_r = 9;
int32 right_g = 10;
int32 right_b = 11;
}
message RunningRainbowRequest {
double brightness = 1;
double speed = 2;
}
message SnakeRequest {
double brightness = 1;
double speed = 2;
int32 head_length = 3;
int32 tail_length = 4;
int32 head_r = 5;
int32 head_g = 6;
int32 head_b = 7;
int32 tail_r = 8;
int32 tail_g = 9;
int32 tail_b = 10;
}
message FireRequest {
double brightness = 1;
double speed = 2;
int32 r = 3;
int32 g = 4;
int32 b = 5;
}
message SnowfallRequest {
double brightness = 1;
double speed = 2;
int32 r = 3;
int32 g = 4;
int32 b = 5;
}
message FlashRequest {
double brightness = 1;
double speed = 2;
int32 r = 3;
int32 g = 4;
int32 b = 5;
}
message LoadRequest {
double brightness = 1;
double speed = 2;
double percent = 3;
double last_percent = 4;
int32 r = 5;
int32 g = 6;
int32 b = 7;
}
message CpuLoadRequest {
double brightness = 1;
double speed = 2;
int32 r = 3;
int32 g = 4;
int32 b = 5;
}
+17
View File
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-GSS2.Test-191ece0a-855d-46ec-9ecb-44378fc3db83</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GSS2.Core\GSS2.Core.csproj" />
</ItemGroup>
</Project>
+66
View File
@@ -0,0 +1,66 @@
using System.Data;
using GSS2.Core.Analysis.AmineContent.Database;
using GSS2.Core.Hardware;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Protocols.Configuration;
namespace GSS2.Test;
public class Program
{
public static void Main(string[] args)
{
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddDbContext<AmineContentCalibrationContext>(
options =>
{
var databasePath = builder.Configuration.GetConnectionString("AmineContentCalibrationDatabasePath");
options.UseSqlite($"Data Source={databasePath}");
},
ServiceLifetime.Singleton
);
builder.Services.AddSingleton<LightsService>(
serviceProvider =>
{
var configuration = serviceProvider.GetService<IConfiguration>();
if (configuration is null)
throw new Exception("Cannot get configuration");
var serviceConfiguration = configuration.GetValue<LightsService.LightsServiceConfiguration>("Lights");
if (serviceConfiguration is null)
throw new InvalidConfigurationException("Cannot get configuration (Lights)");
var logger = serviceProvider.GetService<ILogger<LightsService>>();
if (logger is null)
throw new Exception("Cannot get logger");
return new LightsService(logger, serviceConfiguration);
}
);
builder.Services.AddSingleton<TemperatureHumidityService>(
serviceProvider =>
{
var configuration = serviceProvider.GetService<IConfiguration>();
if (configuration is null)
throw new Exception("Cannot get configuration");
var serviceConfiguration = configuration.GetValue<TemperatureHumidityService.TemperatureHumidityServiceConfiguration>("TemperatureHumidity");
if (serviceConfiguration is null)
throw new InvalidConfigurationException("Cannot get configuration (TemperatureHumidity)");
var logger = serviceProvider.GetService<ILogger<TemperatureHumidityService>>();
if (logger is null)
throw new Exception("Cannot get logger");
return new TemperatureHumidityService(logger, serviceConfiguration);
}
);
builder.Services.AddHostedService<Worker>();
var host = builder.Build();
using (var scope = host.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<AmineContentCalibrationContext>();
context.Database.Migrate();
}
host.Run();
}
}
+30
View File
@@ -0,0 +1,30 @@
using System.Drawing;
using GSS2.Core.Analysis.AmineContent.Database;
using GSS2.Core.Hardware;
namespace GSS2.Test;
public class Worker(
ILogger<Worker> logger,
AmineContentCalibrationContext calibrationContext,
LightsService lightsService,
TemperatureHumidityService temperatureHumidity
) : BackgroundService
{
private readonly ILogger<Worker> _logger = logger;
private readonly AmineContentCalibrationContext _calibrationContext = calibrationContext;
private readonly LightsService _lightsService = lightsService;
private readonly TemperatureHumidityService _temperatureHumidity = temperatureHumidity;
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
_ = _lightsService.Run(cancellationToken);
await _lightsService.SnowfallAsync(0.3, 1, Color.Aqua, cancellationToken);
while (!cancellationToken.IsCancellationRequested)
{
var (temperature, humidity) = await _temperatureHumidity.MeasureAsync();
_logger.LogInformation("Temperature: {}, RelativeHumidity: {}", temperature, humidity);
await Task.Delay(5000);
}
}
}
+33
View File
@@ -0,0 +1,33 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"AmineContentCalibrationDatabasePath": "AmineContentCalibration.db"
},
"Hardware": {
"Illuminator": {
"WhitePwmPin": 13,
"Uv254PwmPin": 18,
"Uv365PwmPin": 12,
"WhiteRelayPin": 16,
"Uv254RelayPin": 15,
"Uv365RelayPin": 20,
"FanPin": 21,
"PwmFrequency": 200,
"MaxWhitePwmDuty": 1.0,
"MaxUv254PwmDuty": 1.0,
"MaxUv365PwmDuty": 1.0
},
"TemperatureHumidity": {
"Bus": 0,
"Address": 68
},
"Lights": {
"ServerAddress": "localhost:50051"
}
}
}
+70
View File
@@ -0,0 +1,70 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GSS2.Core", "GSS2.Core\GSS2.Core.csproj", "{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GSS2.Test", "GSS2.Test\GSS2.Test.csproj", "{94F2F407-23DC-4945-AE67-B33C540DEF28}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GSS2.LightsControl", "GSS2.LightsControl", "{B8CA8CAF-03B6-2A4B-B2EF-758B3B2ADED2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CSharpClient", "CSharpClient", "{478CE71E-3653-8F33-D0F9-CF70B2F74279}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GSS2.LightsControl.CSharpClient", "GSS2.LightsControl\CSharpClient\GSS2.LightsControl.CSharpClient.csproj", "{D0130B90-6ECA-49CA-A126-EFEFF481FC74}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Debug|x64.ActiveCfg = Debug|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Debug|x64.Build.0 = Debug|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Debug|x86.ActiveCfg = Debug|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Debug|x86.Build.0 = Debug|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Release|Any CPU.Build.0 = Release|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Release|x64.ActiveCfg = Release|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Release|x64.Build.0 = Release|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Release|x86.ActiveCfg = Release|Any CPU
{C691BFCC-A6EF-4331-B7C4-E264441BEE0D}.Release|x86.Build.0 = Release|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Debug|x64.ActiveCfg = Debug|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Debug|x64.Build.0 = Debug|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Debug|x86.ActiveCfg = Debug|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Debug|x86.Build.0 = Debug|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Release|Any CPU.Build.0 = Release|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Release|x64.ActiveCfg = Release|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Release|x64.Build.0 = Release|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Release|x86.ActiveCfg = Release|Any CPU
{94F2F407-23DC-4945-AE67-B33C540DEF28}.Release|x86.Build.0 = Release|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Debug|x64.ActiveCfg = Debug|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Debug|x64.Build.0 = Debug|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Debug|x86.ActiveCfg = Debug|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Debug|x86.Build.0 = Debug|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Release|Any CPU.Build.0 = Release|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Release|x64.ActiveCfg = Release|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Release|x64.Build.0 = Release|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Release|x86.ActiveCfg = Release|Any CPU
{D0130B90-6ECA-49CA-A126-EFEFF481FC74}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{478CE71E-3653-8F33-D0F9-CF70B2F74279} = {B8CA8CAF-03B6-2A4B-B2EF-758B3B2ADED2}
{D0130B90-6ECA-49CA-A126-EFEFF481FC74} = {478CE71E-3653-8F33-D0F9-CF70B2F74279}
EndGlobalSection
EndGlobal