commit 85417acb41477aac8f84ebacfae967cc42ff9ba0 Author: Mike Phares Date: Sat Feb 3 17:30:01 2024 -0700 Ready to test diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..650e00d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,272 @@ +[*.md] +end_of_line = crlf +file_header_template = unset +indent_size = 2 +indent_style = space +insert_final_newline = false +root = true +tab_width = 2 +[*.csproj] +end_of_line = crlf +file_header_template = unset +indent_size = 2 +indent_style = space +insert_final_newline = false +root = true +tab_width = 2 +[*.cs] +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true +csharp_prefer_braces = false +csharp_prefer_qualified_reference = true:error +csharp_prefer_simple_default_expression = true:warning +csharp_prefer_simple_using_statement = true:warning +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true +csharp_style_conditional_delegate_call = true +csharp_style_deconstructed_variable_declaration = false +csharp_style_expression_bodied_accessors = when_on_single_line:warning +csharp_style_expression_bodied_constructors = when_on_single_line:warning +csharp_style_expression_bodied_indexers = when_on_single_line:warning +csharp_style_expression_bodied_lambdas = when_on_single_line:warning +csharp_style_expression_bodied_local_functions = when_on_single_line:warning +csharp_style_expression_bodied_methods = when_on_single_line:warning +csharp_style_expression_bodied_operators = when_on_single_line:warning +csharp_style_expression_bodied_properties = when_on_single_line:warning +csharp_style_implicit_object_creation_when_type_is_apparent = true:warning +csharp_style_inlined_variable_declaration = false +csharp_style_namespace_declarations = file_scoped:warning +csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_style_pattern_matching_over_as_with_null_check = true:warning +csharp_style_pattern_matching_over_is_with_cast_check = true:warning +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_not_pattern = true:warning +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_pattern_matching = true:warning +csharp_style_prefer_range_operator = true:warning +csharp_style_prefer_switch_expression = true:warning +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable:warning +csharp_style_unused_value_expression_statement_preference = discard_variable:warning +csharp_style_var_elsewhere = false:warning +csharp_style_var_for_built_in_types = false:warning +csharp_style_var_when_type_is_apparent = false:warning +csharp_using_directive_placement = outside_namespace +dotnet_code_quality_unused_parameters = all +dotnet_code_quality_unused_parameters = non_public # IDE0060: Remove unused parameter +dotnet_code_quality.CAXXXX.api_surface = private, internal +dotnet_diagnostic.CA1825.severity = warning # CA1823: Avoid zero-length array allocations +dotnet_diagnostic.CA1829.severity = warning # CA1829: Use Length/Count property instead of Count() when available +dotnet_diagnostic.CA1834.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable +dotnet_diagnostic.CA1860.severity = error # CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance +dotnet_diagnostic.CA1869.severity = none # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead. +dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])' +dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name +dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2"); +dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant. +dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary +dotnet_diagnostic.IDE0028.severity = error # IDE0028: Collection initialization can be simplified +dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031) +dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed +dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references (IDE0049) +dotnet_diagnostic.IDE0060.severity = warning # IDE0060: Remove unused parameter +dotnet_diagnostic.IDE0270.severity = warning # IDE0270: Null check can be simplified +dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290) +dotnet_diagnostic.IDE0300.severity = error # IDE0300: Collection initialization can be simplified +dotnet_diagnostic.IDE0301.severity = error #IDE0301: Collection initialization can be simplified +dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified +dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning +dotnet_naming_rule.abstract_method_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.abstract_method_should_be_pascal_case.symbols = abstract_method +dotnet_naming_rule.class_should_be_pascal_case.severity = warning +dotnet_naming_rule.class_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.class_should_be_pascal_case.symbols = class +dotnet_naming_rule.delegate_should_be_pascal_case.severity = warning +dotnet_naming_rule.delegate_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.delegate_should_be_pascal_case.symbols = delegate +dotnet_naming_rule.enum_should_be_pascal_case.severity = warning +dotnet_naming_rule.enum_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.enum_should_be_pascal_case.symbols = enum +dotnet_naming_rule.event_should_be_pascal_case.severity = warning +dotnet_naming_rule.event_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.event_should_be_pascal_case.symbols = event +dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.method_should_be_pascal_case.severity = warning +dotnet_naming_rule.method_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.method_should_be_pascal_case.symbols = method +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.private_method_should_be_pascal_case.severity = warning +dotnet_naming_rule.private_method_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.private_method_should_be_pascal_case.symbols = private_method +dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.severity = warning +dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.style = private_of_internal_field +dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.severity = warning +dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.style = private_of_internal_field +dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.symbols = private_or_internal_static_field +dotnet_naming_rule.property_should_be_pascal_case.severity = warning +dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.property_should_be_pascal_case.symbols = property +dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.severity = warning +dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.style = private_of_internal_field +dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.symbols = public_or_protected_field +dotnet_naming_rule.static_field_should_be_pascal_case.severity = warning +dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field +dotnet_naming_rule.static_method_should_be_pascal_case.severity = warning +dotnet_naming_rule.static_method_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.static_method_should_be_pascal_case.symbols = static_method +dotnet_naming_rule.struct_should_be_pascal_case.severity = warning +dotnet_naming_rule.struct_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.struct_should_be_pascal_case.symbols = struct +dotnet_naming_rule.types_should_be_pascal_case.severity = warning +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_style.begins_with_i.capitalization = pascal_case +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.private_of_internal_field.capitalization = pascal_case +dotnet_naming_style.private_of_internal_field.required_prefix = _ +dotnet_naming_style.private_of_internal_field.required_suffix = +dotnet_naming_style.private_of_internal_field.word_separator = +dotnet_naming_symbols.abstract_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.abstract_method.applicable_kinds = method +dotnet_naming_symbols.abstract_method.required_modifiers = abstract +dotnet_naming_symbols.class.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.class.applicable_kinds = class +dotnet_naming_symbols.class.required_modifiers = +dotnet_naming_symbols.delegate.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.delegate.applicable_kinds = delegate +dotnet_naming_symbols.delegate.required_modifiers = +dotnet_naming_symbols.enum.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.enum.applicable_kinds = enum +dotnet_naming_symbols.enum.required_modifiers = +dotnet_naming_symbols.event.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.event.applicable_kinds = event +dotnet_naming_symbols.event.required_modifiers = +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.method.applicable_accessibilities = public +dotnet_naming_symbols.method.applicable_kinds = method +dotnet_naming_symbols.method.required_modifiers = +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.private_method.applicable_accessibilities = private +dotnet_naming_symbols.private_method.applicable_kinds = method +dotnet_naming_symbols.private_method.required_modifiers = +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.required_modifiers = +dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static +dotnet_naming_symbols.property.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.property.applicable_kinds = property +dotnet_naming_symbols.property.required_modifiers = +dotnet_naming_symbols.public_or_protected_field.applicable_accessibilities = public, protected +dotnet_naming_symbols.public_or_protected_field.applicable_kinds = field +dotnet_naming_symbols.public_or_protected_field.required_modifiers = +dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.static_field.applicable_kinds = field +dotnet_naming_symbols.static_field.required_modifiers = static +dotnet_naming_symbols.static_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.static_method.applicable_kinds = method +dotnet_naming_symbols.static_method.required_modifiers = static +dotnet_naming_symbols.struct.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.struct.applicable_kinds = struct +dotnet_naming_symbols.struct.required_modifiers = +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.required_modifiers = +dotnet_remove_unnecessary_suppression_exclusions = 0 +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +dotnet_style_allow_multiple_blank_lines_experimental = false:warning +dotnet_style_allow_statement_immediately_after_block_experimental = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true:warning +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true:warning +dotnet_style_object_initializer = true:warning +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true:warning +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = false +dotnet_style_prefer_conditional_expression_over_return = false +dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_simplified_boolean_expressions = true:warning +dotnet_style_prefer_simplified_interpolation = true +dotnet_style_qualification_for_event = false:error +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false:error +dotnet_style_qualification_for_property = false:error +dotnet_style_readonly_field = true:warning +dotnet_style_require_accessibility_modifiers = for_non_interface_members +end_of_line = crlf +file_header_template = unset +indent_size = 4 +indent_style = space +insert_final_newline = false +root = true +tab_width = 4 +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822 +# https://github.com/dotnet/aspnetcore/blob/main/.editorconfig +# https://github.com/dotnet/project-system/blob/main/.editorconfig \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c04d1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,329 @@ +## 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/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ + +# 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 + +# 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/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.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 + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# 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 +# 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 + +# 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 + +# 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 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/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# 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/ \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..cbd1d3f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +/* + +!/.kanbn diff --git a/.vscode/format-report.json b/.vscode/format-report.json new file mode 100644 index 0000000..5626ddc --- /dev/null +++ b/.vscode/format-report.json @@ -0,0 +1,38 @@ +[ + { + "DocumentId": { + "ProjectId": { + "Id": "5fe69a98-912a-4333-9972-d2d28ed44de6" + }, + "Id": "3ad564ae-60db-4ab3-b80d-8719620c732d" + }, + "FileName": "HelperPhysicalAddress.cs", + "FilePath": "L:\\DevOps\\Mesa_FI\\Parsing-Packets\\Helpers\\HelperPhysicalAddress.cs", + "FileChanges": [ + { + "LineNumber": 1, + "CharNumber": 1, + "DiagnosticId": "WHITESPACE", + "FormatDescription": "Fix whitespace formatting." + } + ] + }, + { + "DocumentId": { + "ProjectId": { + "Id": "5fe69a98-912a-4333-9972-d2d28ed44de6" + }, + "Id": "2900c916-8ebb-46e5-8162-e9e461b1052b" + }, + "FileName": "HelperPhysicalAddressDictionarySourceGenerationContext.cs", + "FilePath": "L:\\DevOps\\Mesa_FI\\Parsing-Packets\\Models\\HelperPhysicalAddressDictionarySourceGenerationContext.cs", + "FileChanges": [ + { + "LineNumber": 9, + "CharNumber": 2, + "DiagnosticId": "FINALNEWLINE", + "FormatDescription": "Fix final newline. Delete 2 characters." + } + ] + } +] \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b0c6563 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/Parsing-Packets.dll", + "args": [ + "s" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..64c835c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,38 @@ +{ + "[markdown]": { + "editor.wordWrap": "off" + }, + "files.exclude": { + "**/.git": false, + "**/node_modules": true + }, + "files.watcherExclude": { + "**/node_modules": true + }, + "cSpell.words": [ + "ASPNETCORE", + "BIRT", + "CHIL", + "DEAT", + "endianness", + "FAMC", + "FAMS", + "GIVN", + "HUSB", + "INDI", + "Infineon", + "Kanban", + "kanbn", + "Kofax", + "NSFX", + "OBJE", + "onenote", + "Pcap", + "pged", + "Phares", + "Serilog", + "SUBM", + "SURN", + "SYSLIB" + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..82e74c3 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,81 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "User Secrets Init", + "command": "dotnet", + "type": "process", + "args": [ + "user-secrets", + "-p", + "${workspaceFolder}/Parsing-Packets.csproj", + "init" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "User Secrets Set", + "command": "dotnet", + "type": "process", + "args": [ + "user-secrets", + "-p", + "${workspaceFolder}/Parsing-Packets.csproj", + "set", + "asdf", + "6516d19d6569" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "Format", + "command": "dotnet", + "type": "process", + "args": [ + "format", + "--report", + ".vscode", + "--verbosity", + "detailed", + "--severity", + "warn" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Parsing-Packets.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "Publish AOT", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "-r", + "win-x64", + "-c", + "Release", + "-p:PublishAot=true", + "${workspaceFolder}/Parsing-Packets.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "Parsing-Packets AOT s V Helpers", + "type": "shell", + "command": "& L:/DevOps/Mesa_FI/Parsing-Packets/bin/Release/net8.0/win-x64/publish/Parsing-Packets.exe s V Helpers", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/Helpers/HelperPhysicalAddress.cs b/Helpers/HelperPhysicalAddress.cs new file mode 100644 index 0000000..3a326a0 --- /dev/null +++ b/Helpers/HelperPhysicalAddress.cs @@ -0,0 +1,178 @@ +using PacketDotNet; +using Parsing_Packets.Models; +using SharpPcap; +using System.Net.NetworkInformation; +using System.Text; +using System.Text.Json; + +namespace Parsing_Packets.Helpers; + +internal static class HelperPhysicalAddress +{ + + private static string GetPhysicalAddress(PhysicalAddress physicalAddress, char c) + { + StringBuilder results = new(); + string d = physicalAddress.ToString().ToLower(); + for (int i = 0; i < d.Length; i += 2) + _ = results.Append(d[i]).Append(d[i + 1]).Append(c); + results = results.Remove(results.Length - 1, 1); + return results.ToString(); + } + + private static string GetPhysicalAddress(PhysicalAddress physicalAddress) => + $"{GetPhysicalAddress(physicalAddress, '-')} | {GetPhysicalAddress(physicalAddress, ':')}"; + + private static void AddPacket(PhysicalAddressConfiguration physicalAddressConfiguration, ILogger logger, string file, string physicalAddress, string ipv4Address, bool isSuggestion) + { + List? collection; + Dictionary> keyValuePairs = GetKeyValuePairs(file); + if (!keyValuePairs.TryGetValue(physicalAddress, out collection)) + { + keyValuePairs.Add(physicalAddress, []); + if (!keyValuePairs.TryGetValue(physicalAddress, out collection)) + throw new Exception(); + } + if (ipv4Address.StartsWith(physicalAddressConfiguration.IPV4Filter) && (collection.Count < 2 || (!isSuggestion && collection[^1] != ipv4Address))) + { + logger.LogInformation(""); + if (collection.Count == 0) + collection.Add($"block-{physicalAddress[^2..]}"); + else + logger.LogInformation(collection[0]); + if (collection.Count < 2) + collection.Add(DateTime.Now.ToString("yyyy-MM-dd_HH-mm")); + else + { + logger.LogInformation(collection[1]); + _ = collection.Remove(ipv4Address); + } + collection.Add(ipv4Address); + logger.LogInformation(ipv4Address); + logger.LogInformation(physicalAddress); + string json = JsonSerializer.Serialize(keyValuePairs, HelperPhysicalAddressDictionarySourceGenerationContext.Default.DictionaryStringListString); + File.WriteAllText(file, json); + } + } + + private static void AddArpPacket(PhysicalAddressConfiguration physicalAddressConfiguration, ILogger logger, string file, EthernetPacket ethernetPacket, ArpPacket arpPacket) + { + string ipv4Address = arpPacket.SenderProtocolAddress.ToString(); + string physicalAddress = GetPhysicalAddress(ethernetPacket.SourceHardwareAddress); + AddPacket(physicalAddressConfiguration, logger, file, physicalAddress, ipv4Address, isSuggestion: false); + } + + private static void AddDhcpPacket(PhysicalAddressConfiguration physicalAddressConfiguration, ILogger logger, string file, DhcpV4Packet dhcpV4Packet) + { + string ipv4Address; + string physicalAddress; + if (dhcpV4Packet.MessageType is DhcpV4MessageType.Request or DhcpV4MessageType.Ack) + { + ipv4Address = dhcpV4Packet.ClientAddress.ToString(); + physicalAddress = GetPhysicalAddress(dhcpV4Packet.ClientHardwareAddress); + bool isSuggestion = dhcpV4Packet.MessageType is DhcpV4MessageType.Request; + AddPacket(physicalAddressConfiguration, logger, file, physicalAddress, ipv4Address, isSuggestion); + } + else + { + ipv4Address = dhcpV4Packet.ClientAddress.ToString(); + physicalAddress = GetPhysicalAddress(dhcpV4Packet.ClientHardwareAddress); + AddPacket(physicalAddressConfiguration, logger, file, physicalAddress, ipv4Address, isSuggestion: true); + } + } + + private static void AddIpv4Packet(PhysicalAddressConfiguration physicalAddressConfiguration, ILogger logger, string file, EthernetPacket ethernetPacket, IPv4Packet ipv4Packet) + { + string ipv4Address = ipv4Packet.SourceAddress.ToString(); + string physicalAddress = GetPhysicalAddress(ethernetPacket.SourceHardwareAddress); + AddPacket(physicalAddressConfiguration, logger, file, physicalAddress, ipv4Address, isSuggestion: true); + } + + private static void ParseEthernetPacket(PhysicalAddressConfiguration physicalAddressConfiguration, ILogger logger, string file, EthernetPacket ethernetPacket) + { + for (int i = 0; i < 1; i++) + { + if (physicalAddressConfiguration.UseARP && ethernetPacket?.PayloadPacket is ArpPacket arpPacket) + { + if (arpPacket.Operation != ArpOperation.Response) + continue; + AddArpPacket(physicalAddressConfiguration, logger, file, ethernetPacket, arpPacket); + } + else if (ethernetPacket?.PayloadPacket is IPv4Packet ipv4Packet) + { + if (ipv4Packet.SourceAddress.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) + continue; + if (!ipv4Packet.SourceAddress.ToString().StartsWith(physicalAddressConfiguration.IPV4Filter)) + continue; + if (ipv4Packet.PayloadPacket is UdpPacket udpPacket && udpPacket.PayloadPacket is DhcpV4Packet dhcpV4Packet) + AddDhcpPacket(physicalAddressConfiguration, logger, file, dhcpV4Packet); + else + AddIpv4Packet(physicalAddressConfiguration, logger, file, ethernetPacket, ipv4Packet); + } + } + } + + private static Dictionary> GetKeyValuePairs(string file) + { + Dictionary> keyValuePairs; + if (!File.Exists(file)) + keyValuePairs = []; + else + { + string json = File.ReadAllText(file); + keyValuePairs = JsonSerializer.Deserialize(json, HelperPhysicalAddressDictionarySourceGenerationContext.Default.DictionaryStringListString) ?? []; + } + return keyValuePairs; + } + + internal static bool ParsePackets(AppSettings appSettings, ILogger logger) + { + ILiveDevice? liveDevice = null; + Version version = Pcap.SharpPcapVersion; + CaptureDeviceList devices = CaptureDeviceList.Instance; + logger.LogInformation("PacketDotNet example using SharpPcap {version}", version); + if (devices.Count < 1) + logger.LogInformation("No devices were found on this machine"); + else + { + Packet packet; + RawCapture rawCapture; + GetPacketStatus status; + if (!Directory.Exists(appSettings.PhysicalAddressConfiguration.Directory)) + _ = Directory.CreateDirectory(appSettings.PhysicalAddressConfiguration.Directory); + string file = Path.Combine(appSettings.PhysicalAddressConfiguration.Directory, ".json"); + logger.LogInformation(""); + logger.LogInformation("The following devices are available on this machine:"); + logger.LogInformation("----------------------------------------------------"); + logger.LogInformation(""); + foreach (ILiveDevice? device in devices) + { + logger.LogInformation("{Name} {Description}", device.Name, device.Description); + if (device.Name != appSettings.PhysicalAddressConfiguration.DeviceName) + continue; + liveDevice = device; + } + if (liveDevice is null) + logger.LogInformation("No devices matched {DeviceName}", appSettings.PhysicalAddressConfiguration.DeviceName); + else + { + logger.LogInformation(""); + liveDevice.Open(DeviceModes.Promiscuous, appSettings.PhysicalAddressConfiguration.ReadTimeoutMilliseconds); + logger.LogInformation("-- Listening on {Name} {Description}", liveDevice.Name, liveDevice.Description); + while (true) + { + status = liveDevice.GetNextPacket(out PacketCapture e); + if (status != GetPacketStatus.PacketRead) + continue; + rawCapture = e.GetPacket(); + packet = Packet.ParsePacket(rawCapture.GetLinkLayers(), rawCapture.Data); + if (packet is not EthernetPacket ethernetPacket) + continue; + ParseEthernetPacket(appSettings.PhysicalAddressConfiguration, logger, file, ethernetPacket); + } + } + } + return true; + } + +} \ No newline at end of file diff --git a/Helpers/RawCaptureExtensions.cs b/Helpers/RawCaptureExtensions.cs new file mode 100644 index 0000000..307fd2d --- /dev/null +++ b/Helpers/RawCaptureExtensions.cs @@ -0,0 +1,20 @@ +using PacketDotNet; +using SharpPcap; +using System.Reflection; + +namespace Parsing_Packets.Helpers; + +public static class RawCaptureExtensions +{ + private static readonly MethodInfo? _GetLinkLayerType; + + static RawCaptureExtensions() + { + PropertyInfo? propertyInfo = typeof(RawCapture).GetProperty("LinkLayerType", BindingFlags.Public | BindingFlags.Instance); + _GetLinkLayerType = propertyInfo?.GetMethod; + } + + public static LinkLayers GetLinkLayers(this RawCapture rawCapture) => + (LinkLayers)(_GetLinkLayerType?.Invoke(rawCapture, null) ?? 0); + +} \ No newline at end of file diff --git a/Models/AppSettings.cs b/Models/AppSettings.cs new file mode 100644 index 0000000..459f13a --- /dev/null +++ b/Models/AppSettings.cs @@ -0,0 +1,26 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Parsing_Packets.Models; + +public record AppSettings(PhysicalAddressConfiguration PhysicalAddressConfiguration, + string BuildNumber, + string Company, + string GitCommitSeven, + string Helper, + int MillisecondsDelay) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(AppSettings))] +internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Models/Binder/.editorconfig b/Models/Binder/.editorconfig new file mode 100644 index 0000000..1c444cd --- /dev/null +++ b/Models/Binder/.editorconfig @@ -0,0 +1,2 @@ +[*.cs] +csharp_preserve_single_line_statements = true \ No newline at end of file diff --git a/Models/Binder/AppSettings.cs b/Models/Binder/AppSettings.cs new file mode 100644 index 0000000..c486dff --- /dev/null +++ b/Models/Binder/AppSettings.cs @@ -0,0 +1,82 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Parsing_Packets.Models.Binder; + +public class AppSettings +{ + + public string? BuildNumber { get; set; } + public string? Company { get; set; } + public string? GitCommitSeven { get; set; } + public string? Helper { get; set; } + public int? MillisecondsDelay { get; set; } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings); + return result; + } + + private static void PreVerify(IConfigurationRoot configurationRoot, AppSettings? appSettings) + { + if (appSettings?.BuildNumber is null) + { + foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) + { + if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) + continue; + if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) + continue; + if (!physicalFileProvider.Root.Contains("UserSecrets")) + continue; + throw new NotSupportedException(physicalFileProvider.Root); + } + throw new NotSupportedException("Not Found!"); + } + } + + private static void Verify(AppSettings _) + { + } + + private static Models.AppSettings Get(AppSettings? appSettings, + Models.PhysicalAddressConfiguration physicalAddressConfiguration) + { + Models.AppSettings result; + if (appSettings is null) throw new NullReferenceException(nameof(appSettings)); + if (appSettings.BuildNumber is null) throw new NullReferenceException(nameof(BuildNumber)); + if (appSettings.Company is null) throw new NullReferenceException(nameof(Company)); + if (appSettings.GitCommitSeven is null) throw new NullReferenceException(nameof(GitCommitSeven)); + if (appSettings.Helper is null) throw new NullReferenceException(nameof(Helper)); + if (appSettings.MillisecondsDelay is null) throw new NullReferenceException(nameof(MillisecondsDelay)); + Verify(appSettings); + result = new(physicalAddressConfiguration, + appSettings.BuildNumber, + appSettings.Company, + appSettings.GitCommitSeven, + appSettings.Helper, + appSettings.MillisecondsDelay.Value); + return result; + } + + public static Models.AppSettings Get(IConfigurationRoot configurationRoot, + Models.PhysicalAddressConfiguration physicalAddressConfiguration) + { + Models.AppSettings result; +#pragma warning disable IL3050, IL2026 + AppSettings? appSettings = configurationRoot.Get(); +#pragma warning restore IL3050, IL2026 + PreVerify(configurationRoot, appSettings); + result = Get(appSettings, + physicalAddressConfiguration); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(AppSettings))] +internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Models/Binder/PhysicalAddressConfiguration.cs b/Models/Binder/PhysicalAddressConfiguration.cs new file mode 100644 index 0000000..e6ce560 --- /dev/null +++ b/Models/Binder/PhysicalAddressConfiguration.cs @@ -0,0 +1,85 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Parsing_Packets.Models.Binder; + +public class PhysicalAddressConfiguration +{ + + public string? Description { get; set; } + public string? DeviceName { get; set; } + public string? Directory { get; set; } + public string? IPV4Filter { get; set; } + public int? ReadTimeoutMilliseconds { get; set; } + public string? StringOutputType { get; set; } + public bool? UseARP { get; set; } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, BinderPhysicalAddressConfigurationSourceGenerationContext.Default.PhysicalAddressConfiguration); + return result; + } + + private static void PreVerify(IConfigurationRoot configurationRoot, PhysicalAddressConfiguration? configuration) + { + if (configuration?.Description is null) + { + foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) + { + if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) + continue; + if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) + continue; + if (!physicalFileProvider.Root.Contains("UserSecrets")) + continue; + throw new NotSupportedException(physicalFileProvider.Root); + } + throw new NotSupportedException("Not Found!"); + } + } + + private static void Verify(PhysicalAddressConfiguration _) + { + } + + private static Models.PhysicalAddressConfiguration Get(PhysicalAddressConfiguration? configuration) + { + Models.PhysicalAddressConfiguration result; + if (configuration is null) throw new NullReferenceException(nameof(configuration)); + if (configuration.Description is null) throw new NullReferenceException(nameof(configuration.Description)); + if (configuration.DeviceName is null) throw new NullReferenceException(nameof(configuration.DeviceName)); + if (configuration.Directory is null) throw new NullReferenceException(nameof(configuration.Directory)); + if (configuration.IPV4Filter is null) throw new NullReferenceException(nameof(configuration.IPV4Filter)); + if (configuration.ReadTimeoutMilliseconds is null) throw new NullReferenceException(nameof(configuration.ReadTimeoutMilliseconds)); + if (configuration.StringOutputType is null) throw new NullReferenceException(nameof(configuration.StringOutputType)); + if (configuration.UseARP is null) throw new NullReferenceException(nameof(configuration.UseARP)); + Verify(configuration); + result = new(configuration.Description, + configuration.DeviceName, + configuration.Directory, + configuration.IPV4Filter, + configuration.ReadTimeoutMilliseconds.Value, + configuration.StringOutputType, + configuration.UseARP.Value); + return result; + } + + public static Models.PhysicalAddressConfiguration Get(IConfigurationRoot configurationRoot) + { + Models.PhysicalAddressConfiguration result; + IConfigurationSection configurationSection = configurationRoot.GetSection(nameof(Models.PhysicalAddressConfiguration)); +#pragma warning disable IL3050, IL2026 + PhysicalAddressConfiguration? configuration = configurationSection.Get(); +#pragma warning restore IL3050, IL2026 + PreVerify(configurationRoot, configuration); + result = Get(configuration); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(PhysicalAddressConfiguration))] +internal partial class BinderPhysicalAddressConfigurationSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Models/HelperPhysicalAddressDictionarySourceGenerationContext.cs b/Models/HelperPhysicalAddressDictionarySourceGenerationContext.cs new file mode 100644 index 0000000..fd9c1c4 --- /dev/null +++ b/Models/HelperPhysicalAddressDictionarySourceGenerationContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Parsing_Packets.Models; + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(Dictionary>))] +internal partial class HelperPhysicalAddressDictionarySourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Models/PhysicalAddressConfiguration.cs b/Models/PhysicalAddressConfiguration.cs new file mode 100644 index 0000000..b2c255e --- /dev/null +++ b/Models/PhysicalAddressConfiguration.cs @@ -0,0 +1,27 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Parsing_Packets.Models; + +public record PhysicalAddressConfiguration(string Description, + string DeviceName, + string Directory, + string IPV4Filter, + int ReadTimeoutMilliseconds, + string StringOutputType, + bool UseARP) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, PhysicalAddressConfigurationSourceGenerationContext.Default.PhysicalAddressConfiguration); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(PhysicalAddressConfiguration))] +internal partial class PhysicalAddressConfigurationSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Parsing-Packets.csproj b/Parsing-Packets.csproj new file mode 100644 index 0000000..8f2af7c --- /dev/null +++ b/Parsing-Packets.csproj @@ -0,0 +1,21 @@ + + + enable + enable + Exe + win-x64 + net8.0 + 4cfc0085-f5aa-4815-bae9-05ca5ed37b41 + + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..2492c62 --- /dev/null +++ b/Program.cs @@ -0,0 +1,60 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting.WindowsServices; +using Parsing_Packets.Models; + +namespace Parsing_Packets; + +public class Program +{ + + internal static async Task Main(string[] args) + { + ILogger? logger = null; +#pragma warning disable IL3050 + WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args); +#pragma warning restore IL3050 + _ = webApplicationBuilder.Configuration.AddUserSecrets(); + PhysicalAddressConfiguration physicalAddressConfiguration = Models.Binder.PhysicalAddressConfiguration.Get(webApplicationBuilder.Configuration); + AppSettings appSettings = Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration, + physicalAddressConfiguration); + if (string.IsNullOrEmpty(appSettings.Company)) + throw new Exception("Company name must have a value!"); + try + { + List collection = []; + _ = webApplicationBuilder.Services.AddHostedService(); + _ = webApplicationBuilder.Services.AddSingleton(collection); + _ = webApplicationBuilder.Services.AddSingleton(appSettings); + if (WindowsServiceHelpers.IsWindowsService()) + { + collection.Add(nameof(WindowsServiceLifetime)); + _ = webApplicationBuilder.Services.AddSingleton(); + _ = webApplicationBuilder.Logging.AddEventLog(settings => + { +#pragma warning disable CA1416 + if (string.IsNullOrEmpty(settings.SourceName)) + settings.SourceName = webApplicationBuilder.Environment.ApplicationName; +#pragma warning restore + }); + } + using WebApplication webApplication = webApplicationBuilder.Build(); + logger = webApplication.Services.GetRequiredService>(); + if (string.IsNullOrEmpty(appSettings.Company)) + { + Environment.ExitCode = -1; + _ = webApplication.StopAsync(); + } + logger.LogInformation("Starting Web Application"); + logger.LogCritical("{Company}", appSettings.Company); + await webApplication.RunAsync(); + } + catch (Exception ex) + { + try + { logger?.LogCritical(ex, "WebApplication terminated unexpectedly"); } + catch (Exception) { } + throw; + } + } + +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e37e4b1 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Introduction +TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. + +# Getting Started +TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: +1. Installation process +2. Software dependencies +3. Latest releases +4. API references + +# Build and Test +TODO: Describe and show how to build your code and run the tests. + +# Contribute +TODO: Explain how other users and developers can contribute to make your code better. + +If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files: +- [ASP.NET Core](https://github.com/aspnet/Home) +- [Visual Studio Code](https://github.com/Microsoft/vscode) +- [Chakra Core](https://github.com/Microsoft/ChakraCore) \ No newline at end of file diff --git a/Worker.cs b/Worker.cs new file mode 100644 index 0000000..8519eed --- /dev/null +++ b/Worker.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Hosting.WindowsServices; +using Parsing_Packets.Models; +using System.Data; + +namespace Parsing_Packets; + +public partial class Worker : BackgroundService +{ + + private readonly bool _IsWindowsService; + private readonly ILogger _Logger; + private readonly AppSettings _AppSettings; + + public Worker(IServiceProvider serviceProvider, ILogger logger, AppSettings appSettings, List collection) + { + _Logger = logger; + _AppSettings = appSettings; + logger.LogInformation("{buildNumber}-{gitCommitSeven}", _AppSettings.BuildNumber, _AppSettings.GitCommitSeven); + try + { logger.LogInformation("<{folder}>", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); } + catch (Exception) { } + _IsWindowsService = collection.Contains(nameof(WindowsServiceLifetime)); + } + + private void Body() + { + _Logger.LogInformation("A) Next execute will be at {date}", DateTime.Now.AddMilliseconds(_AppSettings.MillisecondsDelay).ToString("yyyy-MM-dd hh:mm:ss.fff tt")); + _ = _AppSettings.Helper switch + { + nameof(Helpers.HelperPhysicalAddress) => Helpers.HelperPhysicalAddress.ParsePackets(_AppSettings, _Logger), + _ => throw new NotSupportedException() + }; + _Logger.LogInformation("B) Next execute will be at {date}", DateTime.Now.AddMilliseconds(_AppSettings.MillisecondsDelay).ToString("yyyy-MM-dd hh:mm:ss.fff tt")); + } + + private async Task Body(CancellationToken stoppingToken) + { + if (!_IsWindowsService) + throw new EvaluateException("Set break point and skip!"); + while (_IsWindowsService && !stoppingToken.IsCancellationRequested) + { + Body(); + await Task.Delay(_AppSettings.MillisecondsDelay, stoppingToken); + } + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) => + await Body(stoppingToken); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7f52f0d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,35 @@ +{ + "name": "Parsing-Packets", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "prettier": "3.0.0" + } + }, + "node_modules/prettier": { + "version": "3.0.0", + "resolved": "http://localhost:4873/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + } + }, + "dependencies": { + "prettier": { + "version": "3.0.0", + "resolved": "http://localhost:4873/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b7f882a --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "prettier.check": "prettier . --check", + "prettier.write": "prettier . --write", + "garbage-collect": "git gc" + }, + "devDependencies": { + "prettier": "3.0.0" + } +} \ No newline at end of file