added proj

This commit is contained in:
Jonas Hinterdorfer 2026-01-15 14:41:25 +01:00
commit d38f26291f
23 changed files with 1725 additions and 0 deletions

403
.gitignore vendored Normal file
View File

@ -0,0 +1,403 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# 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 Core
project.lock.json
project.fragment.lock.json
artifacts/
# 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
Thumbs.db
*.bak
.idea/

BIN
Actor.csv Normal file

Binary file not shown.
1 Id Name FirstSeen ActorTypeId
2 1 tasmota_PC 2022/11/30 21:25:44 1
3 2 tasmota_WZ 2022/11/30 21:29:34 1
4 3 tasmota_Surface 2022/12/01 08:40:07 1
5 4 tasmota_Server 2022/12/01 15:52:59 1
6 6 tasmota_SW1 2022/12/03 2
7 7 tasmota_SW2 2022/12/03 2
8 8 tasmota_SW3 2022/12/03 2
9 9 tasmota_Temp 2022/12/04 19:52:12 3

BIN
ActorType.csv Normal file

Binary file not shown.
1 Id Name
2 1 Gosund EP2
3 2 Tasmota SW
4 3 Tasmota Sensor

25
Iot.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Iot", "Iot\Iot.csproj", "{29858458-29BE-4432-BADD-3F00367A34D5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{29858458-29BE-4432-BADD-3F00367A34D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29858458-29BE-4432-BADD-3F00367A34D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29858458-29BE-4432-BADD-3F00367A34D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29858458-29BE-4432-BADD-3F00367A34D5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8E039742-FC97-46AF-9602-39FD513A6A88}
EndGlobalSection
EndGlobal

113
Iot.sln.DotSettings Normal file
View File

@ -0,0 +1,113 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=39FD3420_002D6140_002D4A24_002D82D3_002D2E9FB14B096E_002Fd_003AClientApp_002Fd_003Adist/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D2B2F798_002D28FC_002D4515_002D966F_002DB92C3D11D3F2_002Fd_003AClientApp_002Fd_003Adist/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/GeneratedFilesAndFolders/=39FD3420_002D6140_002D4A24_002D82D3_002D2E9FB14B096E_002Fd_003AClientApp_002Fd_003Adist/@EntryIndexedValue">39FD3420-6140-4A24-82D3-2E9FB14B096E/d:ClientApp/d:dist</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/GeneratedFilesAndFolders/=972C43C8_002D8BF9_002D41FA_002DBA3C_002D299AC44A6938_002Fd_003AMigrations/@EntryIndexedValue">972C43C8-8BF9-41FA-BA3C-299AC44A6938/d:Migrations</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/GeneratedFilesAndFolders/=BD772F21_002DD4E2_002D4577_002D9AF3_002DFBE59BB72C03_002Fd_003AMigrations/@EntryIndexedValue">BD772F21-D4E2-4577-9AF3-FBE59BB72C03/d:Migrations</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/GeneratedFilesAndFolders/=D2B2F798_002D28FC_002D4515_002D966F_002DB92C3D11D3F2_002Fd_003AClientApp_002Fd_003Adist/@EntryIndexedValue">D2B2F798-28FC-4515-966F-B92C3D11D3F2/d:ClientApp/d:dist</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedFileMasks/=clipper_002Ecs/@EntryIndexedValue">clipper.cs</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedFileMasks/=package_002Dlock_002Ejson/@EntryIndexedValue">package-lock.json</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InconsistentNaming/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnassignedGetOnlyAutoProperty/@EntryIndexedValue">SUGGESTION</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_COMMENTS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_DECLARATION_NAMES/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BETWEEN_USING_GROUPS/@EntryValue">1</s:Int64>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_ASSIGNMENTS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_COMMENTS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_FIELDS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_INVOCATIONS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_METHODS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_PARAMETERS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_PROPERTIES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_SWITCH_EXPRESSIONS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_SWITCH_SECTIONS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INT_ALIGN_VARIABLES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_ATTRIBUTE_ARRANGEMENT/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">250</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/HtmlFormatter/MaxIndentedTagSize/@EntryValue">20000</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/JavaScriptCodeFormatting/WRAP_LIMIT/@EntryValue">250</s:Int64>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/QualifiedUsingAtNestedScope/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CRUD/@EntryIndexedValue">CRUD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/Abbreviations/=CNC/@EntryIndexedValue">CNC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCLASS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCONSTRUCTOR/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FGLOBAL_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLABEL/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLOCAL_005FCONSTRUCTOR/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLOCAL_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FOBJECT_005FPROPERTY_005FOF_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FPARAMETER/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FCLASS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM_005FMEMBER/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FINTERFACE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMIXED_005FENUM/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FEXPORTED/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FLOCAL/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FMEMBER_005FACCESSOR/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FTYPE_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FTYPE_005FMETHOD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FMEMBER_005FACCESSOR/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FTYPE_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FTYPE_005FMETHOD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FMEMBER_005FACCESSOR/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FMETHOD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FALIAS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FPARAMETER/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FHTML_005FCONTROL/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FNAME/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FPREFIX/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/ExcludedFiles/FileMasksThirdParty/=node_005Fmodules_005C_002A_002A/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/ExcludedFiles/FileMasksThirdParty/=node_005Fmodules_005C_002A_002A/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/ExcludedFiles/FileMasksThirdParty/=node_005Fmodules_005C_002A_002A/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=Antivirus/@EntryIndexedValue">DO_NOTHING</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=AutoRecoverer/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=Format/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=Roslyn_002Dswea/@EntryIndexedValue">DO_NOTHING</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=ShowAnnotations/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=SolExp_002DTrack/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=StartPage_002DIsDownloadRefreshEnabled/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=StartPage_002DOnEnvironmentStatup/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=SyncSettings/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=TextEditor_002DCodeLens/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=TextEditor_002DTrackChanges_002D2/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=VCS/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=VsBulb/@EntryIndexedValue">DO_NOTHING</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=XAML_0020Designer/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002EFunctionReturnStyleSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Aitenbichler/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Arduino/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Cambam/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=HPGL/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NONINFRINGEMENT/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

11
Iot/CsvEntities/Actor.cs Normal file
View File

@ -0,0 +1,11 @@
namespace Iot.CsvEntities;
using System;
public class Actor
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime FirstSeen { get; set; }
public int ActorTypeId { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Iot.CsvEntities;
public class ActorType
{
public int Id { get; set; }
public string Name { get; set; }
}

View File

@ -0,0 +1,12 @@
namespace Iot.CsvEntities;
using System;
public class Measurement
{
public int Id { get; set; }
public DateTime Time { get; set; }
public int ActorId { get; set; }
public int MeasurementTypeId { get; set; }
public double Value { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace Iot.CsvEntities;
public class MeasurementType
{
public int Id { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}

16
Iot/Entities/Actor.cs Normal file
View File

@ -0,0 +1,16 @@
namespace Iot.Entities;
using System;
using System.Collections.Generic;
public class Actor
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime FirstSeen { get; set; }
public int ActorTypeId { get; set; }
// Navigation Properties
public ActorType ActorType { get; set; }
public List<Measurement> Measurements { get; set; } = new List<Measurement>();
}

12
Iot/Entities/ActorType.cs Normal file
View File

@ -0,0 +1,12 @@
namespace Iot.Entities;
using System.Collections.Generic;
public class ActorType
{
public int Id { get; set; }
public string Name { get; set; }
// Navigation Property
public List<Actor> Actors { get; set; } = new List<Actor>();
}

45
Iot/Entities/Entities.cd Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="Iot.Entities.Actor">
<Position X="8.5" Y="3" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAEAAAACAQAAAAAAAAAAAAQAAAAAAQ=</HashCode>
<FileName>Entities\Actor.cs</FileName>
</TypeIdentifier>
<ShowAsCollectionAssociation>
<Property Name="Measurements" />
</ShowAsCollectionAssociation>
</Class>
<Class Name="Iot.Entities.ActorType">
<Position X="8.5" Y="0.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Entities\ActorType.cs</FileName>
</TypeIdentifier>
<ShowAsCollectionAssociation>
<Property Name="Actors" />
</ShowAsCollectionAssociation>
</Class>
<Class Name="Iot.Entities.Measurement">
<Position X="5.75" Y="3.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAgAAAAAAAAAAIAAAAIAAAAAAgAQAAAQA=</HashCode>
<FileName>Entities\Measurement.cs</FileName>
</TypeIdentifier>
<ShowAsAssociation>
<Property Name="MeasurementType" />
<Property Name="Actor" />
</ShowAsAssociation>
</Class>
<Class Name="Iot.Entities.MeasurementType">
<Position X="5.75" Y="0.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAQAAAAAAA=</HashCode>
<FileName>Entities\MeasurementType.cs</FileName>
</TypeIdentifier>
<ShowAsCollectionAssociation>
<Property Name="Measurements" />
</ShowAsCollectionAssociation>
</Class>
<Font Name="Segoe UI" Size="9" />
</ClassDiagram>

View File

@ -0,0 +1,16 @@
namespace Iot.Entities;
using System;
public class Measurement
{
public int Id { get; set; }
public DateTime Time { get; set; }
public int ActorId { get; set; }
public int MeasurementTypeId { get; set; }
public double Value { get; set; }
// Navigation Properties
public Actor Actor { get; set; }
public MeasurementType MeasurementType { get; set; }
}

View File

@ -0,0 +1,13 @@
namespace Iot.Entities;
using System.Collections.Generic;
public class MeasurementType
{
public int Id { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
// Navigation Property
public List<Measurement> Measurements { get; set; } = new List<Measurement>();
}

23
Iot/Iot.csproj Normal file
View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Content Include="..\Actor.csv" Link="Actor.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ActorType.csv" Link="ActorType.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\Measurement.csv" Link="Measurement.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\MeasurementType.csv" Link="MeasurementType.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

195
Iot/Program.cs Normal file
View File

@ -0,0 +1,195 @@
/*--------------------------------------------------------------
* HTBLA-Leonding / Class: 1xHIF
*--------------------------------------------------------------
* Musterlösung-HA
*--------------------------------------------------------------
* Description: Iot
*--------------------------------------------------------------
*/
using System;
using System.Linq;
using System.Text;
using Iot.Tools;
Console.WriteLine("Iot");
Console.WriteLine("=====================");
#region Import Csvs
string fileNameActor = "Actor.csv";
string fileNameActorType = "ActorType.csv";
string fileNameMeasurement = "Measurement.csv";
string fileNameMeasurementType = "MeasurementType.csv";
var actorsCsv = new CsvImport<Iot.CsvEntities.Actor>()
{
DateFormat = "yyyy/MM/dd",
TimeFormat = "HH:mm:ss",
Encoding = Encoding.UTF8
}.Read(fileNameActor);
var actorsTypeCsv = new CsvImport<Iot.CsvEntities.ActorType>()
{
DateFormat = "yyyy/MM/dd",
TimeFormat = "HH:mm:ss",
Encoding = Encoding.UTF8
}.Read(fileNameActorType);
var measurementsCsv = new CsvImport<Iot.CsvEntities.Measurement>()
{
DateFormat = "yyyy/MM/dd",
TimeFormat = "HH:mm:ss",
Encoding = Encoding.UTF8
}.Read(fileNameMeasurement);
var measurementTypesCsv = new CsvImport<Iot.CsvEntities.MeasurementType>()
{
DateFormat = "yyyy/MM/dd",
TimeFormat = "HH:mm:ss",
Encoding = Encoding.UTF8
}.Read(fileNameMeasurementType);
#endregion
#region Convert to Entities (with navigation properties)
var actorTypes = actorsTypeCsv.Select(at => new Iot.Entities.ActorType
{
Id = at.Id,
Name = at.Name
}).ToDictionary(x => x.Id, x => x);
var actors = actorsCsv.Select(a => new Iot.Entities.Actor
{
Id = a.Id,
Name = a.Name,
FirstSeen = a.FirstSeen,
ActorTypeId = a.ActorTypeId,
ActorType = actorTypes[a.ActorTypeId]
}).ToList();
var measurementTypes = measurementTypesCsv.Select(mt => new Iot.Entities.MeasurementType
{
Id = mt.Id,
Name = mt.Name,
Comment = mt.Comment
}).ToList();
var measurementTypeDict = measurementTypes.ToDictionary(x => x.Id);
var actorDict = actors.ToDictionary(a => a.Id);
var measurements = measurementsCsv.Select(m =>
{
actorDict.TryGetValue(m.ActorId, out var actor);
measurementTypeDict.TryGetValue(m.MeasurementTypeId, out var measurementType);
return new Iot.Entities.Measurement
{
Id = m.Id,
Time = m.Time,
ActorId = m.ActorId,
MeasurementTypeId = m.MeasurementTypeId,
Value = m.Value,
Actor = actor,
MeasurementType = measurementType
};
}).ToList();
// Populate Actor.Measurements using ToLookup (O(n) instead of O(n*m))
var measurementsByActor = measurements.ToLookup(m => m.ActorId);
foreach (var actor in actors)
{
actor.Measurements = measurementsByActor[actor.Id].ToList();
}
// Populate MeasurementType.Measurements using ToLookup (O(n) instead of O(n*m))
var measurementsByType = measurements.ToLookup(m => m.MeasurementTypeId);
foreach (var measurementType in measurementTypes)
{
measurementType.Measurements = measurementsByType[measurementType.Id].ToList();
}
// Populate ActorType.Actors using ToLookup (O(n) instead of O(n*m))
var actorsByType = actors.ToLookup(a => a.ActorTypeId);
foreach (var actorType in actorTypes.Values)
{
actorType.Actors = actorsByType[actorType.Id].ToList();
}
#endregion
#region some Reports
Console.WriteLine("List of Actors");
Console.WriteLine("Id Name Count");
Console.WriteLine("==========================");
var actorReport = actors
.Select(a => new
{
a.Id,
a.Name,
Count = a.Measurements.Count
})
.OrderBy(a => a.Id);
foreach (var item in actorReport)
{
Console.WriteLine($"{item.Id,2} {item.Name,-17} {item.Count,5}");
}
Console.WriteLine();
Console.WriteLine("List of Measurement-Types");
Console.WriteLine("Id Name Count");
Console.WriteLine("==========================");
var measurementTypeReport = measurementTypes
.Select(mt => new
{
mt.Id,
mt.Name,
Count = mt.Measurements.Count
})
.OrderBy(mt => mt.Id);
foreach (var item in measurementTypeReport)
{
Console.WriteLine($"{item.Id,2} {item.Name,-17} {item.Count,5}");
}
Console.WriteLine();
Console.WriteLine("List of Actor by Measurement-Types");
Console.WriteLine("Actor Measurement Count Min Max");
Console.WriteLine("==============================================================");
var actorMeasurementReport = measurements
.GroupBy(m => new
{
ActorName = m.Actor?.Name ?? "Unknown",
MeasurementName = m.MeasurementType?.Name ?? "Unknown"
})
.Select(g =>
{
var values = g.Select(m => m.Value).ToList();
return new
{
Actor = g.Key.ActorName,
Measurement = g.Key.MeasurementName,
Count = values.Count,
Min = values.Min(),
Max = values.Max()
};
})
.OrderBy(r => r.Actor)
.ThenBy(r => r.Measurement);
foreach (var item in actorMeasurementReport)
{
Console.WriteLine($"{item.Actor,-14} {item.Measurement,-18} {item.Count,5} {item.Min,10:F2} {item.Max,10:F2}");
}
#endregion

253
Iot/Tools/CsvImport.cs Normal file
View File

@ -0,0 +1,253 @@
/*
This file is part of https://github.com/aiten/Framework.
Copyright (c) Herbert Aitenbichler
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Iot.Tools
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
public class CsvImport<T> : CsvImportBase where T : new()
{
public class ColumnMapping
{
public string ColumnName { get; set; }
public PropertyInfo MapTo { get; set; }
public bool Ignore { get; set; }
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
public Func<string, object> GetValue { get; set; }
public Func<object, object> AdjustValue { get; set; }
public Action<T, string> SetValue { get; set; }
#pragma warning restore CS8632
public bool IsConfigured => Ignore || MapTo != null || SetValue != null;
public bool IsMapped => !Ignore && MapTo != null;
public bool IsSetValue => !Ignore && SetValue != null;
}
public ICollection<string> IgnoreColumns { get; set; }
public IDictionary<string, string> MapColumns { get; set; }
public IList<T> Read(string[] csvLines)
{
var lines = ReadStringMatrixFromCsv(csvLines, false);
return MapTo(lines);
}
public IList<T> Read(string fileName)
{
var lines = ReadStringMatrixFromCsv(fileName, false);
return MapTo(lines);
}
public async Task<IList<T>> ReadAsync(string fileName)
{
var lines = await ReadStringMatrixFromCsvAsync(fileName, false);
return MapTo(lines);
}
public IList<T> MapTo(IList<IList<string>> lines)
{
// first line is columnLineHeader!!!!
var mapping = GetPropertyMapping(lines[0]);
CheckPropertyMapping(mapping);
var list = new List<T>();
var first = true;
foreach (var line in lines)
{
if (first)
{
first = false;
}
else
{
list.Add(Map(line, mapping));
}
}
return list;
}
private void CheckPropertyMapping(ColumnMapping[] mapping)
{
var notConfigured = mapping.Where(m => !m.IsConfigured).ToList();
if (notConfigured.Any())
{
var columnList = string.Join(", ", notConfigured.Select(m => m.ColumnName));
throw new ArgumentException($"Column cannot be mapped: {columnList}");
}
var notCanWrite = mapping.Where(x => x.IsMapped && !x.MapTo.CanWrite).ToList();
if (notCanWrite.Any())
{
var columnList = string.Join(", ", notCanWrite.Select(m => m.ColumnName));
throw new ArgumentException($"Column is readonly: {columnList}");
}
}
protected virtual ColumnMapping[] GetPropertyMapping(IList<string> columnNames)
{
return columnNames
.Select(GetColumnMapping)
.ToArray();
}
public Action<ColumnMapping> ConfigureColumnMapping { get; set; }
protected virtual ColumnMapping GetColumnMapping(string columnName)
{
var ignoreColumn = IgnoreColumns?.Contains(columnName, StringComparer.InvariantCultureIgnoreCase) ?? false;
var mapToColumn = MapColumns?.ContainsKey(columnName) ?? false ? MapColumns[columnName] : columnName;
var columnMapping = new ColumnMapping
{
ColumnName = columnName,
Ignore = ignoreColumn,
MapTo = ignoreColumn ? null : GetPropertyInfo(mapToColumn),
};
ConfigureColumnMapping?.Invoke(columnMapping);
return columnMapping;
}
public static PropertyInfo GetPropertyInfo(string columnName)
{
return typeof(T).GetProperty(columnName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
private T Map(IList<string> line, ColumnMapping[] mapping)
{
var newT = new T();
var idx = 0;
foreach (var column in line)
{
AssignProperty(newT, column, mapping[idx++]);
}
return newT;
}
#pragma warning disable 8632
private object GetValue(string valueAsString, Type type)
#pragma warning restore 8632
{
if (type.IsGenericType && type.Name.StartsWith(@"Nullable"))
{
if (string.IsNullOrEmpty(valueAsString))
{
return null;
}
type = type.GenericTypeArguments[0];
}
if (type == typeof(string))
{
return ExcelString(valueAsString);
}
else if (type == typeof(int))
{
return ExcelInt(valueAsString);
}
else if (type == typeof(long))
{
return ExcelLong(valueAsString);
}
else if (type == typeof(short))
{
return ExcelShort(valueAsString);
}
else if (type == typeof(uint))
{
return ExcelUInt(valueAsString);
}
else if (type == typeof(ulong))
{
return ExcelULong(valueAsString);
}
else if (type == typeof(ushort))
{
return ExcelUShort(valueAsString);
}
else if (type == typeof(decimal))
{
return ExcelDecimal(valueAsString);
}
else if (type == typeof(byte))
{
return ExcelByte(valueAsString);
}
else if (type == typeof(bool))
{
return ExcelBool(valueAsString);
}
else if (type == typeof(DateTime))
{
return ExcelDateOrDateTime(valueAsString);
}
else if (type == typeof(TimeSpan))
{
return ExcelTimeSpan(valueAsString);
}
else if (type == typeof(double))
{
return ExcelDouble(valueAsString);
}
else if (type.IsEnum)
{
return ExcelEnum(type, valueAsString);
}
else if (type == typeof(byte[]))
{
return ExcelImage(valueAsString);
}
throw new NotImplementedException();
}
private void AssignProperty(object obj, string valueAsString, ColumnMapping mapping)
{
if (mapping.IsSetValue)
{
mapping.SetValue((T)obj, valueAsString);
}
else if (mapping.IsMapped)
{
var mapTo = mapping.MapTo;
#pragma warning disable 8632
object val = mapping.GetValue != null
? mapping.GetValue(valueAsString)
: GetValue(valueAsString, mapTo.PropertyType);
#pragma warning restore 8632
if (mapping.AdjustValue != null)
{
val = mapping.AdjustValue(val);
}
mapTo.SetValue(obj, val);
}
}
}
}

410
Iot/Tools/CsvImportBase.cs Normal file
View File

@ -0,0 +1,410 @@
/*
This file is part of https://github.com/aiten/Framework.
Copyright (c) Herbert Aitenbichler
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Iot.Tools
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
public class CsvImportBase
{
private readonly NumberFormatInfo _nfi;
public Encoding Encoding { get; set; } = Encoding.Default;
public string DateFormat { get; set; } = "yyyy/MM/dd";
public string TimeFormat { get; set; } = "HH:mm:ss";
public string Fraction3Format { get; set; } = ".fff";
public string Fraction5Format { get; set; } = ".fffff";
public string DateTimeFormat => GetDateTimeFormat(DateFormat, TimeFormat);
public string DateTimeFraction1Format => GetDateTimeFormat(DateFormat, TimeFormat, Fraction3Format);
public string DateTimeFraction5Format => GetDateTimeFormat(DateFormat, TimeFormat, Fraction5Format);
public string GetDateTimeFormat(string dateFormat, string timeFormat, string fractionFormat = null) => $"{dateFormat} {timeFormat}{fractionFormat ?? ""}";
public event EventHandler<IList<string>> ReadFirstLine;
public char ListSeparatorChar { get; set; } = ';';
public string NewLineInString { get; set; } = "\n";
public CsvImportBase()
{
// Retrieve a writable NumberFormatInfo object.
var enUS = CultureInfo.CreateSpecificCulture("en-US");
_nfi = enUS.NumberFormat;
}
public NumberFormatInfo NumberFormat => _nfi;
public void SetAustriaNumberFormat()
{
_nfi.NumberDecimalSeparator = ",";
_nfi.NumberGroupSeparator = ".";
}
#region read
public IList<IList<string>> ReadStringMatrixFromCsv(string[] lines, bool skipTitleLine)
{
var elements = new List<IList<string>>();
var lineIdx = 0;
var readLineIdx = 0;
var compareLineIdx = skipTitleLine ? 1 : 0;
while (true)
{
var row = ReadLine(() =>
{
if (readLineIdx >= lines.Length)
{
return null;
}
return lines[readLineIdx++];
});
if (row == null)
{
break;
}
if (lineIdx == 0)
{
ReadFirstLine?.Invoke(this, row);
}
if (lineIdx >= compareLineIdx)
{
elements.Add(row);
}
lineIdx++;
}
return elements;
}
public IList<IList<string>> ReadStringMatrixFromCsv(string fileName, bool skipTitleLine)
{
var lines = File.ReadAllLines(fileName, Encoding);
return ReadStringMatrixFromCsv(lines, skipTitleLine);
}
public async Task<IList<IList<string>>> ReadStringMatrixFromCsvAsync(string fileName, bool skipTitleLine)
{
var lines = await File.ReadAllLinesAsync(fileName, Encoding);
return ReadStringMatrixFromCsv(lines, skipTitleLine);
}
private IList<string> ReadLine(Func<string> getNextLine)
{
var line = getNextLine();
if (line == null)
{
return null;
}
var columns = new List<string>();
var sb = new StringBuilder(line.Length);
var noQuoteChar = '\0';
var quoteChar = noQuoteChar;
while (true)
{
for (var idx = 0; idx < line.Length; idx++)
{
var ch = line[idx];
if (ch == quoteChar)
{
// end of " or ""
if (idx + 1 < line.Length && line[idx + 1] == quoteChar)
{
idx++;
sb.Append(ch);
}
else
{
quoteChar = noQuoteChar;
}
}
else if (quoteChar == noQuoteChar && ch == '"')
{
quoteChar = ch;
}
else if (quoteChar == noQuoteChar && ch == ListSeparatorChar)
{
columns.Add(sb.ToString());
sb.Clear();
}
else if (quoteChar == noQuoteChar && ch == '|')
{
columns.Add(sb.ToString());
sb.Clear();
}
else
{
sb.Append(ch);
}
}
if (quoteChar == noQuoteChar)
{
break;
}
sb.Append(NewLineInString);
line = getNextLine();
}
columns.Add(sb.ToString());
return columns;
}
#endregion
#region convert
public string ExcelString(string excelField)
{
return excelField;
}
public short ExcelShort(string excelField)
{
return short.Parse(excelField);
}
public int ExcelInt(string excelField)
{
return int.Parse(excelField);
}
public long ExcelLong(string excelField)
{
return long.Parse(excelField);
}
public ushort ExcelUShort(string excelField)
{
return ushort.Parse(excelField);
}
public uint ExcelUInt(string excelField)
{
return uint.Parse(excelField);
}
public ulong ExcelULong(string excelField)
{
return ulong.Parse(excelField);
}
public float ExcelFloat(string excelField)
{
return float.Parse(excelField, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, _nfi);
}
public byte ExcelByte(string excelField)
{
return byte.Parse(excelField);
}
public bool ExcelBool(string excelField)
{
switch (excelField)
{
case @"0":
case @"false":
return false;
case @"1":
case @"true":
return true;
default:
throw new ArgumentOutOfRangeException(nameof(excelField), excelField, $@"cannot convert '{excelField}' to 'bool'.");
}
}
public decimal ExcelDecimal(string excelField)
{
return decimal.Parse(excelField, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, _nfi);
}
public double ExcelDouble(string excelField)
{
return double.Parse(excelField, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, _nfi);
}
public DateTime ExcelDateOrDateTime(string excelField)
{
if (excelField.Length > DateFormat.Length)
{
return ExcelDateTime(excelField, DateFormat, TimeFormat, Fraction3Format, Fraction3Format);
}
return ExcelDate(excelField, DateFormat);
}
public DateTime ExcelDate(string excelField, string format)
{
try
{
return DateTime.ParseExact(excelField, format, CultureInfo.InvariantCulture);
}
catch (Exception)
{
throw;
}
}
public DateTime ExcelDate(string excelField)
{
return ExcelDate(excelField, DateFormat);
}
public DateTime ExcelDateDMY(string excelField)
{
// Parse date and time with custom specifier.
// e.g. string dateString = "19.01.2018";
return ExcelDate(excelField, "dd.MM.yyyy");
}
public DateTime ExcelDateYMD(string excelField)
{
// Parse date and time with custom specifier.
// e.g. string dateString = "19.01.2018";
return ExcelDate(excelField, "yyyy/MM/dd");
}
public object ExcelTimeSpan(string excelField)
{
try
{
var timeSpan = TimeSpan.Parse(excelField);
return timeSpan;
}
catch (Exception e)
{
throw new Exception(e.StackTrace);
}
}
public DateTime ExcelDateTime(string excelField, string formatDate, string formatTime, string formatFraction3, string formatFraction5)
{
try
{
var fractionFormat = string.Empty;
var dotIdx = excelField.LastIndexOf('.');
if (dotIdx > formatDate.Length)
{
var fractionLength = excelField.Length - dotIdx - 1;
fractionFormat = fractionLength == 3 ? formatFraction3 : formatFraction5;
}
return DateTime.ParseExact(excelField, GetDateTimeFormat(formatDate, formatTime, fractionFormat), CultureInfo.InvariantCulture);
}
catch (Exception)
{
throw;
}
}
public DateTime ExcelDateTime(string excelField)
{
return ExcelDateTime(excelField, DateFormat, TimeFormat, Fraction3Format, Fraction5Format);
}
public DateTime ExcelDateTimeYMD(string excelField)
{
return ExcelDateTime(excelField, "yyyy/MM/dd", TimeFormat, Fraction3Format, Fraction5Format);
}
public object ExcelEnum(Type enumType, string excelField)
{
try
{
var enumValue = Enum.Parse(enumType, excelField);
return enumValue;
}
catch (Exception)
{
throw;
}
}
public byte[] ExcelImage(string excelField)
{
byte[] bytes;
if (excelField.StartsWith(@"0x"))
{
if (excelField.Length % 2 == 1)
{
throw new ArgumentException(@"string has odd length.", nameof(excelField));
}
int length = (excelField.Length - 2) / 2;
int chIdx = 2;
bytes = new byte[length];
for (int i = 0; i < length; i++)
{
bytes[i] = (byte)(ToHex(excelField[chIdx]) * 16 + ToHex(excelField[chIdx + 1]));
chIdx += 2;
}
}
else
{
bytes = Convert.FromBase64String(excelField);
}
return bytes;
}
private int ToHex(char ch)
{
if (ch >= '0' && ch <= '9')
{
return ch - '0';
}
if (ch >= 'a' && ch <= 'f')
{
return 10 + ch - 'a';
}
if (ch >= 'A' && ch <= 'F')
{
return 10 + ch - 'F';
}
throw new ArgumentException(nameof(ch), $@"'{ch}' is not a hex digit.");
}
#endregion
}
}

BIN
Measurement.csv Normal file

Binary file not shown.
Can't render this file because it is too large.

BIN
MeasurementType.csv Normal file

Binary file not shown.
1 Id Name Comment
2 1 Total
3 2 Today
4 3 Period
5 4 Power
6 5 ApparentPower
7 6 ReactivePower
8 7 Factor
9 8 Voltage
10 9 Current
11 10 Temperature
12 11 Humidity
13 12 DewPoint
14 13 Pressure
15 14 eCO2
16 15 TVOC
17 16 aHumidity

77
README.md Normal file
View File

@ -0,0 +1,77 @@
"# 10-Iot
## Lehrziele
- LINQ to Object
- Navigation Properties
- Wiederholung: Projektion, Join, GroupBy
## Aufgabenstellung
In einem IoT Projekt werden Messdaten gesammelt und in einer Datenbank abgespeichert. Von dieser Datenbank sind CSV Exportdateien vorhanden:
### Datenstruktur
#### ActorType.csv
Kategorisierung der Aktoren.
- Id: Eindeutige ID
- Name: Bezeichnung der Aktorkategorie
#### Actor.csv
Liste aller im System vorhandenen Aktoren.
- Id: Eindeutige ID
- Name: Bezeichnung des Aktors
- FirstSeen: Zeitpunkt der ersten Registrierung
- ActorTypeId: Beziehung zu den Aktoren-Kategorien
#### MeasurementType.csv
Liste aller Messwert-Kategorien.
- Id: Eindeutige ID
- Name: Bezeichnung (z.B. "Temperature")
- Comment: Kommentar/Beschreibung
#### Measurement.csv
Liste aller (historischen) Messwerte.
- Id: Eindeutige ID
- Time: Zeitpunkt der Messung
- ActorId: Zuordnung zu einem Aktor
- MeasurementTypeId: Zuordnung zu einer Messwert-Kategorie
- Value: Der gemessene Wert
## Implementierung
### Entitäten
Das Projekt enthält zwei Arten von Entitäten:
1. **CsvEntities**: Einfache DTOs für den CSV-Import (ohne Beziehungen)
2. **Entities**: Domain-Entitäten mit Navigation Properties
### Navigation Properties
Die Entities implementieren folgende Beziehungen:
- `ActorType``Actors` (1:n)
- `Actor``ActorType` (n:1)
- `Actor``Measurements` (1:n)
- `MeasurementType``Measurements` (1:n)
- `Measurement``Actor` (n:1)
- `Measurement``MeasurementType` (n:1)
### LINQ Queries
Das Programm demonstriert verschiedene LINQ-Operationen:
#### Report 1: Liste aller Aktoren mit Anzahl der Messungen
- **Verwendete LINQ-Operationen**: Select, Projection, OrderBy
#### Report 2: Liste aller Messwert-Typen mit Anzahl
- **Verwendete LINQ-Operationen**: Select, Projection, OrderBy
#### Report 3: Aktoren nach Messwert-Typen gruppiert mit Statistiken
- **Verwendete LINQ-Operationen**: GroupBy, Select, Projection, Min, Max, Count, OrderBy
- Zeigt für jede Kombination von Aktor und Messwert-Typ:
- Anzahl der Messungen
- Minimaler Wert
- Maximaler Wert
## Ausführung
```bash
dotnet run --project Iot/Iot.csproj
```
"

61
flake.lock generated Normal file
View File

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1768305791,
"narHash": "sha256-AIdl6WAn9aymeaH/NvBj0H9qM+XuAuYbGMZaP0zcXAQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1412caf7bf9e660f2f962917c14b1ea1c3bc695e",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

25
flake.nix Normal file
View File

@ -0,0 +1,25 @@
{
description = "Dev shell with .NET 9";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
};
in {
devShells.default = pkgs.mkShell {
packages = [
pkgs.dotnet-sdk_9
# alternativ/zusätzlich:
# pkgs.dotnetCorePackages.sdk_9_0
];
};
});
}