diff --git a/App/.editorconfig b/App/.editorconfig
new file mode 100644
index 0000000..4aaad81
--- /dev/null
+++ b/App/.editorconfig
@@ -0,0 +1,280 @@
+# editorconfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Default settings:
+# A newline ending every file
+# Use 4 spaces as indentation
+[*]
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+end_of_line = crlf
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_auto_properties = true:silent
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
+dotnet_style_namespace_match_folder = true:suggestion
+
+# C# files
+[*.cs]
+# New line preferences
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+# trim_trailing_whitespace = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = one_less_than_current
+
+# avoid this. unless absolutely necessary
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+
+# prefer var
+csharp_style_var_for_built_in_types = true
+csharp_style_var_when_type_is_apparent = true
+csharp_style_var_elsewhere = true:suggestion
+
+# use language keywords instead of BCL types
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+# name all constant fields using PascalCase
+dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
+
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.required_modifiers = const
+
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
+# private static fields should have s_ prefix
+dotnet_naming_rule.private_static_fields_should_have_prefix.severity = suggestion
+dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields
+dotnet_naming_rule.private_static_fields_should_have_prefix.style = private_static_prefix_style
+
+dotnet_naming_symbols.private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_fields.required_modifiers = static
+dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private
+
+dotnet_naming_style.private_static_prefix_style.required_prefix = s_
+dotnet_naming_style.private_static_prefix_style.capitalization = camel_case
+
+# internal and private fields should be _camelCase
+dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
+dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
+dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
+
+dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
+dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
+
+dotnet_naming_style.camel_case_underscore_style.required_prefix = _
+dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
+
+# use accessibility modifiers
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
+
+# Code style defaults
+dotnet_sort_system_directives_first = true
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = false
+
+# Expression-level preferences
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+
+# Expression-bodied members
+csharp_style_expression_bodied_methods = false:none
+csharp_style_expression_bodied_constructors = false:none
+csharp_style_expression_bodied_operators = false:none
+csharp_style_expression_bodied_properties = true:none
+csharp_style_expression_bodied_indexers = true:none
+csharp_style_expression_bodied_accessors = true:none
+
+# Pattern matching
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+
+# Null checking preferences
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Space preferences
+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
+space_within_single_line_array_initializer_braces = true
+
+#Net Analyzer
+dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed.
+
+# CS0649: Field 'field' is never assigned to, and will always have its default value 'value'
+dotnet_diagnostic.CS0649.severity = error
+
+# CS1591: Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1591.severity = suggestion
+
+# CS0162: Remove unreachable code
+dotnet_diagnostic.CS0162.severity = error
+# CA1018: Mark attributes with AttributeUsageAttribute
+dotnet_diagnostic.CA1018.severity = error
+# CA1304: Specify CultureInfo
+dotnet_diagnostic.CA1304.severity = warning
+# CA1802: Use literals where appropriate
+dotnet_diagnostic.CA1802.severity = warning
+# CA1813: Avoid unsealed attributes
+dotnet_diagnostic.CA1813.severity = error
+# CA1815: Override equals and operator equals on value types
+dotnet_diagnostic.CA1815.severity = warning
+# CA1820: Test for empty strings using string length
+dotnet_diagnostic.CA1820.severity = warning
+# CA1821: Remove empty finalizers
+dotnet_diagnostic.CA1821.severity = error
+# CA1822: Mark members as static
+dotnet_diagnostic.CA1822.severity = suggestion
+dotnet_code_quality.CA1822.api_surface = private, internal
+# CA1823: Avoid unused private fields
+dotnet_diagnostic.CA1823.severity = error
+# CA1825: Avoid zero-length array allocations
+dotnet_diagnostic.CA1825.severity = warning
+# CA1826: Use property instead of Linq Enumerable method
+dotnet_diagnostic.CA1826.severity = suggestion
+# CA1827: Do not use Count/LongCount when Any can be used
+dotnet_diagnostic.CA1827.severity = warning
+# CA1828: Do not use CountAsync/LongCountAsync when AnyAsync can be used
+dotnet_diagnostic.CA1828.severity = warning
+# CA1829: Use Length/Count property instead of Enumerable.Count method
+dotnet_diagnostic.CA1829.severity = warning
+#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
+dotnet_diagnostic.CA1847.severity = warning
+# CA1851: Possible multiple enumerations of IEnumerable collection
+dotnet_diagnostic.CA1851.severity = warning
+#CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method
+dotnet_diagnostic.CA1854.severity = warning
+#CA2211:Non-constant fields should not be visible
+dotnet_diagnostic.CA2211.severity = error
+
+# Wrapping preferences
+csharp_wrap_before_ternary_opsigns = false
+
+# Avalonia DevAnalyzer preferences
+dotnet_diagnostic.AVADEV2001.severity = error
+
+# Avalonia PublicAnalyzer preferences
+dotnet_diagnostic.AVP1000.severity = error
+dotnet_diagnostic.AVP1001.severity = error
+dotnet_diagnostic.AVP1002.severity = error
+dotnet_diagnostic.AVP1010.severity = error
+dotnet_diagnostic.AVP1011.severity = error
+dotnet_diagnostic.AVP1012.severity = warning
+dotnet_diagnostic.AVP1013.severity = error
+dotnet_diagnostic.AVP1020.severity = error
+dotnet_diagnostic.AVP1021.severity = error
+dotnet_diagnostic.AVP1022.severity = error
+dotnet_diagnostic.AVP1030.severity = error
+dotnet_diagnostic.AVP1031.severity = error
+dotnet_diagnostic.AVP1032.severity = error
+dotnet_diagnostic.AVP1040.severity = error
+dotnet_diagnostic.AVA2001.severity = error
+csharp_using_directive_placement = outside_namespace:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_prefer_braces = true:silent
+csharp_style_namespace_declarations = block_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_prefer_system_threading_lock = true:suggestion
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+
+# Xaml files
+[*.{xaml,axaml}]
+indent_size = 2
+# DuplicateSetterError
+avalonia_xaml_diagnostic.AVLN2203.severity = error
+# StyleInMergedDictionaries
+avalonia_xaml_diagnostic.AVLN2204.severity = error
+# RequiredTemplatePartMissing
+avalonia_xaml_diagnostic.AVLN2205.severity = error
+# OptionalTemplatePartMissing
+avalonia_xaml_diagnostic.AVLN2206.severity = info
+# TemplatePartWrongType
+avalonia_xaml_diagnostic.AVLN2207.severity = error
+# ItemContainerInsideTemplate
+avalonia_xaml_diagnostic.AVLN2208.severity = error
+# Obsolete
+avalonia_xaml_diagnostic.AVLN5001.severity = error
+
+# Xml project files
+[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
+indent_size = 2
+
+# Xml build files
+[*.builds]
+indent_size = 2
+
+# Xml files
+[*.{xml,stylecop,resx,ruleset}]
+indent_size = 2
+
+# Xml config files
+[*.{props,targets,config,nuspec}]
+indent_size = 2
+
+[*.json]
+indent_size = 2
+
+# Shell scripts
+[*.sh]
+end_of_line = lf
+[*.{cmd,bat}]
+end_of_line = crlf
diff --git a/App/.gitignore b/App/.gitignore
new file mode 100644
index 0000000..4dd3751
--- /dev/null
+++ b/App/.gitignore
@@ -0,0 +1,453 @@
+## 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
+*.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/
+
+# Tye
+.tye/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.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 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/
+
+# 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
+
+##
+## Visual studio for Mac
+##
+
+
+# globs
+Makefile.in
+*.userprefs
+*.usertasks
+config.make
+config.status
+aclocal.m4
+install-sh
+autom4te.cache/
+*.tar.gz
+tarballs/
+test-results/
+
+# Mac bundle stuff
+*.dmg
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+##
+## Visual Studio Code
+##
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
diff --git a/App/Directory.Build.props b/App/Directory.Build.props
new file mode 100644
index 0000000..4d9a552
--- /dev/null
+++ b/App/Directory.Build.props
@@ -0,0 +1,7 @@
+
+
+ enable
+ 11.2.6
+ 1.0.0
+
+
diff --git a/App/Harp.Behavior.App.sln b/App/Harp.Behavior.App.sln
new file mode 100644
index 0000000..174fcca
--- /dev/null
+++ b/App/Harp.Behavior.App.sln
@@ -0,0 +1,41 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.12.35707.178
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{D6F1C6E5-7507-425D-A697-29DAB58ED458}") = "Harp.Behavior.App", "Harp.Behavior.App\Harp.Behavior.App.csproj", "{6281A17E-B18E-4F00-86B9-BC1665108D9D}"
+EndProject
+Project("{D6F1C6E5-7507-425D-A697-29DAB58ED458}") = "Harp.Behavior.Design", "Harp.Behavior.Design\Harp.Behavior.Design.csproj", "{FCB73743-9ECF-4D3E-A235-F0493BC10629}"
+EndProject
+Project("{D6F1C6E5-7507-425D-A697-29DAB58ED458}") = "Harp.Behavior", "..\Interface\Harp.Behavior\Harp.Behavior.csproj", "{5C1A2A65-9C57-419A-89A0-0B59BA4CF40C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{10D72C30-D95E-452C-910F-F39FC6BAF129}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ Harp.Behavior.nsi = Harp.Behavior.nsi
+ README.md = README.md
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6281A17E-B18E-4F00-86B9-BC1665108D9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6281A17E-B18E-4F00-86B9-BC1665108D9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6281A17E-B18E-4F00-86B9-BC1665108D9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6281A17E-B18E-4F00-86B9-BC1665108D9D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FCB73743-9ECF-4D3E-A235-F0493BC10629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FCB73743-9ECF-4D3E-A235-F0493BC10629}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FCB73743-9ECF-4D3E-A235-F0493BC10629}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FCB73743-9ECF-4D3E-A235-F0493BC10629}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5C1A2A65-9C57-419A-89A0-0B59BA4CF40C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5C1A2A65-9C57-419A-89A0-0B59BA4CF40C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5C1A2A65-9C57-419A-89A0-0B59BA4CF40C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5C1A2A65-9C57-419A-89A0-0B59BA4CF40C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/App/Harp.Behavior.App/Harp.Behavior.App.csproj b/App/Harp.Behavior.App/Harp.Behavior.App.csproj
new file mode 100644
index 0000000..80940c0
--- /dev/null
+++ b/App/Harp.Behavior.App/Harp.Behavior.App.csproj
@@ -0,0 +1,51 @@
+
+
+ WinExe
+
+ net8.0
+ enable
+ true
+ app.manifest
+ ..\bin\$(Configuration)
+ $(AppVersion)
+ ..\Harp.Behavior.Design\Assets\cf-logo.ico
+ Harp.Behavior.App
+
+
+
+ Harp.Behavior.App
+ Harp.Behavior.App
+ org.fchampalimaud
+ $(Version)
+ $(Version)
+ AAPL
+ .
+ Harp.Behavior.App
+ cf-logo.icns
+ NSApplication
+ true
+ Champalimaud Foundation
+ ..\Harp.Behavior.Design\Assets\cf-logo.ico
+ ..\README.md
+ git
+ ..\bin\$(Configuration)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/Harp.Behavior.App/Program.cs b/App/Harp.Behavior.App/Program.cs
new file mode 100644
index 0000000..bc33a2b
--- /dev/null
+++ b/App/Harp.Behavior.App/Program.cs
@@ -0,0 +1,24 @@
+using System;
+
+using Avalonia;
+using Avalonia.ReactiveUI;
+
+namespace Harp.Behavior.App;
+
+class Program
+{
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace()
+ .UseReactiveUI();
+}
diff --git a/App/Harp.Behavior.App/Properties/launchSettings.json b/App/Harp.Behavior.App/Properties/launchSettings.json
new file mode 100644
index 0000000..8455e9d
--- /dev/null
+++ b/App/Harp.Behavior.App/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "Harp.Behavior.App": {
+ "commandName": "Project"
+ },
+ "WSL": {
+ "commandName": "WSL2",
+ "distributionName": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/App/Harp.Behavior.App/app.manifest b/App/Harp.Behavior.App/app.manifest
new file mode 100644
index 0000000..e0ce8d0
--- /dev/null
+++ b/App/Harp.Behavior.App/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/Harp.Behavior.Design/Adapters/RgbRegisterAdapter.cs b/App/Harp.Behavior.Design/Adapters/RgbRegisterAdapter.cs
new file mode 100644
index 0000000..dd8d639
--- /dev/null
+++ b/App/Harp.Behavior.Design/Adapters/RgbRegisterAdapter.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Reactive.Linq;
+using Avalonia.Media;
+using ReactiveUI;
+
+namespace Harp.Behavior.Design.Adapters;
+
+public class RgbColorItem : ReactiveObject
+{
+ private Color _color;
+ public Color Color
+ {
+ get => _color;
+ set => this.RaiseAndSetIfChanged(ref _color, value);
+ }
+}
+
+public enum RgbKind
+{
+ None,
+ Simple,
+ Array,
+ Structured,
+ Complex
+}
+
+public class RgbRegisterAdapter : ReactiveObject
+{
+ public ObservableCollection Colors { get; }
+ public bool IsWritable { get; }
+ public string RegisterKey { get; }
+ public RgbKind Kind { get; }
+
+ // For simple/structured: expose a single color property
+ public Color Color
+ {
+ get => Colors.Count > 0 ? Colors[0].Color : Avalonia.Media.Colors.Transparent;
+ set { if (Colors.Count > 0) Colors[0].Color = value; this.RaisePropertyChanged(nameof(Color)); }
+ }
+
+ private RgbRegisterAdapter _linkedAdapter;
+ private int _linkedOffset;
+
+ ///
+ /// Used for design-time data or default initialization.
+ ///
+ public RgbRegisterAdapter()
+ : this("DefaultKey", 1, true, RgbKind.Simple)
+ {
+ }
+
+ public RgbRegisterAdapter(string registerKey, int colorCount, bool isWritable, RgbKind kind)
+ {
+ RegisterKey = registerKey;
+ IsWritable = isWritable;
+ Kind = kind;
+ Colors = new ObservableCollection(
+ Enumerable.Range(0, colorCount).Select(_ => new RgbColorItem()));
+ }
+
+ ///
+ /// Updates the adapter from a register value (byte[] or struct).
+ ///
+ public void UpdateFromRegister(object registerValue)
+ {
+ if (registerValue is byte[] arr && arr.Length >= Colors.Count * 3)
+ {
+ for (int i = 0; i < Colors.Count; i++)
+ {
+ Colors[i].Color = Color.FromRgb(arr[i * 3], arr[i * 3 + 1], arr[i * 3 + 2]);
+ }
+ }
+ // Handle struct/complex types (e.g., with Red0, Green0, Blue0, Red1, ...)
+ else if (registerValue != null)
+ {
+ var type = registerValue.GetType();
+ for (int i = 0; i < Colors.Count; i++)
+ {
+ string rName = Colors.Count == 1 ? "Red" : $"Red{i}";
+ string gName = Colors.Count == 1 ? "Green" : $"Green{i}";
+ string bName = Colors.Count == 1 ? "Blue" : $"Blue{i}";
+
+ // FIXME: exchanges here to compensate the error in the firmware
+ var r = GetByteField(type, registerValue, rName);
+ var g = GetByteField(type, registerValue, gName);
+ var b = GetByteField(type, registerValue, bName);
+ Colors[i].Color = Color.FromRgb(r, g, b);
+ }
+ }
+ }
+
+ private static byte GetByteField(Type type, object obj, string name)
+ {
+ var prop = type.GetProperty(name);
+ if (prop != null && prop.PropertyType == typeof(byte))
+ return (byte)prop.GetValue(obj);
+ var field = type.GetField(name);
+ if (field != null && field.FieldType == typeof(byte))
+ return (byte)field.GetValue(obj);
+ return 0;
+ }
+
+ ///
+ /// Converts the adapter's colors to a register value (byte[]).
+ ///
+ public byte[] ToRegisterValue()
+ {
+ var arr = new byte[Colors.Count * 3];
+ for (int i = 0; i < Colors.Count; i++)
+ {
+ arr[i * 3] = Colors[i].Color.R;
+ arr[i * 3 + 1] = Colors[i].Color.G;
+ arr[i * 3 + 2] = Colors[i].Color.B;
+ }
+ return arr;
+ }
+
+ ///
+ /// Link this adapter to a parent adapter's color collection (for subrange sync).
+ ///
+ public void LinkToParent(RgbRegisterAdapter parent, int offset)
+ {
+ _linkedAdapter = parent;
+ _linkedOffset = offset;
+
+ // Sync initial values
+ for (int i = 0; i < Colors.Count; i++)
+ {
+ Colors[i].Color = parent.Colors[offset + i].Color;
+ }
+
+ // Subscribe to changes in this adapter and propagate to parent
+ for (int i = 0; i < Colors.Count; i++)
+ {
+ int idx = i;
+ Colors[i].WhenAnyValue(x => x.Color)
+ .Skip(1)
+ .Subscribe(color =>
+ {
+ parent.Colors[offset + idx].Color = color;
+ });
+
+ // Subscribe to changes in parent and propagate to this adapter
+ parent.Colors[offset + i].WhenAnyValue(x => x.Color)
+ .Skip(1)
+ .Subscribe(color =>
+ {
+ Colors[idx].Color = color;
+ });
+ }
+ }
+}
diff --git a/App/Harp.Behavior.Design/App.axaml b/App/Harp.Behavior.Design/App.axaml
new file mode 100644
index 0000000..6183319
--- /dev/null
+++ b/App/Harp.Behavior.Design/App.axaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/Harp.Behavior.Design/App.axaml.cs b/App/Harp.Behavior.Design/App.axaml.cs
new file mode 100644
index 0000000..073c400
--- /dev/null
+++ b/App/Harp.Behavior.Design/App.axaml.cs
@@ -0,0 +1,44 @@
+using System;
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+
+using Harp.Behavior.Design.ViewModels;
+using Harp.Behavior.Design.Views;
+
+namespace Harp.Behavior.Design;
+
+public partial class App : Application
+{
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = new BehaviorViewModel()
+ };
+ }
+ else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
+ {
+ singleViewPlatform.MainView = new BehaviorView
+ {
+ DataContext = new BehaviorViewModel()
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+
+ private void NativeMenuItem_OnClick(object sender, EventArgs e)
+ {
+ var about = new About() { DataContext = new AboutViewModel() };
+ about.ShowDialog((Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)
+ .MainWindow);
+ }
+}
diff --git a/App/Harp.Behavior.Design/Assets/cf-logo-small.bmp b/App/Harp.Behavior.Design/Assets/cf-logo-small.bmp
new file mode 100644
index 0000000..3d70185
Binary files /dev/null and b/App/Harp.Behavior.Design/Assets/cf-logo-small.bmp differ
diff --git a/App/Harp.Behavior.Design/Assets/cf-logo-white-lettering.png b/App/Harp.Behavior.Design/Assets/cf-logo-white-lettering.png
new file mode 100644
index 0000000..ca64284
Binary files /dev/null and b/App/Harp.Behavior.Design/Assets/cf-logo-white-lettering.png differ
diff --git a/App/Harp.Behavior.Design/Assets/cf-logo-white-lettering.svg b/App/Harp.Behavior.Design/Assets/cf-logo-white-lettering.svg
new file mode 100644
index 0000000..dc288cd
--- /dev/null
+++ b/App/Harp.Behavior.Design/Assets/cf-logo-white-lettering.svg
@@ -0,0 +1,224 @@
+
+
+
+
diff --git a/App/Harp.Behavior.Design/Assets/cf-logo.icns b/App/Harp.Behavior.Design/Assets/cf-logo.icns
new file mode 100644
index 0000000..d50056d
Binary files /dev/null and b/App/Harp.Behavior.Design/Assets/cf-logo.icns differ
diff --git a/App/Harp.Behavior.Design/Assets/cf-logo.ico b/App/Harp.Behavior.Design/Assets/cf-logo.ico
new file mode 100644
index 0000000..7f15636
Binary files /dev/null and b/App/Harp.Behavior.Design/Assets/cf-logo.ico differ
diff --git a/App/Harp.Behavior.Design/Assets/cf-logo.png b/App/Harp.Behavior.Design/Assets/cf-logo.png
new file mode 100644
index 0000000..007e30e
Binary files /dev/null and b/App/Harp.Behavior.Design/Assets/cf-logo.png differ
diff --git a/App/Harp.Behavior.Design/Assets/cf-logo.svg b/App/Harp.Behavior.Design/Assets/cf-logo.svg
new file mode 100644
index 0000000..08e8841
--- /dev/null
+++ b/App/Harp.Behavior.Design/Assets/cf-logo.svg
@@ -0,0 +1,235 @@
+
+
+
+
diff --git a/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo.png b/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo.png
new file mode 100644
index 0000000..f8afc50
Binary files /dev/null and b/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo.png differ
diff --git a/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo.svg b/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo.svg
new file mode 100644
index 0000000..1896536
--- /dev/null
+++ b/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo.svg
@@ -0,0 +1,94 @@
+
+
diff --git a/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo_white_lettering.png b/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo_white_lettering.png
new file mode 100644
index 0000000..e201b4b
Binary files /dev/null and b/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo_white_lettering.png differ
diff --git a/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo_white_lettering.svg b/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo_white_lettering.svg
new file mode 100644
index 0000000..c11b09a
--- /dev/null
+++ b/App/Harp.Behavior.Design/Assets/cf_hardware_software_logo_white_lettering.svg
@@ -0,0 +1,81 @@
+
+
+
+
diff --git a/App/Harp.Behavior.Design/Controls/ExtendedColorPicker.cs b/App/Harp.Behavior.Design/Controls/ExtendedColorPicker.cs
new file mode 100644
index 0000000..f91dd70
--- /dev/null
+++ b/App/Harp.Behavior.Design/Controls/ExtendedColorPicker.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Reflection;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
+
+namespace Harp.Behavior.Design.Controls;
+
+// NOTE: This is currently needed because there's an issue in the ColorPicker control where
+// the SelectedIndex is not respected on load.
+public class ExtendedColorPicker : ColorPicker
+{
+ protected override Type StyleKeyOverride => typeof(ColorPicker);
+
+ private int _selectedTabIndex;
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ // There might be a property set in xaml that get's overridden here
+ _selectedTabIndex = SelectedIndex;
+ base.OnApplyTemplate(e);
+ }
+
+ protected override void OnLoaded(RoutedEventArgs e)
+ {
+ base.OnLoaded(e);
+
+ var type = typeof(ColorView);
+ if (type.GetField("_tabControl", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(this) is not TabControl tabControl)
+ return;
+ tabControl.SelectedIndex = _selectedTabIndex;
+ }
+}
diff --git a/App/Harp.Behavior.Design/Controls/RgbRegisterControl.axaml b/App/Harp.Behavior.Design/Controls/RgbRegisterControl.axaml
new file mode 100644
index 0000000..871761f
--- /dev/null
+++ b/App/Harp.Behavior.Design/Controls/RgbRegisterControl.axaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/Harp.Behavior.Design/Controls/RgbRegisterControl.axaml.cs b/App/Harp.Behavior.Design/Controls/RgbRegisterControl.axaml.cs
new file mode 100644
index 0000000..829fd0f
--- /dev/null
+++ b/App/Harp.Behavior.Design/Controls/RgbRegisterControl.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace Harp.Behavior.Design.Controls;
+
+public partial class RgbRegisterControl : UserControl
+{
+ public RgbRegisterControl()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/App/Harp.Behavior.Design/Controls/VisualStatus.axaml b/App/Harp.Behavior.Design/Controls/VisualStatus.axaml
new file mode 100644
index 0000000..20d62e0
--- /dev/null
+++ b/App/Harp.Behavior.Design/Controls/VisualStatus.axaml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/App/Harp.Behavior.Design/Controls/VisualStatus.axaml.cs b/App/Harp.Behavior.Design/Controls/VisualStatus.axaml.cs
new file mode 100644
index 0000000..73eb13f
--- /dev/null
+++ b/App/Harp.Behavior.Design/Controls/VisualStatus.axaml.cs
@@ -0,0 +1,21 @@
+using Avalonia;
+using Avalonia.Controls;
+
+namespace Harp.Behavior.Design.Controls;
+
+public partial class VisualStatus : ContentControl
+{
+ public static readonly StyledProperty StatusProperty =
+ AvaloniaProperty.Register(nameof(Status), null);
+
+ public bool? Status
+ {
+ get => GetValue(StatusProperty);
+ set => SetValue(StatusProperty, value);
+ }
+
+ public VisualStatus()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/App/Harp.Behavior.Design/Controls/WriteMessagesControl.axaml b/App/Harp.Behavior.Design/Controls/WriteMessagesControl.axaml
new file mode 100644
index 0000000..d94cfd5
--- /dev/null
+++ b/App/Harp.Behavior.Design/Controls/WriteMessagesControl.axaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/App/Harp.Behavior.Design/Controls/WriteMessagesControl.axaml.cs b/App/Harp.Behavior.Design/Controls/WriteMessagesControl.axaml.cs
new file mode 100644
index 0000000..0885c8a
--- /dev/null
+++ b/App/Harp.Behavior.Design/Controls/WriteMessagesControl.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace Harp.Behavior.Design.Controls;
+
+public partial class WriteMessagesControl : UserControl
+{
+ public WriteMessagesControl()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/App/Harp.Behavior.Design/Converters/BooleanConverter.cs b/App/Harp.Behavior.Design/Converters/BooleanConverter.cs
new file mode 100644
index 0000000..1772b97
--- /dev/null
+++ b/App/Harp.Behavior.Design/Converters/BooleanConverter.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace Harp.Behavior.Design.Converters;
+
+
+
+
+public class BooleanConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool boolValue)
+ {
+ return boolValue ? "Output" : "Input";
+ }
+ return "Input"; // Default fallback
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string stringValue)
+ {
+ return stringValue == "Output";
+ }
+ return false; // Default fallback
+ }
+}
diff --git a/App/Harp.Behavior.Design/Converters/EnableFlagConverter.cs b/App/Harp.Behavior.Design/Converters/EnableFlagConverter.cs
new file mode 100644
index 0000000..6bc6b75
--- /dev/null
+++ b/App/Harp.Behavior.Design/Converters/EnableFlagConverter.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Bonsai.Harp;
+
+namespace Harp.Behavior.Design.Converters;
+
+///
+/// Converts between EnableFlag enum values and boolean values for two-way binding
+///
+public class EnableFlagConverter : IValueConverter
+{
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ // Convert from enum to bool (for IsChecked)
+ if (value == null)
+ return false;
+
+ return value.ToString().Contains("Enable");
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ // Convert from bool (IsChecked) to enum
+ if (value is not bool isChecked || parameter == null)
+ return null;
+
+ // Create the appropriate enum value based on the checkbox state
+ return isChecked ? EnableFlag.Enable : EnableFlag.Disable;
+ }
+}
diff --git a/App/Harp.Behavior.Design/Converters/EnumDisplayConverter.cs b/App/Harp.Behavior.Design/Converters/EnumDisplayConverter.cs
new file mode 100644
index 0000000..b6bb89f
--- /dev/null
+++ b/App/Harp.Behavior.Design/Converters/EnumDisplayConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace Harp.Behavior.Design.Converters;
+
+public class EnumDisplayConverter : IValueConverter
+{
+ public Dictionary Mappings { get; set; } = new Dictionary();
+
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value == null)
+ return null;
+ var key = value.ToString();
+ return Mappings!.GetValueOrDefault(key, key);
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return null;
+ }
+}
diff --git a/App/Harp.Behavior.Design/Converters/PayloadFieldConverter.cs b/App/Harp.Behavior.Design/Converters/PayloadFieldConverter.cs
new file mode 100644
index 0000000..a7d3106
--- /dev/null
+++ b/App/Harp.Behavior.Design/Converters/PayloadFieldConverter.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Globalization;
+using System.Reflection;
+using Avalonia.Data.Converters;
+
+namespace Harp.Behavior.Design.Converters;
+
+public class PayloadFieldConverter : IValueConverter
+{
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value == null || parameter == null)
+ return null;
+
+ var fieldName = parameter.ToString();
+ var valueType = value.GetType();
+
+ // Handle struct types directly with field/property access
+ if (valueType.IsValueType && !valueType.IsPrimitive)
+ {
+ // Try direct field access first
+ var fieldInfo = valueType.GetField(fieldName);
+ if (fieldInfo != null)
+ {
+ return fieldInfo.GetValue(value);
+ }
+
+ // Try property access second
+ var propInfo = valueType.GetProperty(fieldName);
+ if (propInfo != null)
+ {
+ return propInfo.GetValue(value);
+ }
+ }
+
+ // For primitive types, use bit masking
+ // This handles any register with a maskType like DigitalOutputSyncPayload
+ // that is actually a simple number but needs masking
+ try
+ {
+ int mask = GetMaskForField(valueType, fieldName);
+ if (mask != 0)
+ {
+ int rawValue = System.Convert.ToInt32(value);
+ int shift = GetShiftForMask(mask);
+ int maskedValue = (rawValue & mask) >> shift;
+
+ // If targetType is an enum, convert to that enum type
+ if (targetType.IsEnum)
+ {
+ return Enum.ToObject(targetType, maskedValue);
+ }
+ return maskedValue;
+ }
+ }
+ catch
+ {
+ // Silently continue if mask extraction fails
+ }
+
+ return value;
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value == null || parameter == null)
+ return null;
+
+ var fieldName = parameter.ToString();
+
+ // Handle struct types - boxed copy approach
+ if (targetType.IsValueType && !targetType.IsPrimitive)
+ {
+ // Create a copy of the current target value if it exists
+ object currentValue = Activator.CreateInstance(targetType);
+
+ // Set the field/property on the copy
+ var fieldInfo = targetType.GetField(fieldName);
+ if (fieldInfo != null && currentValue != null)
+ {
+ object boxedCopy = currentValue;
+ fieldInfo.SetValue(boxedCopy, value);
+ return boxedCopy;
+ }
+
+ var propInfo = targetType.GetProperty(fieldName);
+ if (propInfo != null && propInfo.CanWrite && currentValue != null)
+ {
+ object boxedCopy = currentValue;
+ propInfo.SetValue(boxedCopy, value);
+ return boxedCopy;
+ }
+ }
+
+ // For primitive/enum types with bitmasks
+ try
+ {
+ int mask = GetMaskForField(targetType, fieldName);
+ if (mask != 0)
+ {
+ // Get the current value if available
+ int currentValue = 0;
+
+ // Extract the value from the selected enum
+ int newValue = System.Convert.ToInt32(value);
+ int shift = GetShiftForMask(mask);
+
+ // Apply the new value at the correct bit position
+ int result = (currentValue & ~mask) | ((newValue << shift) & mask);
+ return System.Convert.ChangeType(result, targetType);
+ }
+ }
+ catch
+ {
+ // Silently continue if mask extraction fails
+ }
+
+ // Default conversion
+ return value;
+ }
+
+ private int GetMaskForField(Type type, string fieldName)
+ {
+ // Try to find mask by reflection from payload specification
+ // This assumes there's a static class or field with mask information
+ try
+ {
+ Type payloadSpecType = Type.GetType($"{type.Namespace}.{type.Name}PayloadSpec");
+ if (payloadSpecType != null)
+ {
+ var maskField = payloadSpecType.GetField($"{fieldName}Mask", BindingFlags.Public | BindingFlags.Static);
+ if (maskField != null)
+ {
+ return (int)maskField.GetValue(null);
+ }
+ }
+ }
+ catch
+ {
+ // Ignore and use default
+ }
+ return 0;
+ }
+
+ private int GetShiftForMask(int mask)
+ {
+ int shift = 0;
+ while ((mask & 1) == 0 && shift < 32)
+ {
+ mask >>= 1;
+ shift++;
+ }
+ return shift;
+ }
+}
diff --git a/App/Harp.Behavior.Design/Converters/VisualStatusColorConverter.cs b/App/Harp.Behavior.Design/Converters/VisualStatusColorConverter.cs
new file mode 100644
index 0000000..1bfc2ac
--- /dev/null
+++ b/App/Harp.Behavior.Design/Converters/VisualStatusColorConverter.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+
+namespace Harp.Behavior.Design.Converters;
+
+public class VisualStatusColorConverter : IValueConverter
+{
+ public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ var status = value as bool?;
+ return status switch
+ {
+ true => new SolidColorBrush(Colors.Green),
+ false => new SolidColorBrush(Colors.Red),
+ _ => new SolidColorBrush(Colors.Gray)
+ };
+ }
+
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
+ throw new NotImplementedException();
+}
diff --git a/App/Harp.Behavior.Design/FodyWeavers.xml b/App/Harp.Behavior.Design/FodyWeavers.xml
new file mode 100644
index 0000000..63fc148
--- /dev/null
+++ b/App/Harp.Behavior.Design/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/App/Harp.Behavior.Design/Harp.Behavior.Design.csproj b/App/Harp.Behavior.Design/Harp.Behavior.Design.csproj
new file mode 100644
index 0000000..da952e3
--- /dev/null
+++ b/App/Harp.Behavior.Design/Harp.Behavior.Design.csproj
@@ -0,0 +1,29 @@
+
+
+ net8.0
+ enable
+ latest
+ true
+ ..\bin\$(Configuration)
+ $(AppVersion)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/Harp.Behavior.Design/Styles/DefaultStyles.axaml b/App/Harp.Behavior.Design/Styles/DefaultStyles.axaml
new file mode 100644
index 0000000..65d2028
--- /dev/null
+++ b/App/Harp.Behavior.Design/Styles/DefaultStyles.axaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/App/Harp.Behavior.Design/ViewModels/AboutViewModel.cs b/App/Harp.Behavior.Design/ViewModels/AboutViewModel.cs
new file mode 100644
index 0000000..dac2e56
--- /dev/null
+++ b/App/Harp.Behavior.Design/ViewModels/AboutViewModel.cs
@@ -0,0 +1,5 @@
+namespace Harp.Behavior.Design.ViewModels;
+
+internal class AboutViewModel : ViewModelBase
+{
+}
diff --git a/App/Harp.Behavior.Design/ViewModels/MyDeviceViewModel.cs b/App/Harp.Behavior.Design/ViewModels/MyDeviceViewModel.cs
new file mode 100644
index 0000000..c8e96ec
--- /dev/null
+++ b/App/Harp.Behavior.Design/ViewModels/MyDeviceViewModel.cs
@@ -0,0 +1,5790 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO.Ports;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Media;
+using Bonsai.Harp;
+using Harp.Behavior.Design.Adapters;
+using Harp.Behavior.Design.Views;
+using MsBox.Avalonia;
+using MsBox.Avalonia.Enums;
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+
+namespace Harp.Behavior.Design.ViewModels;
+
+
+public class BehaviorViewModel : ViewModelBase
+{
+ public string AppVersion { get; set; }
+ public ReactiveCommand LoadDeviceInformation { get; }
+
+ #region Connection Information
+
+ [Reactive] public ObservableCollection Ports { get; set; }
+ [Reactive] public string SelectedPort { get; set; }
+ [Reactive] public bool Connected { get; set; }
+ [Reactive] public string ConnectButtonText { get; set; } = "Connect";
+ public ReactiveCommand ConnectAndGetBaseInfoCommand { get; }
+
+ #endregion
+
+ #region Operations
+ [Reactive] public int SelectedTabIndex { get; set; } // Allow for the opening tab to configurable
+ #region Configuration Commands
+ public ReactiveCommand SaveConfigurationCommand { get; }
+ public ReactiveCommand ResetConfigurationCommand { get; }
+ public ReactiveCommand SaveDirectionConfigCommand { get; }
+ #endregion
+
+ #region RGB Configuration Commands
+ public ReactiveCommand Rgb0ApplyConfigurationCommand { get; }
+ public ReactiveCommand Rgb1ApplyConfigurationCommand { get; }
+ public ReactiveCommand SaveRgbConfigColorRgb0Command { get; }
+ public ReactiveCommand SaveRgbConfigColorRgb1Command { get; }
+ #endregion
+
+ #region LED Configuration Commands
+ public ReactiveCommand SaveLedConfigCurrentLed0Command { get; }
+ public ReactiveCommand SaveLedConfigCurrentLed1Command { get; }
+ #endregion
+
+ #region Encoder Configuration Commands
+ public ReactiveCommand EncoderApplyConfigurationCommand { get; }
+ #endregion
+
+ #region Serial/Timestamp Configuration Commands
+ public ReactiveCommand SerialTimestampApplyConfigurationCommand { get; }
+ #endregion
+
+ #region Digital Output (DO) Control Commands
+ public ReactiveCommand DO0SetCommand { get; }
+ public ReactiveCommand DO0ClearCommand { get; }
+ public ReactiveCommand DO1SetCommand { get; }
+ public ReactiveCommand DO1ClearCommand { get; }
+ public ReactiveCommand DO2SetCommand { get; }
+ public ReactiveCommand DO2ClearCommand { get; }
+ public ReactiveCommand DO3SetCommand { get; }
+ public ReactiveCommand DO3ClearCommand { get; }
+ #endregion
+
+ #region Port Control Commands
+ // DOPort Commands
+ public ReactiveCommand DOPort0SetCommand { get; }
+ public ReactiveCommand DOPort0ClearCommand { get; }
+ public ReactiveCommand DOPort1SetCommand { get; }
+ public ReactiveCommand DOPort1ClearCommand { get; }
+ public ReactiveCommand DOPort2SetCommand { get; }
+ public ReactiveCommand DOPort2ClearCommand { get; }
+
+ // DIOPort Commands
+ public ReactiveCommand DIOPort0SetCommand { get; }
+ public ReactiveCommand DIOPort0ClearCommand { get; }
+ public ReactiveCommand DIOPort1SetCommand { get; }
+ public ReactiveCommand DIOPort1ClearCommand { get; }
+ public ReactiveCommand DIOPort2SetCommand { get; }
+ public ReactiveCommand DIOPort2ClearCommand { get; }
+
+ // SupplyPort Commands
+ public ReactiveCommand SupplyPort0SetCommand { get; }
+ public ReactiveCommand SupplyPort0ClearCommand { get; }
+ public ReactiveCommand SupplyPort1SetCommand { get; }
+ public ReactiveCommand SupplyPort1ClearCommand { get; }
+ public ReactiveCommand SupplyPort2SetCommand { get; }
+ public ReactiveCommand SupplyPort2ClearCommand { get; }
+ #endregion
+
+ #region Port Direction Commands
+ public ReactiveCommand Port0DirectionApplyCommand { get; }
+ public ReactiveCommand Port1DirectionApplyCommand { get; }
+ public ReactiveCommand Port2DirectionApplyCommand { get; }
+ #endregion
+
+ #region LED Control Commands
+ public ReactiveCommand Led0SetCommand { get; }
+ public ReactiveCommand Led0ClearCommand { get; }
+ public ReactiveCommand Led1SetCommand { get; }
+ public ReactiveCommand Led1ClearCommand { get; }
+ #endregion
+
+ #region RGB Control Commands
+ public ReactiveCommand Rgb0SetCommand { get; }
+ public ReactiveCommand Rgb0ClearCommand { get; }
+ public ReactiveCommand Rgb1SetCommand { get; }
+ public ReactiveCommand Rgb1ClearCommand { get; }
+ #endregion
+
+ #region PWM Control Commands
+ public ReactiveCommand PwmDO0StartCommand { get; }
+ public ReactiveCommand PwmDO0StopCommand { get; }
+ public ReactiveCommand PwmDO1StartCommand { get; }
+ public ReactiveCommand PwmDO1StopCommand { get; }
+ public ReactiveCommand PwmDO2StartCommand { get; }
+ public ReactiveCommand PwmDO2StopCommand { get; }
+ public ReactiveCommand PwmDO3StartCommand { get; }
+ public ReactiveCommand PwmDO3StopCommand { get; }
+ #endregion
+
+ #region Pulse Configuration Commands
+ // Digital Output Pulse Config
+ public ReactiveCommand SavePulseConfigDO0Command { get; }
+ public ReactiveCommand SavePulseConfigDO1Command { get; }
+ public ReactiveCommand SavePulseConfigDO2Command { get; }
+ public ReactiveCommand SavePulseConfigDO3Command { get; }
+
+ // Port Pulse Config
+ public ReactiveCommand SavePulseConfigDOPort0Command { get; }
+ public ReactiveCommand SavePulseConfigDOPort1Command { get; }
+ public ReactiveCommand SavePulseConfigDOPort2Command { get; }
+
+ // Supply Port Pulse Config
+ public ReactiveCommand SavePulseConfigSupplyPort0Command { get; }
+ public ReactiveCommand SavePulseConfigSupplyPort1Command { get; }
+ public ReactiveCommand SavePulseConfigSupplyPort2Command { get; }
+
+ // RGB and LED Pulse Config
+ public ReactiveCommand SavePulseConfigRgb0Command { get; }
+ public ReactiveCommand SavePulseConfigRgb1Command { get; }
+ public ReactiveCommand SavePulseConfigLed0Command { get; }
+ public ReactiveCommand SavePulseConfigLed1Command { get; }
+ #endregion
+
+ #region Mimic Configuration Commands
+ // IR Mimic Config
+ public ReactiveCommand SaveMimicConfigPort0IRCommand { get; }
+ public ReactiveCommand SaveMimicConfigPort1IRCommand { get; }
+ public ReactiveCommand SaveMimicConfigPort2IRCommand { get; }
+
+ // Valve Mimic Config
+ public ReactiveCommand SaveMimicConfigPort0ValveCommand { get; }
+ public ReactiveCommand SaveMimicConfigPort1ValveCommand { get; }
+ public ReactiveCommand SaveMimicConfigPort2ValveCommand { get; }
+ #endregion
+
+ #region Servo Configuration Commands
+ public ReactiveCommand ServoApplyConfigurationCommand { get; }
+ public ReactiveCommand ServoOutput2StartCommand { get; }
+ public ReactiveCommand ServoOutput2StopCommand { get; }
+ public ReactiveCommand ServoOutput3StartCommand { get; }
+ public ReactiveCommand ServoOutput3StopCommand { get; }
+ #endregion
+
+ #region Camera Configuration Commands
+ public ReactiveCommand CameraApplyConfigurationCommand { get; }
+ public ReactiveCommand Camera0StartCommand { get; }
+ public ReactiveCommand Camera0StopCommand { get; }
+ public ReactiveCommand Camera1StartCommand { get; }
+ public ReactiveCommand Camera1StopCommand { get; }
+ #endregion
+
+ #region Device basic information
+
+ [Reactive] public int DeviceID { get; set; }
+ [Reactive] public string DeviceName { get; set; }
+ [Reactive] public HarpVersion HardwareVersion { get; set; }
+ [Reactive] public HarpVersion FirmwareVersion { get; set; }
+ [Reactive] public int SerialNumber { get; set; }
+
+ #endregion
+
+ #region Registers
+
+ [Reactive] public DigitalInputs DigitalInputState { get; set; }
+ [Reactive] public DigitalOutputs OutputSet { get; set; }
+ [Reactive] public DigitalOutputs OutputClear { get; set; }
+ [Reactive] public DigitalOutputs OutputToggle { get; set; }
+ [Reactive] public DigitalOutputs OutputState { get; set; }
+ [Reactive] public PortDigitalIOS PortDIOSet { get; set; }
+ [Reactive] public PortDigitalIOS PortDIOClear { get; set; }
+ [Reactive] public PortDigitalIOS PortDIOToggle { get; set; }
+ [Reactive] public PortDigitalIOS PortDIOState { get; set; }
+ [Reactive] public PortDigitalIOS PortDIODirection { get; set; }
+ [Reactive] public PortDigitalIOS PortDIOStateEvent { get; set; }
+ [Reactive] public AnalogDataPayload AnalogData { get; set; }
+ [Reactive] public DigitalOutputs OutputPulseEnable { get; set; }
+ [Reactive] public ushort PulseDOPort0 { get; set; }
+ [Reactive] public ushort PulseDOPort1 { get; set; }
+ [Reactive] public ushort PulseDOPort2 { get; set; }
+ [Reactive] public ushort PulseSupplyPort0 { get; set; }
+ [Reactive] public ushort PulseSupplyPort1 { get; set; }
+ [Reactive] public ushort PulseSupplyPort2 { get; set; }
+ [Reactive] public ushort PulseLed0 { get; set; }
+ [Reactive] public ushort PulseLed1 { get; set; }
+ [Reactive] public ushort PulseRgb0 { get; set; }
+ [Reactive] public ushort PulseRgb1 { get; set; }
+ [Reactive] public ushort PulseDO0 { get; set; }
+ [Reactive] public ushort PulseDO1 { get; set; }
+ [Reactive] public ushort PulseDO2 { get; set; }
+ [Reactive] public ushort PulseDO3 { get; set; }
+ [Reactive] public ushort PwmFrequencyDO0 { get; set; }
+ [Reactive] public ushort PwmFrequencyDO1 { get; set; }
+ [Reactive] public ushort PwmFrequencyDO2 { get; set; }
+ [Reactive] public ushort PwmFrequencyDO3 { get; set; }
+ [Reactive] public byte PwmDutyCycleDO0 { get; set; }
+ [Reactive] public byte PwmDutyCycleDO1 { get; set; }
+ [Reactive] public byte PwmDutyCycleDO2 { get; set; }
+ [Reactive] public byte PwmDutyCycleDO3 { get; set; }
+ [Reactive] public PwmOutputs PwmStart { get; set; }
+ [Reactive] public PwmOutputs PwmStop { get; set; }
+ [Reactive] public RgbAllPayload RgbAll { get; set; }
+ [Reactive] public RgbPayload Rgb0 { get; set; }
+ [Reactive] public RgbPayload Rgb1 { get; set; }
+ [Reactive] public byte Led0Current { get; set; }
+ [Reactive] public byte Led1Current { get; set; }
+ [Reactive] public byte Led0MaxCurrent { get; set; }
+ [Reactive] public byte Led1MaxCurrent { get; set; }
+ [Reactive] public Events EventEnable { get; set; }
+ [Reactive] public CameraOutputs StartCameras { get; set; }
+ [Reactive] public CameraOutputs StopCameras { get; set; }
+ [Reactive] public ServoOutputs EnableServos { get; set; }
+ [Reactive] public ServoOutputs DisableServos { get; set; }
+ [Reactive] public EncoderInputs EnableEncoders { get; set; }
+ [Reactive] public EncoderModeConfig EncoderMode { get; set; }
+ [Reactive] public FrameAcquired Camera0Frame { get; set; }
+ [Reactive] public ushort Camera0Frequency { get; set; }
+ [Reactive] public FrameAcquired Camera1Frame { get; set; }
+ [Reactive] public ushort Camera1Frequency { get; set; }
+ [Reactive] public ushort ServoMotor2Period { get; set; }
+ [Reactive] public ushort ServoMotor2Pulse { get; set; }
+ [Reactive] public ushort ServoMotor3Period { get; set; }
+ [Reactive] public ushort ServoMotor3Pulse { get; set; }
+ [Reactive] public EncoderInputs EncoderReset { get; set; }
+ [Reactive] public byte EnableSerialTimestamp { get; set; }
+ [Reactive] public MimicOutput MimicPort0IR { get; set; }
+ [Reactive] public MimicOutput MimicPort1IR { get; set; }
+ [Reactive] public MimicOutput MimicPort2IR { get; set; }
+ [Reactive] public MimicOutput MimicPort0Valve { get; set; }
+ [Reactive] public MimicOutput MimicPort1Valve { get; set; }
+ [Reactive] public MimicOutput MimicPort2Valve { get; set; }
+ [Reactive] public byte PokeInputFilter { get; set; }
+
+ #endregion
+
+ #region Array collections
+
+ [Reactive] public RgbRegisterAdapter RgbAllAdapter { get; set; }
+ [Reactive] public RgbRegisterAdapter Rgb0Adapter { get; set; }
+ [Reactive] public RgbRegisterAdapter Rgb1Adapter { get; set; }
+ #endregion
+
+ #region Events Flags
+
+ public bool IsPortDIEnabled
+ {
+ get
+ {
+ return EventEnable.HasFlag(Events.PortDI);
+ }
+ set
+ {
+ if (value)
+ {
+ EventEnable |= Events.PortDI;
+ }
+ else
+ {
+ EventEnable &= ~Events.PortDI;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPortDIEnabled));
+ this.RaisePropertyChanged(nameof(EventEnable));
+ }
+ }
+
+ public bool IsPortDIOEnabled
+ {
+ get
+ {
+ return EventEnable.HasFlag(Events.PortDIO);
+ }
+ set
+ {
+ if (value)
+ {
+ EventEnable |= Events.PortDIO;
+ }
+ else
+ {
+ EventEnable &= ~Events.PortDIO;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPortDIOEnabled));
+ this.RaisePropertyChanged(nameof(EventEnable));
+ }
+ }
+
+ public bool IsAnalogDataEnabled
+ {
+ get
+ {
+ return EventEnable.HasFlag(Events.AnalogData);
+ }
+ set
+ {
+ if (value)
+ {
+ EventEnable |= Events.AnalogData;
+ }
+ else
+ {
+ EventEnable &= ~Events.AnalogData;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsAnalogDataEnabled));
+ this.RaisePropertyChanged(nameof(EventEnable));
+ }
+ }
+
+ public bool IsCamera0Enabled
+ {
+ get
+ {
+ return EventEnable.HasFlag(Events.Camera0);
+ }
+ set
+ {
+ if (value)
+ {
+ EventEnable |= Events.Camera0;
+ }
+ else
+ {
+ EventEnable &= ~Events.Camera0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsCamera0Enabled));
+ this.RaisePropertyChanged(nameof(EventEnable));
+ }
+ }
+
+ public bool IsCamera1Enabled
+ {
+ get
+ {
+ return EventEnable.HasFlag(Events.Camera1);
+ }
+ set
+ {
+ if (value)
+ {
+ EventEnable |= Events.Camera1;
+ }
+ else
+ {
+ EventEnable &= ~Events.Camera1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsCamera1Enabled));
+ this.RaisePropertyChanged(nameof(EventEnable));
+ }
+ }
+
+ #endregion
+
+ #region DigitalInputs_DigitalInputState Flags
+
+ public bool IsDIPort0Enabled_DigitalInputState
+ {
+ get
+ {
+ return DigitalInputState.HasFlag(DigitalInputs.DIPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ DigitalInputState |= DigitalInputs.DIPort0;
+ }
+ else
+ {
+ DigitalInputState &= ~DigitalInputs.DIPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIPort0Enabled_DigitalInputState));
+ this.RaisePropertyChanged(nameof(DigitalInputState));
+ }
+ }
+
+ public bool IsDIPort1Enabled_DigitalInputState
+ {
+ get
+ {
+ return DigitalInputState.HasFlag(DigitalInputs.DIPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ DigitalInputState |= DigitalInputs.DIPort1;
+ }
+ else
+ {
+ DigitalInputState &= ~DigitalInputs.DIPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIPort1Enabled_DigitalInputState));
+ this.RaisePropertyChanged(nameof(DigitalInputState));
+ }
+ }
+
+ public bool IsDIPort2Enabled_DigitalInputState
+ {
+ get
+ {
+ return DigitalInputState.HasFlag(DigitalInputs.DIPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ DigitalInputState |= DigitalInputs.DIPort2;
+ }
+ else
+ {
+ DigitalInputState &= ~DigitalInputs.DIPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIPort2Enabled_DigitalInputState));
+ this.RaisePropertyChanged(nameof(DigitalInputState));
+ }
+ }
+
+ public bool IsDI3Enabled_DigitalInputState
+ {
+ get
+ {
+ return DigitalInputState.HasFlag(DigitalInputs.DI3);
+ }
+ set
+ {
+ if (value)
+ {
+ DigitalInputState |= DigitalInputs.DI3;
+ }
+ else
+ {
+ DigitalInputState &= ~DigitalInputs.DI3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDI3Enabled_DigitalInputState));
+ this.RaisePropertyChanged(nameof(DigitalInputState));
+ }
+ }
+
+ #endregion
+
+ #region DigitalOutputs_OutputSet Flags
+
+ public bool IsDOPort0Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.DOPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.DOPort0;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.DOPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort0Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsDOPort1Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.DOPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.DOPort1;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.DOPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort1Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsDOPort2Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.DOPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.DOPort2;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.DOPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort2Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsSupplyPort0Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.SupplyPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.SupplyPort0;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.SupplyPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort0Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsSupplyPort1Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.SupplyPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.SupplyPort1;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.SupplyPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort1Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsSupplyPort2Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.SupplyPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.SupplyPort2;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.SupplyPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort2Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsLed0Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.Led0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.Led0;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.Led0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed0Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsLed1Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.Led1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.Led1;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.Led1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed1Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsRgb0Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.Rgb0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.Rgb0;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.Rgb0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb0Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsRgb1Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.Rgb1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.Rgb1;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.Rgb1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb1Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsDO0Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.DO0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.DO0;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.DO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO0Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsDO1Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.DO1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.DO1;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.DO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO1Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsDO2Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.DO2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.DO2;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.DO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO2Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ public bool IsDO3Enabled_OutputSet
+ {
+ get
+ {
+ return OutputSet.HasFlag(DigitalOutputs.DO3);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputSet |= DigitalOutputs.DO3;
+ }
+ else
+ {
+ OutputSet &= ~DigitalOutputs.DO3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO3Enabled_OutputSet));
+ this.RaisePropertyChanged(nameof(OutputSet));
+ }
+ }
+
+ #endregion
+
+ #region DigitalOutputs_OutputClear Flags
+
+ public bool IsDOPort0Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.DOPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.DOPort0;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.DOPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort0Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsDOPort1Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.DOPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.DOPort1;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.DOPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort1Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsDOPort2Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.DOPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.DOPort2;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.DOPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort2Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsSupplyPort0Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.SupplyPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.SupplyPort0;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.SupplyPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort0Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsSupplyPort1Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.SupplyPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.SupplyPort1;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.SupplyPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort1Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsSupplyPort2Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.SupplyPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.SupplyPort2;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.SupplyPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort2Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsLed0Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.Led0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.Led0;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.Led0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed0Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsLed1Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.Led1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.Led1;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.Led1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed1Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsRgb0Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.Rgb0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.Rgb0;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.Rgb0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb0Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsRgb1Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.Rgb1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.Rgb1;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.Rgb1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb1Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsDO0Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.DO0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.DO0;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.DO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO0Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsDO1Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.DO1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.DO1;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.DO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO1Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsDO2Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.DO2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.DO2;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.DO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO2Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ public bool IsDO3Enabled_OutputClear
+ {
+ get
+ {
+ return OutputClear.HasFlag(DigitalOutputs.DO3);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputClear |= DigitalOutputs.DO3;
+ }
+ else
+ {
+ OutputClear &= ~DigitalOutputs.DO3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO3Enabled_OutputClear));
+ this.RaisePropertyChanged(nameof(OutputClear));
+ }
+ }
+
+ #endregion
+
+ #region DigitalOutputs_OutputToggle Flags
+
+ public bool IsDOPort0Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.DOPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.DOPort0;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.DOPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort0Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsDOPort1Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.DOPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.DOPort1;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.DOPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort1Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsDOPort2Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.DOPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.DOPort2;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.DOPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort2Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsSupplyPort0Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.SupplyPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.SupplyPort0;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.SupplyPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort0Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsSupplyPort1Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.SupplyPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.SupplyPort1;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.SupplyPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort1Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsSupplyPort2Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.SupplyPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.SupplyPort2;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.SupplyPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort2Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsLed0Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.Led0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.Led0;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.Led0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed0Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsLed1Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.Led1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.Led1;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.Led1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed1Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsRgb0Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.Rgb0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.Rgb0;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.Rgb0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb0Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsRgb1Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.Rgb1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.Rgb1;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.Rgb1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb1Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsDO0Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.DO0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.DO0;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.DO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO0Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsDO1Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.DO1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.DO1;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.DO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO1Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsDO2Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.DO2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.DO2;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.DO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO2Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ public bool IsDO3Enabled_OutputToggle
+ {
+ get
+ {
+ return OutputToggle.HasFlag(DigitalOutputs.DO3);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputToggle |= DigitalOutputs.DO3;
+ }
+ else
+ {
+ OutputToggle &= ~DigitalOutputs.DO3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO3Enabled_OutputToggle));
+ this.RaisePropertyChanged(nameof(OutputToggle));
+ }
+ }
+
+ #endregion
+
+ #region DigitalOutputs_OutputState Flags
+
+ public bool IsDOPort0Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.DOPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.DOPort0;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.DOPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort0Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsDOPort1Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.DOPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.DOPort1;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.DOPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort1Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsDOPort2Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.DOPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.DOPort2;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.DOPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort2Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsSupplyPort0Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.SupplyPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.SupplyPort0;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.SupplyPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort0Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsSupplyPort1Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.SupplyPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.SupplyPort1;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.SupplyPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort1Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsSupplyPort2Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.SupplyPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.SupplyPort2;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.SupplyPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort2Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsLed0Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.Led0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.Led0;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.Led0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed0Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsLed1Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.Led1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.Led1;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.Led1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed1Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsRgb0Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.Rgb0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.Rgb0;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.Rgb0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb0Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsRgb1Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.Rgb1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.Rgb1;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.Rgb1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb1Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsDO0Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.DO0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.DO0;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.DO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO0Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsDO1Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.DO1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.DO1;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.DO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO1Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsDO2Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.DO2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.DO2;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.DO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO2Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ public bool IsDO3Enabled_OutputState
+ {
+ get
+ {
+ return OutputState.HasFlag(DigitalOutputs.DO3);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputState |= DigitalOutputs.DO3;
+ }
+ else
+ {
+ OutputState &= ~DigitalOutputs.DO3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO3Enabled_OutputState));
+ this.RaisePropertyChanged(nameof(OutputState));
+ }
+ }
+
+ #endregion
+
+ #region PortDigitalIOS_PortDIOSet Flags
+
+ public bool IsDIO0Enabled_PortDIOSet
+ {
+ get
+ {
+ return PortDIOSet.HasFlag(PortDigitalIOS.DIO0);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOSet |= PortDigitalIOS.DIO0;
+ }
+ else
+ {
+ PortDIOSet &= ~PortDigitalIOS.DIO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO0Enabled_PortDIOSet));
+ this.RaisePropertyChanged(nameof(PortDIOSet));
+ }
+ }
+
+ public bool IsDIO1Enabled_PortDIOSet
+ {
+ get
+ {
+ return PortDIOSet.HasFlag(PortDigitalIOS.DIO1);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOSet |= PortDigitalIOS.DIO1;
+ }
+ else
+ {
+ PortDIOSet &= ~PortDigitalIOS.DIO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO1Enabled_PortDIOSet));
+ this.RaisePropertyChanged(nameof(PortDIOSet));
+ }
+ }
+
+ public bool IsDIO2Enabled_PortDIOSet
+ {
+ get
+ {
+ return PortDIOSet.HasFlag(PortDigitalIOS.DIO2);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOSet |= PortDigitalIOS.DIO2;
+ }
+ else
+ {
+ PortDIOSet &= ~PortDigitalIOS.DIO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO2Enabled_PortDIOSet));
+ this.RaisePropertyChanged(nameof(PortDIOSet));
+ }
+ }
+
+ #endregion
+
+ #region PortDigitalIOS_PortDIOClear Flags
+
+ public bool IsDIO0Enabled_PortDIOClear
+ {
+ get
+ {
+ return PortDIOClear.HasFlag(PortDigitalIOS.DIO0);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOClear |= PortDigitalIOS.DIO0;
+ }
+ else
+ {
+ PortDIOClear &= ~PortDigitalIOS.DIO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO0Enabled_PortDIOClear));
+ this.RaisePropertyChanged(nameof(PortDIOClear));
+ }
+ }
+
+ public bool IsDIO1Enabled_PortDIOClear
+ {
+ get
+ {
+ return PortDIOClear.HasFlag(PortDigitalIOS.DIO1);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOClear |= PortDigitalIOS.DIO1;
+ }
+ else
+ {
+ PortDIOClear &= ~PortDigitalIOS.DIO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO1Enabled_PortDIOClear));
+ this.RaisePropertyChanged(nameof(PortDIOClear));
+ }
+ }
+
+ public bool IsDIO2Enabled_PortDIOClear
+ {
+ get
+ {
+ return PortDIOClear.HasFlag(PortDigitalIOS.DIO2);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOClear |= PortDigitalIOS.DIO2;
+ }
+ else
+ {
+ PortDIOClear &= ~PortDigitalIOS.DIO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO2Enabled_PortDIOClear));
+ this.RaisePropertyChanged(nameof(PortDIOClear));
+ }
+ }
+
+ #endregion
+
+ #region PortDigitalIOS_PortDIOToggle Flags
+
+ public bool IsDIO0Enabled_PortDIOToggle
+ {
+ get
+ {
+ return PortDIOToggle.HasFlag(PortDigitalIOS.DIO0);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOToggle |= PortDigitalIOS.DIO0;
+ }
+ else
+ {
+ PortDIOToggle &= ~PortDigitalIOS.DIO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO0Enabled_PortDIOToggle));
+ this.RaisePropertyChanged(nameof(PortDIOToggle));
+ }
+ }
+
+ public bool IsDIO1Enabled_PortDIOToggle
+ {
+ get
+ {
+ return PortDIOToggle.HasFlag(PortDigitalIOS.DIO1);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOToggle |= PortDigitalIOS.DIO1;
+ }
+ else
+ {
+ PortDIOToggle &= ~PortDigitalIOS.DIO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO1Enabled_PortDIOToggle));
+ this.RaisePropertyChanged(nameof(PortDIOToggle));
+ }
+ }
+
+ public bool IsDIO2Enabled_PortDIOToggle
+ {
+ get
+ {
+ return PortDIOToggle.HasFlag(PortDigitalIOS.DIO2);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOToggle |= PortDigitalIOS.DIO2;
+ }
+ else
+ {
+ PortDIOToggle &= ~PortDigitalIOS.DIO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO2Enabled_PortDIOToggle));
+ this.RaisePropertyChanged(nameof(PortDIOToggle));
+ }
+ }
+
+ #endregion
+
+ #region PortDigitalIOS_PortDIOState Flags
+
+ public bool IsDIO0Enabled_PortDIOState
+ {
+ get
+ {
+ return PortDIOState.HasFlag(PortDigitalIOS.DIO0);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOState |= PortDigitalIOS.DIO0;
+ }
+ else
+ {
+ PortDIOState &= ~PortDigitalIOS.DIO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO0Enabled_PortDIOState));
+ this.RaisePropertyChanged(nameof(PortDIOState));
+ }
+ }
+
+ public bool IsDIO1Enabled_PortDIOState
+ {
+ get
+ {
+ return PortDIOState.HasFlag(PortDigitalIOS.DIO1);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOState |= PortDigitalIOS.DIO1;
+ }
+ else
+ {
+ PortDIOState &= ~PortDigitalIOS.DIO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO1Enabled_PortDIOState));
+ this.RaisePropertyChanged(nameof(PortDIOState));
+ }
+ }
+
+ public bool IsDIO2Enabled_PortDIOState
+ {
+ get
+ {
+ return PortDIOState.HasFlag(PortDigitalIOS.DIO2);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOState |= PortDigitalIOS.DIO2;
+ }
+ else
+ {
+ PortDIOState &= ~PortDigitalIOS.DIO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO2Enabled_PortDIOState));
+ this.RaisePropertyChanged(nameof(PortDIOState));
+ }
+ }
+
+ #endregion
+
+ #region PortDigitalIOS_PortDIODirection Flags
+
+ public bool IsDIO0Enabled_PortDIODirection
+ {
+ get
+ {
+ return PortDIODirection.HasFlag(PortDigitalIOS.DIO0);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIODirection |= PortDigitalIOS.DIO0;
+ }
+ else
+ {
+ PortDIODirection &= ~PortDigitalIOS.DIO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO0Enabled_PortDIODirection));
+ this.RaisePropertyChanged(nameof(PortDIODirection));
+ }
+ }
+
+ public bool IsDIO1Enabled_PortDIODirection
+ {
+ get
+ {
+ return PortDIODirection.HasFlag(PortDigitalIOS.DIO1);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIODirection |= PortDigitalIOS.DIO1;
+ }
+ else
+ {
+ PortDIODirection &= ~PortDigitalIOS.DIO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO1Enabled_PortDIODirection));
+ this.RaisePropertyChanged(nameof(PortDIODirection));
+ }
+ }
+
+ public bool IsDIO2Enabled_PortDIODirection
+ {
+ get
+ {
+ return PortDIODirection.HasFlag(PortDigitalIOS.DIO2);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIODirection |= PortDigitalIOS.DIO2;
+ }
+ else
+ {
+ PortDIODirection &= ~PortDigitalIOS.DIO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO2Enabled_PortDIODirection));
+ this.RaisePropertyChanged(nameof(PortDIODirection));
+ }
+ }
+
+ #endregion
+
+ #region PortDigitalIOS_PortDIOStateEvent Flags
+
+ public bool IsDIO0Enabled_PortDIOStateEvent
+ {
+ get
+ {
+ return PortDIOStateEvent.HasFlag(PortDigitalIOS.DIO0);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOStateEvent |= PortDigitalIOS.DIO0;
+ }
+ else
+ {
+ PortDIOStateEvent &= ~PortDigitalIOS.DIO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO0Enabled_PortDIOStateEvent));
+ this.RaisePropertyChanged(nameof(PortDIOStateEvent));
+ }
+ }
+
+ public bool IsDIO1Enabled_PortDIOStateEvent
+ {
+ get
+ {
+ return PortDIOStateEvent.HasFlag(PortDigitalIOS.DIO1);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOStateEvent |= PortDigitalIOS.DIO1;
+ }
+ else
+ {
+ PortDIOStateEvent &= ~PortDigitalIOS.DIO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO1Enabled_PortDIOStateEvent));
+ this.RaisePropertyChanged(nameof(PortDIOStateEvent));
+ }
+ }
+
+ public bool IsDIO2Enabled_PortDIOStateEvent
+ {
+ get
+ {
+ return PortDIOStateEvent.HasFlag(PortDigitalIOS.DIO2);
+ }
+ set
+ {
+ if (value)
+ {
+ PortDIOStateEvent |= PortDigitalIOS.DIO2;
+ }
+ else
+ {
+ PortDIOStateEvent &= ~PortDigitalIOS.DIO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDIO2Enabled_PortDIOStateEvent));
+ this.RaisePropertyChanged(nameof(PortDIOStateEvent));
+ }
+ }
+
+ #endregion
+
+ #region DigitalOutputs_OutputPulseEnable Flags
+
+ public bool IsDOPort0Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.DOPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.DOPort0;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.DOPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort0Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsDOPort1Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.DOPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.DOPort1;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.DOPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort1Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsDOPort2Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.DOPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.DOPort2;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.DOPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDOPort2Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsSupplyPort0Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.SupplyPort0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.SupplyPort0;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.SupplyPort0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort0Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsSupplyPort1Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.SupplyPort1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.SupplyPort1;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.SupplyPort1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort1Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsSupplyPort2Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.SupplyPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.SupplyPort2;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.SupplyPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsSupplyPort2Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsLed0Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.Led0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.Led0;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.Led0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed0Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsLed1Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.Led1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.Led1;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.Led1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsLed1Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsRgb0Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.Rgb0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.Rgb0;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.Rgb0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb0Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsRgb1Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.Rgb1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.Rgb1;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.Rgb1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsRgb1Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsDO0Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.DO0);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.DO0;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.DO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO0Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsDO1Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.DO1);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.DO1;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.DO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO1Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsDO2Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.DO2);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.DO2;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.DO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO2Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ public bool IsDO3Enabled_OutputPulseEnable
+ {
+ get
+ {
+ return OutputPulseEnable.HasFlag(DigitalOutputs.DO3);
+ }
+ set
+ {
+ if (value)
+ {
+ OutputPulseEnable |= DigitalOutputs.DO3;
+ }
+ else
+ {
+ OutputPulseEnable &= ~DigitalOutputs.DO3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsDO3Enabled_OutputPulseEnable));
+ this.RaisePropertyChanged(nameof(OutputPulseEnable));
+ }
+ }
+
+ #endregion
+
+ #region PwmOutputs_PwmStart Flags
+
+ public bool IsPwmDO0Enabled_PwmStart
+ {
+ get
+ {
+ return PwmStart.HasFlag(PwmOutputs.PwmDO0);
+ }
+ set
+ {
+ if (value)
+ {
+ PwmStart |= PwmOutputs.PwmDO0;
+ }
+ else
+ {
+ PwmStart &= ~PwmOutputs.PwmDO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPwmDO0Enabled_PwmStart));
+ this.RaisePropertyChanged(nameof(PwmStart));
+ }
+ }
+
+ public bool IsPwmDO1Enabled_PwmStart
+ {
+ get
+ {
+ return PwmStart.HasFlag(PwmOutputs.PwmDO1);
+ }
+ set
+ {
+ if (value)
+ {
+ PwmStart |= PwmOutputs.PwmDO1;
+ }
+ else
+ {
+ PwmStart &= ~PwmOutputs.PwmDO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPwmDO1Enabled_PwmStart));
+ this.RaisePropertyChanged(nameof(PwmStart));
+ }
+ }
+
+ public bool IsPwmDO2Enabled_PwmStart
+ {
+ get
+ {
+ return PwmStart.HasFlag(PwmOutputs.PwmDO2);
+ }
+ set
+ {
+ if (value)
+ {
+ PwmStart |= PwmOutputs.PwmDO2;
+ }
+ else
+ {
+ PwmStart &= ~PwmOutputs.PwmDO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPwmDO2Enabled_PwmStart));
+ this.RaisePropertyChanged(nameof(PwmStart));
+ }
+ }
+
+ public bool IsPwmDO3Enabled_PwmStart
+ {
+ get
+ {
+ return PwmStart.HasFlag(PwmOutputs.PwmDO3);
+ }
+ set
+ {
+ if (value)
+ {
+ PwmStart |= PwmOutputs.PwmDO3;
+ }
+ else
+ {
+ PwmStart &= ~PwmOutputs.PwmDO3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPwmDO3Enabled_PwmStart));
+ this.RaisePropertyChanged(nameof(PwmStart));
+ }
+ }
+
+ #endregion
+
+ #region PwmOutputs_PwmStop Flags
+
+ public bool IsPwmDO0Enabled_PwmStop
+ {
+ get
+ {
+ return PwmStop.HasFlag(PwmOutputs.PwmDO0);
+ }
+ set
+ {
+ if (value)
+ {
+ PwmStop |= PwmOutputs.PwmDO0;
+ }
+ else
+ {
+ PwmStop &= ~PwmOutputs.PwmDO0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPwmDO0Enabled_PwmStop));
+ this.RaisePropertyChanged(nameof(PwmStop));
+ }
+ }
+
+ public bool IsPwmDO1Enabled_PwmStop
+ {
+ get
+ {
+ return PwmStop.HasFlag(PwmOutputs.PwmDO1);
+ }
+ set
+ {
+ if (value)
+ {
+ PwmStop |= PwmOutputs.PwmDO1;
+ }
+ else
+ {
+ PwmStop &= ~PwmOutputs.PwmDO1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPwmDO1Enabled_PwmStop));
+ this.RaisePropertyChanged(nameof(PwmStop));
+ }
+ }
+
+ public bool IsPwmDO2Enabled_PwmStop
+ {
+ get
+ {
+ return PwmStop.HasFlag(PwmOutputs.PwmDO2);
+ }
+ set
+ {
+ if (value)
+ {
+ PwmStop |= PwmOutputs.PwmDO2;
+ }
+ else
+ {
+ PwmStop &= ~PwmOutputs.PwmDO2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPwmDO2Enabled_PwmStop));
+ this.RaisePropertyChanged(nameof(PwmStop));
+ }
+ }
+
+ public bool IsPwmDO3Enabled_PwmStop
+ {
+ get
+ {
+ return PwmStop.HasFlag(PwmOutputs.PwmDO3);
+ }
+ set
+ {
+ if (value)
+ {
+ PwmStop |= PwmOutputs.PwmDO3;
+ }
+ else
+ {
+ PwmStop &= ~PwmOutputs.PwmDO3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsPwmDO3Enabled_PwmStop));
+ this.RaisePropertyChanged(nameof(PwmStop));
+ }
+ }
+
+ #endregion
+
+ #region CameraOutputs_StartCameras Flags
+
+ public bool IsCameraOutput0Enabled_StartCameras
+ {
+ get
+ {
+ return StartCameras.HasFlag(CameraOutputs.CameraOutput0);
+ }
+ set
+ {
+ if (value)
+ {
+ StartCameras |= CameraOutputs.CameraOutput0;
+ }
+ else
+ {
+ StartCameras &= ~CameraOutputs.CameraOutput0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsCameraOutput0Enabled_StartCameras));
+ this.RaisePropertyChanged(nameof(StartCameras));
+ }
+ }
+
+ public bool IsCameraOutput1Enabled_StartCameras
+ {
+ get
+ {
+ return StartCameras.HasFlag(CameraOutputs.CameraOutput1);
+ }
+ set
+ {
+ if (value)
+ {
+ StartCameras |= CameraOutputs.CameraOutput1;
+ }
+ else
+ {
+ StartCameras &= ~CameraOutputs.CameraOutput1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsCameraOutput1Enabled_StartCameras));
+ this.RaisePropertyChanged(nameof(StartCameras));
+ }
+ }
+
+ #endregion
+
+ #region CameraOutputs_StopCameras Flags
+
+ public bool IsCameraOutput0Enabled_StopCameras
+ {
+ get
+ {
+ return StopCameras.HasFlag(CameraOutputs.CameraOutput0);
+ }
+ set
+ {
+ if (value)
+ {
+ StopCameras |= CameraOutputs.CameraOutput0;
+ }
+ else
+ {
+ StopCameras &= ~CameraOutputs.CameraOutput0;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsCameraOutput0Enabled_StopCameras));
+ this.RaisePropertyChanged(nameof(StopCameras));
+ }
+ }
+
+ public bool IsCameraOutput1Enabled_StopCameras
+ {
+ get
+ {
+ return StopCameras.HasFlag(CameraOutputs.CameraOutput1);
+ }
+ set
+ {
+ if (value)
+ {
+ StopCameras |= CameraOutputs.CameraOutput1;
+ }
+ else
+ {
+ StopCameras &= ~CameraOutputs.CameraOutput1;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsCameraOutput1Enabled_StopCameras));
+ this.RaisePropertyChanged(nameof(StopCameras));
+ }
+ }
+
+ #endregion
+
+ #region ServoOutputs_EnableServos Flags
+
+ public bool IsServoOutput2Enabled_EnableServos
+ {
+ get
+ {
+ return EnableServos.HasFlag(ServoOutputs.ServoOutput2);
+ }
+ set
+ {
+ if (value)
+ {
+ EnableServos |= ServoOutputs.ServoOutput2;
+ }
+ else
+ {
+ EnableServos &= ~ServoOutputs.ServoOutput2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsServoOutput2Enabled_EnableServos));
+ this.RaisePropertyChanged(nameof(EnableServos));
+ }
+ }
+
+ public bool IsServoOutput3Enabled_EnableServos
+ {
+ get
+ {
+ return EnableServos.HasFlag(ServoOutputs.ServoOutput3);
+ }
+ set
+ {
+ if (value)
+ {
+ EnableServos |= ServoOutputs.ServoOutput3;
+ }
+ else
+ {
+ EnableServos &= ~ServoOutputs.ServoOutput3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsServoOutput3Enabled_EnableServos));
+ this.RaisePropertyChanged(nameof(EnableServos));
+ }
+ }
+
+ #endregion
+
+ #region ServoOutputs_DisableServos Flags
+
+ public bool IsServoOutput2Enabled_DisableServos
+ {
+ get
+ {
+ return DisableServos.HasFlag(ServoOutputs.ServoOutput2);
+ }
+ set
+ {
+ if (value)
+ {
+ DisableServos |= ServoOutputs.ServoOutput2;
+ }
+ else
+ {
+ DisableServos &= ~ServoOutputs.ServoOutput2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsServoOutput2Enabled_DisableServos));
+ this.RaisePropertyChanged(nameof(DisableServos));
+ }
+ }
+
+ public bool IsServoOutput3Enabled_DisableServos
+ {
+ get
+ {
+ return DisableServos.HasFlag(ServoOutputs.ServoOutput3);
+ }
+ set
+ {
+ if (value)
+ {
+ DisableServos |= ServoOutputs.ServoOutput3;
+ }
+ else
+ {
+ DisableServos &= ~ServoOutputs.ServoOutput3;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsServoOutput3Enabled_DisableServos));
+ this.RaisePropertyChanged(nameof(DisableServos));
+ }
+ }
+
+ #endregion
+
+ #region EncoderInputs_EnableEncoders Flags
+
+ public bool IsEncoderPort2Enabled_EnableEncoders
+ {
+ get
+ {
+ return EnableEncoders.HasFlag(EncoderInputs.EncoderPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ EnableEncoders |= EncoderInputs.EncoderPort2;
+ }
+ else
+ {
+ EnableEncoders &= ~EncoderInputs.EncoderPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsEncoderPort2Enabled_EnableEncoders));
+ this.RaisePropertyChanged(nameof(EnableEncoders));
+ }
+ }
+
+ #endregion
+
+ #region FrameAcquired_Camera0Frame Flags
+
+ public bool IsFrameAcquiredEnabled_Camera0Frame
+ {
+ get
+ {
+ return Camera0Frame.HasFlag(FrameAcquired.FrameAcquired);
+ }
+ set
+ {
+ if (value)
+ {
+ Camera0Frame |= FrameAcquired.FrameAcquired;
+ }
+ else
+ {
+ Camera0Frame &= ~FrameAcquired.FrameAcquired;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsFrameAcquiredEnabled_Camera0Frame));
+ this.RaisePropertyChanged(nameof(Camera0Frame));
+ }
+ }
+
+ #endregion
+
+ #region FrameAcquired_Camera1Frame Flags
+
+ public bool IsFrameAcquiredEnabled_Camera1Frame
+ {
+ get
+ {
+ return Camera1Frame.HasFlag(FrameAcquired.FrameAcquired);
+ }
+ set
+ {
+ if (value)
+ {
+ Camera1Frame |= FrameAcquired.FrameAcquired;
+ }
+ else
+ {
+ Camera1Frame &= ~FrameAcquired.FrameAcquired;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsFrameAcquiredEnabled_Camera1Frame));
+ this.RaisePropertyChanged(nameof(Camera1Frame));
+ }
+ }
+
+ #endregion
+
+ #region EncoderInputs_EncoderReset Flags
+
+ public bool IsEncoderPort2Enabled_EncoderReset
+ {
+ get
+ {
+ return EncoderReset.HasFlag(EncoderInputs.EncoderPort2);
+ }
+ set
+ {
+ if (value)
+ {
+ EncoderReset |= EncoderInputs.EncoderPort2;
+ }
+ else
+ {
+ EncoderReset &= ~EncoderInputs.EncoderPort2;
+ }
+
+ // Notify the UI about the change
+ this.RaisePropertyChanged(nameof(IsEncoderPort2Enabled_EncoderReset));
+ this.RaisePropertyChanged(nameof(EncoderReset));
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Application State
+
+ [ObservableAsProperty] public bool IsLoadingPorts { get; }
+ [ObservableAsProperty] public bool IsConnecting { get; }
+ [ObservableAsProperty] public bool IsResetting { get; }
+ [ObservableAsProperty] public bool IsSaving { get; }
+
+ [Reactive] public bool ShowWriteMessages { get; set; }
+ [Reactive] public ObservableCollection HarpEvents { get; set; } = new ObservableCollection();
+ [Reactive] public ObservableCollection SentMessages { get; set; } = new ObservableCollection();
+
+ public ReactiveCommand ShowAboutCommand { get; private set; }
+ public ReactiveCommand ClearMessagesCommand { get; private set; }
+ public ReactiveCommand ShowMessagesCommand { get; private set; }
+
+
+ #endregion
+
+ private Harp.Behavior.AsyncDevice? _device;
+ private IObservable _deviceEventsObservable;
+ private IDisposable? _deviceEventsSubscription;
+
+ public BehaviorViewModel()
+ {
+ var assembly = typeof(BehaviorViewModel).Assembly;
+ var informationVersion = assembly.GetName().Version;
+ if (informationVersion != null)
+ AppVersion = $"v{informationVersion.Major}.{informationVersion.Minor}.{informationVersion.Build}";
+
+ Ports = new ObservableCollection();
+
+ ClearMessagesCommand = ReactiveCommand.Create(() => { SentMessages.Clear(); });
+ ShowMessagesCommand = ReactiveCommand.Create(() => { ShowWriteMessages = !ShowWriteMessages; });
+
+
+ LoadDeviceInformation = ReactiveCommand.CreateFromObservable(LoadUsbInformation);
+ LoadDeviceInformation.IsExecuting.ToPropertyEx(this, x => x.IsLoadingPorts);
+ LoadDeviceInformation.ThrownExceptions.Subscribe(ex =>
+ Console.WriteLine($"Error loading device information with exception: {ex.Message}"));
+ //Log.Error(ex, "Error loading device information with exception: {Exception}", ex));
+
+ // can connect if there is a selection and also if the new selection is different than the old one
+ var canConnect = this.WhenAnyValue(x => x.SelectedPort)
+ .Select(selectedPort => !string.IsNullOrEmpty(selectedPort));
+
+ ShowAboutCommand = ReactiveCommand.CreateFromTask(async () =>
+ await new About() { DataContext = new AboutViewModel() }.ShowDialog(
+ (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow));
+
+ ConnectAndGetBaseInfoCommand = ReactiveCommand.CreateFromTask(ConnectAndGetBaseInfo, canConnect);
+ ConnectAndGetBaseInfoCommand.IsExecuting.ToPropertyEx(this, x => x.IsConnecting);
+ ConnectAndGetBaseInfoCommand.ThrownExceptions.Subscribe(ex =>
+ //Log.Error(ex, "Error connecting to device with error: {Exception}", ex));
+ Console.WriteLine($"Error connecting to device with error: {ex}"));
+
+ SelectedTabIndex = 1;
+
+ var canChangeConfig = this.WhenAnyValue(x => x.Connected).Select(connected => connected);
+ // Handle Save and Reset
+ SaveConfigurationCommand =
+ ReactiveCommand.CreateFromObservable(SaveConfiguration, canChangeConfig);
+ SaveConfigurationCommand.IsExecuting.ToPropertyEx(this, x => x.IsSaving);
+ SaveConfigurationCommand.ThrownExceptions.Subscribe(ex =>
+ //Log.Error(ex, "Error saving configuration with error: {Exception}", ex));
+ Console.WriteLine($"Error saving configuration with error: {ex}"));
+
+ ResetConfigurationCommand = ReactiveCommand.CreateFromObservable(ResetConfiguration, canChangeConfig);
+ ResetConfigurationCommand.IsExecuting.ToPropertyEx(this, x => x.IsResetting);
+ ResetConfigurationCommand.ThrownExceptions.Subscribe(ex =>
+ //Log.Error(ex, "Error resetting device configuration with error: {Exception}", ex));
+ Console.WriteLine($"Error resetting device configuration with error: {ex}"));
+
+ this.WhenAnyValue(x => x.Connected)
+ .Subscribe(x => { ConnectButtonText = x ? "Disconnect" : "Connect"; });
+
+ this.WhenAnyValue(x => x.EventEnable)
+ .Subscribe(x =>
+ {
+ IsPortDIEnabled = x.HasFlag(Events.PortDI);
+ IsPortDIOEnabled = x.HasFlag(Events.PortDIO);
+ IsAnalogDataEnabled = x.HasFlag(Events.AnalogData);
+ IsCamera0Enabled = x.HasFlag(Events.Camera0);
+ IsCamera1Enabled = x.HasFlag(Events.Camera1);
+ });
+
+ RgbAllAdapter = new RgbRegisterAdapter(
+ "RgbAll",
+ 2,
+ true,
+ RgbKind.Array);
+ Rgb0Adapter = new RgbRegisterAdapter(
+ "Rgb0",
+ 1,
+ true,
+ RgbKind.Simple);
+ Rgb1Adapter = new RgbRegisterAdapter(
+ "Rgb1",
+ 1,
+ true,
+ RgbKind.Simple);
+
+ // handle the events from the device
+ // When Connected changes subscribe/unsubscribe the device events.
+ this.WhenAnyValue(x => x.Connected)
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(isConnected =>
+ {
+ if (isConnected && _deviceEventsObservable != null)
+ {
+ // Subscribe on the UI thread so that the HarpEvents collection can be updated safely.
+ SubscribeToEvents();
+ }
+ else
+ {
+ // Dispose subscription and clear messages.
+ _deviceEventsSubscription?.Dispose();
+ _deviceEventsSubscription = null;
+ }
+ });
+
+ this.WhenAnyValue(x => x.DigitalInputState)
+ .Subscribe(x =>
+ {
+ IsDIPort0Enabled_DigitalInputState = x.HasFlag(DigitalInputs.DIPort0);
+ IsDIPort1Enabled_DigitalInputState = x.HasFlag(DigitalInputs.DIPort1);
+ IsDIPort2Enabled_DigitalInputState = x.HasFlag(DigitalInputs.DIPort2);
+ IsDI3Enabled_DigitalInputState = x.HasFlag(DigitalInputs.DI3);
+ });
+
+ this.WhenAnyValue(x => x.OutputSet)
+ .Subscribe(x =>
+ {
+ IsDOPort0Enabled_OutputSet = x.HasFlag(DigitalOutputs.DOPort0);
+ IsDOPort1Enabled_OutputSet = x.HasFlag(DigitalOutputs.DOPort1);
+ IsDOPort2Enabled_OutputSet = x.HasFlag(DigitalOutputs.DOPort2);
+ IsSupplyPort0Enabled_OutputSet = x.HasFlag(DigitalOutputs.SupplyPort0);
+ IsSupplyPort1Enabled_OutputSet = x.HasFlag(DigitalOutputs.SupplyPort1);
+ IsSupplyPort2Enabled_OutputSet = x.HasFlag(DigitalOutputs.SupplyPort2);
+ IsLed0Enabled_OutputSet = x.HasFlag(DigitalOutputs.Led0);
+ IsLed1Enabled_OutputSet = x.HasFlag(DigitalOutputs.Led1);
+ IsRgb0Enabled_OutputSet = x.HasFlag(DigitalOutputs.Rgb0);
+ IsRgb1Enabled_OutputSet = x.HasFlag(DigitalOutputs.Rgb1);
+ IsDO0Enabled_OutputSet = x.HasFlag(DigitalOutputs.DO0);
+ IsDO1Enabled_OutputSet = x.HasFlag(DigitalOutputs.DO1);
+ IsDO2Enabled_OutputSet = x.HasFlag(DigitalOutputs.DO2);
+ IsDO3Enabled_OutputSet = x.HasFlag(DigitalOutputs.DO3);
+ });
+
+ this.WhenAnyValue(x => x.OutputClear)
+ .Subscribe(x =>
+ {
+ IsDOPort0Enabled_OutputClear = x.HasFlag(DigitalOutputs.DOPort0);
+ IsDOPort1Enabled_OutputClear = x.HasFlag(DigitalOutputs.DOPort1);
+ IsDOPort2Enabled_OutputClear = x.HasFlag(DigitalOutputs.DOPort2);
+ IsSupplyPort0Enabled_OutputClear = x.HasFlag(DigitalOutputs.SupplyPort0);
+ IsSupplyPort1Enabled_OutputClear = x.HasFlag(DigitalOutputs.SupplyPort1);
+ IsSupplyPort2Enabled_OutputClear = x.HasFlag(DigitalOutputs.SupplyPort2);
+ IsLed0Enabled_OutputClear = x.HasFlag(DigitalOutputs.Led0);
+ IsLed1Enabled_OutputClear = x.HasFlag(DigitalOutputs.Led1);
+ IsRgb0Enabled_OutputClear = x.HasFlag(DigitalOutputs.Rgb0);
+ IsRgb1Enabled_OutputClear = x.HasFlag(DigitalOutputs.Rgb1);
+ IsDO0Enabled_OutputClear = x.HasFlag(DigitalOutputs.DO0);
+ IsDO1Enabled_OutputClear = x.HasFlag(DigitalOutputs.DO1);
+ IsDO2Enabled_OutputClear = x.HasFlag(DigitalOutputs.DO2);
+ IsDO3Enabled_OutputClear = x.HasFlag(DigitalOutputs.DO3);
+ });
+
+ this.WhenAnyValue(x => x.OutputToggle)
+ .Subscribe(x =>
+ {
+ IsDOPort0Enabled_OutputToggle = x.HasFlag(DigitalOutputs.DOPort0);
+ IsDOPort1Enabled_OutputToggle = x.HasFlag(DigitalOutputs.DOPort1);
+ IsDOPort2Enabled_OutputToggle = x.HasFlag(DigitalOutputs.DOPort2);
+ IsSupplyPort0Enabled_OutputToggle = x.HasFlag(DigitalOutputs.SupplyPort0);
+ IsSupplyPort1Enabled_OutputToggle = x.HasFlag(DigitalOutputs.SupplyPort1);
+ IsSupplyPort2Enabled_OutputToggle = x.HasFlag(DigitalOutputs.SupplyPort2);
+ IsLed0Enabled_OutputToggle = x.HasFlag(DigitalOutputs.Led0);
+ IsLed1Enabled_OutputToggle = x.HasFlag(DigitalOutputs.Led1);
+ IsRgb0Enabled_OutputToggle = x.HasFlag(DigitalOutputs.Rgb0);
+ IsRgb1Enabled_OutputToggle = x.HasFlag(DigitalOutputs.Rgb1);
+ IsDO0Enabled_OutputToggle = x.HasFlag(DigitalOutputs.DO0);
+ IsDO1Enabled_OutputToggle = x.HasFlag(DigitalOutputs.DO1);
+ IsDO2Enabled_OutputToggle = x.HasFlag(DigitalOutputs.DO2);
+ IsDO3Enabled_OutputToggle = x.HasFlag(DigitalOutputs.DO3);
+ });
+
+ this.WhenAnyValue(x => x.OutputState)
+ .Subscribe(x =>
+ {
+ IsDOPort0Enabled_OutputState = x.HasFlag(DigitalOutputs.DOPort0);
+ IsDOPort1Enabled_OutputState = x.HasFlag(DigitalOutputs.DOPort1);
+ IsDOPort2Enabled_OutputState = x.HasFlag(DigitalOutputs.DOPort2);
+ IsSupplyPort0Enabled_OutputState = x.HasFlag(DigitalOutputs.SupplyPort0);
+ IsSupplyPort1Enabled_OutputState = x.HasFlag(DigitalOutputs.SupplyPort1);
+ IsSupplyPort2Enabled_OutputState = x.HasFlag(DigitalOutputs.SupplyPort2);
+ IsLed0Enabled_OutputState = x.HasFlag(DigitalOutputs.Led0);
+ IsLed1Enabled_OutputState = x.HasFlag(DigitalOutputs.Led1);
+ IsRgb0Enabled_OutputState = x.HasFlag(DigitalOutputs.Rgb0);
+ IsRgb1Enabled_OutputState = x.HasFlag(DigitalOutputs.Rgb1);
+ IsDO0Enabled_OutputState = x.HasFlag(DigitalOutputs.DO0);
+ IsDO1Enabled_OutputState = x.HasFlag(DigitalOutputs.DO1);
+ IsDO2Enabled_OutputState = x.HasFlag(DigitalOutputs.DO2);
+ IsDO3Enabled_OutputState = x.HasFlag(DigitalOutputs.DO3);
+ });
+
+ this.WhenAnyValue(x => x.PortDIOSet)
+ .Subscribe(x =>
+ {
+ IsDIO0Enabled_PortDIOSet = x.HasFlag(PortDigitalIOS.DIO0);
+ IsDIO1Enabled_PortDIOSet = x.HasFlag(PortDigitalIOS.DIO1);
+ IsDIO2Enabled_PortDIOSet = x.HasFlag(PortDigitalIOS.DIO2);
+ });
+
+ this.WhenAnyValue(x => x.PortDIOClear)
+ .Subscribe(x =>
+ {
+ IsDIO0Enabled_PortDIOClear = x.HasFlag(PortDigitalIOS.DIO0);
+ IsDIO1Enabled_PortDIOClear = x.HasFlag(PortDigitalIOS.DIO1);
+ IsDIO2Enabled_PortDIOClear = x.HasFlag(PortDigitalIOS.DIO2);
+ });
+
+ this.WhenAnyValue(x => x.PortDIOToggle)
+ .Subscribe(x =>
+ {
+ IsDIO0Enabled_PortDIOToggle = x.HasFlag(PortDigitalIOS.DIO0);
+ IsDIO1Enabled_PortDIOToggle = x.HasFlag(PortDigitalIOS.DIO1);
+ IsDIO2Enabled_PortDIOToggle = x.HasFlag(PortDigitalIOS.DIO2);
+ });
+
+ this.WhenAnyValue(x => x.PortDIOState)
+ .Subscribe(x =>
+ {
+ IsDIO0Enabled_PortDIOState = x.HasFlag(PortDigitalIOS.DIO0);
+ IsDIO1Enabled_PortDIOState = x.HasFlag(PortDigitalIOS.DIO1);
+ IsDIO2Enabled_PortDIOState = x.HasFlag(PortDigitalIOS.DIO2);
+ });
+
+ this.WhenAnyValue(x => x.PortDIODirection)
+ .Subscribe(x =>
+ {
+ IsDIO0Enabled_PortDIODirection = x.HasFlag(PortDigitalIOS.DIO0);
+ IsDIO1Enabled_PortDIODirection = x.HasFlag(PortDigitalIOS.DIO1);
+ IsDIO2Enabled_PortDIODirection = x.HasFlag(PortDigitalIOS.DIO2);
+ });
+
+ this.WhenAnyValue(x => x.PortDIOStateEvent)
+ .Subscribe(x =>
+ {
+ IsDIO0Enabled_PortDIOStateEvent = x.HasFlag(PortDigitalIOS.DIO0);
+ IsDIO1Enabled_PortDIOStateEvent = x.HasFlag(PortDigitalIOS.DIO1);
+ IsDIO2Enabled_PortDIOStateEvent = x.HasFlag(PortDigitalIOS.DIO2);
+ });
+
+ this.WhenAnyValue(x => x.OutputPulseEnable)
+ .Subscribe(x =>
+ {
+ IsDOPort0Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.DOPort0);
+ IsDOPort1Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.DOPort1);
+ IsDOPort2Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.DOPort2);
+ IsSupplyPort0Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.SupplyPort0);
+ IsSupplyPort1Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.SupplyPort1);
+ IsSupplyPort2Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.SupplyPort2);
+ IsLed0Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.Led0);
+ IsLed1Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.Led1);
+ IsRgb0Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.Rgb0);
+ IsRgb1Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.Rgb1);
+ IsDO0Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.DO0);
+ IsDO1Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.DO1);
+ IsDO2Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.DO2);
+ IsDO3Enabled_OutputPulseEnable = x.HasFlag(DigitalOutputs.DO3);
+ });
+
+ this.WhenAnyValue(x => x.PwmStart)
+ .Subscribe(x =>
+ {
+ IsPwmDO0Enabled_PwmStart = x.HasFlag(PwmOutputs.PwmDO0);
+ IsPwmDO1Enabled_PwmStart = x.HasFlag(PwmOutputs.PwmDO1);
+ IsPwmDO2Enabled_PwmStart = x.HasFlag(PwmOutputs.PwmDO2);
+ IsPwmDO3Enabled_PwmStart = x.HasFlag(PwmOutputs.PwmDO3);
+ });
+
+ this.WhenAnyValue(x => x.PwmStop)
+ .Subscribe(x =>
+ {
+ IsPwmDO0Enabled_PwmStop = x.HasFlag(PwmOutputs.PwmDO0);
+ IsPwmDO1Enabled_PwmStop = x.HasFlag(PwmOutputs.PwmDO1);
+ IsPwmDO2Enabled_PwmStop = x.HasFlag(PwmOutputs.PwmDO2);
+ IsPwmDO3Enabled_PwmStop = x.HasFlag(PwmOutputs.PwmDO3);
+ });
+
+ this.WhenAnyValue(x => x.StartCameras)
+ .Subscribe(x =>
+ {
+ IsCameraOutput0Enabled_StartCameras = x.HasFlag(CameraOutputs.CameraOutput0);
+ IsCameraOutput1Enabled_StartCameras = x.HasFlag(CameraOutputs.CameraOutput1);
+ });
+
+ this.WhenAnyValue(x => x.StopCameras)
+ .Subscribe(x =>
+ {
+ IsCameraOutput0Enabled_StopCameras = x.HasFlag(CameraOutputs.CameraOutput0);
+ IsCameraOutput1Enabled_StopCameras = x.HasFlag(CameraOutputs.CameraOutput1);
+ });
+
+ this.WhenAnyValue(x => x.EnableServos)
+ .Subscribe(x =>
+ {
+ IsServoOutput2Enabled_EnableServos = x.HasFlag(ServoOutputs.ServoOutput2);
+ IsServoOutput3Enabled_EnableServos = x.HasFlag(ServoOutputs.ServoOutput3);
+ });
+
+ this.WhenAnyValue(x => x.DisableServos)
+ .Subscribe(x =>
+ {
+ IsServoOutput2Enabled_DisableServos = x.HasFlag(ServoOutputs.ServoOutput2);
+ IsServoOutput3Enabled_DisableServos = x.HasFlag(ServoOutputs.ServoOutput3);
+ });
+
+ this.WhenAnyValue(x => x.EnableEncoders)
+ .Subscribe(x =>
+ {
+ IsEncoderPort2Enabled_EnableEncoders = x.HasFlag(EncoderInputs.EncoderPort2);
+ });
+
+ this.WhenAnyValue(x => x.Camera0Frame)
+ .Subscribe(x =>
+ {
+ IsFrameAcquiredEnabled_Camera0Frame = x.HasFlag(FrameAcquired.FrameAcquired);
+ });
+
+ this.WhenAnyValue(x => x.Camera1Frame)
+ .Subscribe(x =>
+ {
+ IsFrameAcquiredEnabled_Camera1Frame = x.HasFlag(FrameAcquired.FrameAcquired);
+ });
+
+ this.WhenAnyValue(x => x.EncoderReset)
+ .Subscribe(x =>
+ {
+ IsEncoderPort2Enabled_EncoderReset = x.HasFlag(EncoderInputs.EncoderPort2);
+ });
+
+ DO0SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDO0Set, canChangeConfig);
+ DO0ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDO0Clear, canChangeConfig);
+ DO1SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDO1Set, canChangeConfig);
+ DO1ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDO1Clear, canChangeConfig);
+ DO2SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDO2Set, canChangeConfig);
+ DO2ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDO2Clear, canChangeConfig);
+ DO3SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDO3Set, canChangeConfig);
+ DO3ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDO3Clear, canChangeConfig);
+
+ DOPort0SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDOPort0Set, canChangeConfig);
+ DOPort0ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDOPort0Clear, canChangeConfig);
+ DOPort1SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDOPort1Set, canChangeConfig);
+ DOPort1ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDOPort1Clear, canChangeConfig);
+ DOPort2SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDOPort2Set, canChangeConfig);
+ DOPort2ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDOPort2Clear, canChangeConfig);
+ SupplyPort0SetCommand = ReactiveCommand.CreateFromObservable(ExecuteSupplyPort0Set, canChangeConfig);
+ SupplyPort0ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteSupplyPort0Clear, canChangeConfig);
+ SupplyPort1SetCommand = ReactiveCommand.CreateFromObservable(ExecuteSupplyPort1Set, canChangeConfig);
+ SupplyPort1ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteSupplyPort1Clear, canChangeConfig);
+ SupplyPort2SetCommand = ReactiveCommand.CreateFromObservable(ExecuteSupplyPort2Set, canChangeConfig);
+ SupplyPort2ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteSupplyPort2Clear, canChangeConfig);
+ DIOPort0SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDIOPort0Set, canChangeConfig);
+ DIOPort0ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDIOPort0Clear, canChangeConfig);
+ DIOPort1SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDIOPort1Set, canChangeConfig);
+ DIOPort1ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDIOPort1Clear, canChangeConfig);
+ DIOPort2SetCommand = ReactiveCommand.CreateFromObservable(ExecuteDIOPort2Set, canChangeConfig);
+ DIOPort2ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteDIOPort2Clear, canChangeConfig);
+
+ Led0SetCommand = ReactiveCommand.CreateFromObservable(ExecuteLed0Set, canChangeConfig);
+ Led0ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteLed0Clear, canChangeConfig);
+ Led1SetCommand = ReactiveCommand.CreateFromObservable(ExecuteLed1Set, canChangeConfig);
+ Led1ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteLed1Clear, canChangeConfig);
+ Rgb0SetCommand = ReactiveCommand.CreateFromObservable(ExecuteRgb0Set, canChangeConfig);
+ Rgb0ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteRgb0Clear, canChangeConfig);
+ Rgb1SetCommand = ReactiveCommand.CreateFromObservable(ExecuteRgb1Set, canChangeConfig);
+ Rgb1ClearCommand = ReactiveCommand.CreateFromObservable(ExecuteRgb1Clear, canChangeConfig);
+
+ //Apply Buttons
+ PwmDO0StartCommand = ReactiveCommand.CreateFromObservable(ExecutePwmDO0Start, canChangeConfig);
+ PwmDO0StopCommand = ReactiveCommand.CreateFromObservable(ExecutePwmDO0Stop, canChangeConfig);
+ PwmDO1StartCommand = ReactiveCommand.CreateFromObservable(ExecutePwmDO1Start, canChangeConfig);
+ PwmDO1StopCommand = ReactiveCommand.CreateFromObservable(ExecutePwmDO1Stop, canChangeConfig);
+ PwmDO2StartCommand = ReactiveCommand.CreateFromObservable(ExecutePwmDO2Start, canChangeConfig);
+ PwmDO2StopCommand = ReactiveCommand.CreateFromObservable(ExecutePwmDO2Stop, canChangeConfig);
+ PwmDO3StartCommand = ReactiveCommand.CreateFromObservable(ExecutePwmDO3Start, canChangeConfig);
+ PwmDO3StopCommand = ReactiveCommand.CreateFromObservable(ExecutePwmDO3Stop, canChangeConfig);
+
+ ServoOutput2StartCommand = ReactiveCommand.CreateFromObservable(ExecuteServoOuput2Start, canChangeConfig);
+ ServoOutput2StopCommand = ReactiveCommand.CreateFromObservable(ExecuteServoOuput2Stop, canChangeConfig);
+ ServoOutput3StartCommand = ReactiveCommand.CreateFromObservable(ExecuteServoOuput3Start, canChangeConfig);
+ ServoOutput3StopCommand = ReactiveCommand.CreateFromObservable(ExecuteServoOuput3Stop, canChangeConfig);
+
+ Camera0StartCommand = ReactiveCommand.CreateFromObservable(ExecuteCamera0Start, canChangeConfig);
+ Camera0StopCommand = ReactiveCommand.CreateFromObservable(ExecuteCamera0Stop, canChangeConfig);
+ Camera1StartCommand = ReactiveCommand.CreateFromObservable(ExecuteCamera1Start, canChangeConfig);
+ Camera1StopCommand = ReactiveCommand.CreateFromObservable(ExecuteCamera1Stop, canChangeConfig);
+
+ CameraApplyConfigurationCommand =
+ ReactiveCommand.CreateFromObservable(ExecuteCameraApplyConfiguration, canChangeConfig);
+
+ ServoApplyConfigurationCommand =
+ ReactiveCommand.CreateFromObservable(ExecuteServoApplyConfiguration, canChangeConfig);
+
+ EncoderApplyConfigurationCommand =
+ ReactiveCommand.CreateFromObservable(ExecuteEncoderApplyConfiguration, canChangeConfig);
+
+ Rgb0ApplyConfigurationCommand = ReactiveCommand.CreateFromObservable(ExecuteRgb0ApplyConfiguration, canChangeConfig);
+ Rgb1ApplyConfigurationCommand = ReactiveCommand.CreateFromObservable(ExecuteRgb1ApplyConfiguration, canChangeConfig);
+
+ SerialTimestampApplyConfigurationCommand = ReactiveCommand.CreateFromObservable(ExecuteSerialTimestampApplyConfiguration, canChangeConfig);
+
+ SavePulseConfigDO0Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigDO0, canChangeConfig);
+ SavePulseConfigDO1Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigDO1, canChangeConfig);
+ SavePulseConfigDO2Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigDO2, canChangeConfig);
+ SavePulseConfigDO3Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigDO3, canChangeConfig);
+
+ SavePulseConfigDOPort0Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigDOPort0, canChangeConfig);
+ SavePulseConfigDOPort1Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigDOPort1, canChangeConfig);
+ SavePulseConfigDOPort2Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigDOPort2, canChangeConfig);
+ SavePulseConfigSupplyPort0Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigSupplyPort0, canChangeConfig);
+ SavePulseConfigSupplyPort1Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigSupplyPort1, canChangeConfig);
+ SavePulseConfigSupplyPort2Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigSupplyPort2, canChangeConfig);
+
+ SavePulseConfigRgb0Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigRgb0, canChangeConfig);
+ SavePulseConfigRgb1Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigRgb1, canChangeConfig);
+ SavePulseConfigLed0Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigLed0, canChangeConfig);
+ SavePulseConfigLed1Command = ReactiveCommand.CreateFromObservable(ExecuteSavePulseConfigLed1, canChangeConfig);
+
+ SaveDirectionConfigCommand = ReactiveCommand.CreateFromObservable(ExecuteSaveDirectionConfig, canChangeConfig);
+ SaveMimicConfigPort0IRCommand = ReactiveCommand.CreateFromObservable(ExecuteSaveMimicConfigPort0IR, canChangeConfig);
+ SaveMimicConfigPort1IRCommand = ReactiveCommand.CreateFromObservable(ExecuteSaveMimicConfigPort1IR, canChangeConfig);
+ SaveMimicConfigPort2IRCommand = ReactiveCommand.CreateFromObservable(ExecuteSaveMimicConfigPort2IR, canChangeConfig);
+ SaveMimicConfigPort0ValveCommand = ReactiveCommand.CreateFromObservable(ExecuteSaveMimicConfigPort0Valve, canChangeConfig);
+ SaveMimicConfigPort1ValveCommand = ReactiveCommand.CreateFromObservable(ExecuteSaveMimicConfigPort1Valve, canChangeConfig);
+ SaveMimicConfigPort2ValveCommand = ReactiveCommand.CreateFromObservable(ExecuteSaveMimicConfigPort2Valve, canChangeConfig);
+
+ SaveLedConfigCurrentLed0Command = ReactiveCommand.CreateFromObservable(ExecuteSaveLedConfigCurrentLed0, canChangeConfig);
+ SaveLedConfigCurrentLed1Command = ReactiveCommand.CreateFromObservable(ExecuteSaveLedConfigCurrentLed1, canChangeConfig);
+
+ SaveRgbConfigColorRgb0Command = ReactiveCommand.CreateFromObservable(ExecuteSaveRgbConfigColorRgb0, canChangeConfig);
+ SaveRgbConfigColorRgb1Command = ReactiveCommand.CreateFromObservable(ExecuteSaveRgbConfigColorRgb1, canChangeConfig);
+
+ Port0DirectionApplyCommand = ReactiveCommand.CreateFromObservable(ExecutePort0DirectionApply, canChangeConfig);
+ Port1DirectionApplyCommand = ReactiveCommand.CreateFromObservable(ExecutePort1DirectionApply, canChangeConfig);
+ Port2DirectionApplyCommand = ReactiveCommand.CreateFromObservable(ExecutePort2DirectionApply, canChangeConfig);
+
+ // force initial population of currently connected ports
+ LoadUsbInformation();
+ }
+
+ private IObservable ExecuteSavePulseConfigDO0()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDO0Async(value),
+ PulseDO0,
+ "PulseDO0");
+ });
+ }
+
+ private IObservable ExecuteSavePulseConfigDO1()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+ ushort pulseValue = 0;
+ pulseValue = PulseDO1;
+
+ // Enable
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+
+ // Duration
+ await WriteAndLogAsync(
+ value => _device.WritePulseDO1Async(value),
+ pulseValue,
+ "PulseDO1");
+ });
+ }
+
+ private IObservable ExecuteSavePulseConfigDO2()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDO2Async(value),
+ PulseDO2,
+ "PulseDO2");
+ });
+ }
+
+ private IObservable ExecuteSavePulseConfigDO3()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDO3Async(value),
+ PulseDO3,
+ "PulseDO3");
+ });
+ }
+
+ // Port 0 DO pulse configuration
+ private IObservable ExecuteSavePulseConfigDOPort0()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDOPort0Async(value),
+ PulseDOPort0,
+ "PulseDOPort0");
+ });
+ }
+
+ // Port 1 DO pulse configuration
+ private IObservable ExecuteSavePulseConfigDOPort1()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDOPort1Async(value),
+ PulseDOPort1,
+ "PulseDOPort1");
+ });
+ }
+
+ // Port 2 DO pulse configuration
+ private IObservable ExecuteSavePulseConfigDOPort2()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDOPort2Async(value),
+ PulseDOPort2,
+ "PulseDOPort2");
+ });
+ }
+
+ // Port 0 12V Supply pulse configuration
+ private IObservable ExecuteSavePulseConfigSupplyPort0()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseSupplyPort0Async(value),
+ PulseSupplyPort0,
+ "PulseSupplyPort0");
+ });
+ }
+
+ // Port 1 12V Supply pulse configuration
+ private IObservable ExecuteSavePulseConfigSupplyPort1()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseSupplyPort1Async(value),
+ PulseSupplyPort1,
+ "PulseSupplyPort1");
+ });
+ }
+
+ // Port 2 12V Supply pulse configuration
+ private IObservable ExecuteSavePulseConfigSupplyPort2()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseSupplyPort2Async(value),
+ PulseSupplyPort2,
+ "PulseSupplyPort2");
+ });
+ }
+
+ // RGB 0 pulse configuration
+ private IObservable ExecuteSavePulseConfigRgb0()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb0Async(value),
+ PulseRgb0,
+ "PulseRgb0");
+ });
+ }
+
+ // RGB 1 pulse configuration
+ private IObservable ExecuteSavePulseConfigRgb1()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb1Async(value),
+ PulseRgb1,
+ "PulseRgb1");
+ });
+ }
+
+ // LED 0 pulse configuration
+ private IObservable ExecuteSavePulseConfigLed0()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseLed0Async(value),
+ PulseLed0,
+ "PulseLed0");
+ });
+ }
+
+ // LED 1 pulse configuration
+ private IObservable ExecuteSavePulseConfigLed1()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseLed1Async(value),
+ PulseLed1,
+ "PulseLed1");
+ });
+ }
+
+ // Port DIO Direction configuration
+ private IObservable ExecuteSaveDirectionConfig()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WritePortDIODirectionAsync(value),
+ PortDIODirection,
+ "PortDIODirection");
+ });
+ }
+
+ // Port 0 IR Mimic configuration
+ private IObservable ExecuteSaveMimicConfigPort0IR()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort0IRAsync(value),
+ MimicPort0IR,
+ "MimicPort0IR");
+ });
+ }
+
+ // Port 1 IR Mimic configuration
+ private IObservable ExecuteSaveMimicConfigPort1IR()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort1IRAsync(value),
+ MimicPort1IR,
+ "MimicPort1IR");
+ });
+ }
+
+ // Port 2 IR Mimic configuration
+ private IObservable ExecuteSaveMimicConfigPort2IR()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort2IRAsync(value),
+ MimicPort2IR,
+ "MimicPort2IR");
+ });
+ }
+
+ // Port 0 Valve Mimic configuration
+ private IObservable ExecuteSaveMimicConfigPort0Valve()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort0ValveAsync(value),
+ MimicPort0Valve,
+ "MimicPort0Valve");
+ });
+ }
+
+ // Port 1 Valve Mimic configuration
+ private IObservable ExecuteSaveMimicConfigPort1Valve()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort1ValveAsync(value),
+ MimicPort1Valve,
+ "MimicPort1Valve");
+ });
+ }
+
+ // Port 2 Valve Mimic configuration
+ private IObservable ExecuteSaveMimicConfigPort2Valve()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort2ValveAsync(value),
+ MimicPort2Valve,
+ "MimicPort2Valve");
+ });
+ }
+
+ private IObservable ExecutePort0DirectionApply()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+ await WriteAndLogAsync(
+ value => _device.WritePortDIODirectionAsync(value),
+ PortDIODirection,
+ "PortDIODirection (Port0)");
+ });
+ }
+
+ private IObservable ExecutePort1DirectionApply()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+ await WriteAndLogAsync(
+ value => _device.WritePortDIODirectionAsync(value),
+ PortDIODirection,
+ "PortDIODirection (Port1)");
+ });
+ }
+
+ private IObservable ExecutePort2DirectionApply()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+ await WriteAndLogAsync(
+ value => _device.WritePortDIODirectionAsync(value),
+ PortDIODirection,
+ "PortDIODirection (Port2)");
+ });
+ }
+
+
+
+ // LED 0 Configuration
+ private IObservable ExecuteSaveLedConfigCurrentLed0()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ // Write LED current settings
+ await WriteAndLogAsync(
+ value => _device.WriteLed0CurrentAsync(value),
+ Led0Current,
+ "Led0Current");
+
+ await WriteAndLogAsync(
+ value => _device.WriteLed0MaxCurrentAsync(value),
+ Led0MaxCurrent,
+ "Led0MaxCurrent");
+
+ // Write pulse configuration
+ await WriteAndLogAsync(
+ value => _device.WritePulseLed0Async(value),
+ PulseLed0,
+ "PulseLed0");
+
+ // Write pulse enable state
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ });
+ }
+
+ // LED 1 Configuration
+ private IObservable ExecuteSaveLedConfigCurrentLed1()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ // Write LED current settings
+ await WriteAndLogAsync(
+ value => _device.WriteLed1CurrentAsync(value),
+ Led1Current,
+ "Led1Current");
+
+ await WriteAndLogAsync(
+ value => _device.WriteLed1MaxCurrentAsync(value),
+ Led1MaxCurrent,
+ "Led1MaxCurrent");
+
+ // Write pulse configuration
+ await WriteAndLogAsync(
+ value => _device.WritePulseLed1Async(value),
+ PulseLed1,
+ "PulseLed1");
+
+ // Write pulse enable state
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ });
+ }
+
+ // RGB 0 Configuration
+ private IObservable ExecuteSaveRgbConfigColorRgb0()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ // Update RGB values from adapter
+ var c0 = Rgb0Adapter.Color;
+ Rgb0 = new RgbPayload(c0.R, c0.G, c0.B);
+
+ // Write RGB color
+ await WriteAndLogAsync(
+ value => _device.WriteRgb0Async(value),
+ Rgb0,
+ "Rgb0");
+
+ // Write pulse configuration
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb0Async(value),
+ PulseRgb0,
+ "PulseRgb0");
+
+ // Write pulse enable state
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+
+ // Update RgbAll register to keep it in sync
+ var c1 = Rgb1Adapter.Color;
+ RgbAll = new RgbAllPayload(
+ c0.R, c0.G, c0.B,
+ c1.R, c1.G, c1.B);
+
+ await WriteAndLogAsync(
+ value => _device.WriteRgbAllAsync(value),
+ RgbAll,
+ "RgbAll");
+ });
+ }
+
+ // RGB 1 Configuration
+ private IObservable ExecuteSaveRgbConfigColorRgb1()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ // Update RGB values from adapter
+ var c1 = Rgb1Adapter.Color;
+ Rgb1 = new RgbPayload(c1.R, c1.G, c1.B);
+
+ // Write RGB color
+ await WriteAndLogAsync(
+ value => _device.WriteRgb1Async(value),
+ Rgb1,
+ "Rgb1");
+
+ // Write pulse configuration
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb1Async(value),
+ PulseRgb1,
+ "PulseRgb1");
+
+ // Write pulse enable state
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+
+ // Update RgbAll register to keep it in sync
+ var c0 = Rgb0Adapter.Color;
+ RgbAll = new RgbAllPayload(
+ c0.R, c0.G, c0.B,
+ c1.R, c1.G, c1.B);
+
+ await WriteAndLogAsync(
+ value => _device.WriteRgbAllAsync(value),
+ RgbAll,
+ "RgbAll");
+ });
+ }
+
+ private IObservable ExecuteSerialTimestampApplyConfiguration()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteEnableSerialTimestampAsync(value),
+ EnableSerialTimestamp,
+ "EnableSerialTimestamp");
+ });
+ }
+
+ private IObservable ExecuteRgb0ApplyConfiguration()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ await WriteAndLogAsync(
+ value => _device.WriteRgbAllAsync(value),
+ RgbAll,
+ "RgbAll");
+
+ var c0 = Rgb0Adapter.Color;
+ var c1 = Rgb1Adapter.Color;
+ RgbAll = new RgbAllPayload(
+ c0.R, c0.G, c0.B,
+ c1.R, c1.G, c1.B);
+
+ Rgb0 = new RgbPayload(c0.R, c0.G, c0.B);
+ Rgb1 = new RgbPayload(c1.R, c1.G, c1.B);
+ await WriteAndLogAsync(
+ value => _device.WriteRgb0Async(value),
+ Rgb0,
+ "Rgb0");
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb0Async(value),
+ PulseRgb0,
+ "PulseRgb0");
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.Rgb0,
+ "OutputSet");
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ OutputClear,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteRgb1ApplyConfiguration()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ // Update Rgb1 and RgbAll from adapter
+ var c0 = Rgb0Adapter.Color;
+ var c1 = Rgb1Adapter.Color;
+ RgbAll = new RgbAllPayload(
+ c0.R, c0.G, c0.B,
+ c1.R, c1.G, c1.B);
+
+ Rgb0 = new RgbPayload(c0.R, c0.G, c0.B);
+ Rgb1 = new RgbPayload(c1.R, c1.G, c1.B);
+
+ // Write Rgb1 and related registers
+ await WriteAndLogAsync(
+ value => _device.WriteRgb1Async(value),
+ Rgb1,
+ "Rgb1");
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb1Async(value),
+ PulseRgb1,
+ "PulseRgb1");
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.Rgb1,
+ "OutputSet");
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ OutputClear,
+ "OutputClear");
+ });
+ }
+ private IObservable ExecuteCameraApplyConfiguration(int cameraIndex)
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ switch (cameraIndex)
+ {
+ case 0: // Camera 0
+ await WriteAndLogAsync(
+ value => _device.WriteCamera0FrequencyAsync(value),
+ Camera0Frequency,
+ "Camera0Frequency");
+
+ await WriteAndLogAsync(
+ value => _device.WriteStartCamerasAsync(value),
+ StartCameras,
+ "StartCameras");
+
+ await WriteAndLogAsync(
+ value => _device.WriteStopCamerasAsync(value),
+ StopCameras,
+ "StopCameras");
+
+ break;
+
+ case 1: // Camera 1
+ await WriteAndLogAsync(
+ value => _device.WriteCamera0FrequencyAsync(value),
+ Camera1Frequency,
+ "Camera1Frequency");
+
+ await WriteAndLogAsync(
+ value => _device.WriteStartCamerasAsync(value),
+ StartCameras,
+ "StartCameras");
+
+ await WriteAndLogAsync(
+ value => _device.WriteStopCamerasAsync(value),
+ StopCameras,
+ "StopCameras");
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(cameraIndex), "Port0 index must be 0 (DO), 1 (12V), or 2 (DIO).");
+ }
+
+
+
+
+ });
+ }
+
+ private IObservable ExecuteServoApplyConfiguration(int servoIndex)
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ switch (servoIndex)
+ {
+ case 0: // Servo 2
+ await WriteAndLogAsync(
+ value => _device.WriteServoMotor2PeriodAsync(value),
+ ServoMotor2Period,
+ "ServoMotor2Period");
+
+ await WriteAndLogAsync(
+ value => _device.WriteServoMotor2PulseAsync(value),
+ ServoMotor2Pulse,
+ "ServoMotor2Pulse");
+
+ await WriteAndLogAsync(
+ value => _device.WriteEnableServosAsync(value),
+ EnableServos,
+ "EnableServos");
+ await WriteAndLogAsync(
+ value => _device.WriteDisableServosAsync(value),
+ DisableServos,
+ "DisableServos");
+
+ break;
+
+ case 1: // Servo 3
+ await WriteAndLogAsync(
+ value => _device.WriteServoMotor3PeriodAsync(value),
+ ServoMotor3Period,
+ "ServoMotor3Period");
+
+ await WriteAndLogAsync(
+ value => _device.WriteServoMotor3PulseAsync(value),
+ ServoMotor3Pulse,
+ "ServoMotor3Pulse");
+
+ await WriteAndLogAsync(
+ value => _device.WriteEnableServosAsync(value),
+ EnableServos,
+ "EnableServos");
+ await WriteAndLogAsync(
+ value => _device.WriteDisableServosAsync(value),
+ DisableServos,
+ "DisableServos");
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(servoIndex), "Port0 index must be 0 (DO), 1 (12V), or 2 (DIO).");
+ }
+
+
+
+
+ });
+ }
+
+ public IObservable ExecuteEncoderApplyConfiguration()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+ await WriteAndLogAsync(
+ value => _device.WriteEnableEncodersAsync(value),
+ EnableEncoders,
+ "EnableEncoders");
+ await WriteAndLogAsync(
+ value => _device.WriteEncoderModeAsync(value),
+ EncoderMode,
+ "EncoderMode");
+
+ await WriteAndLogAsync(
+ value => _device.WriteEncoderResetAsync(value),
+ EncoderReset,
+ "EncoderReset");
+
+ IsEncoderPort2Enabled_EncoderReset = false;
+ });
+
+ }
+
+ private IObservable ExecuteDO0Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDO0Enabled_OutputSet = true;
+ IsDO0Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.DO0,
+ "OutputSet");
+ });
+ }
+ private IObservable ExecuteDO0Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDO0Enabled_OutputSet = false;
+ IsDO0Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.DO0,
+ "OutputClear");
+ });
+
+ }
+
+ private IObservable ExecuteDO1Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDO1Enabled_OutputSet = true;
+ IsDO1Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.DO1,
+ "OutputSet");
+ });
+ }
+ private IObservable ExecuteDO1Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDO1Enabled_OutputSet = false;
+ IsDO1Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.DO1,
+ "OutputClear");
+ });
+
+ }
+
+ private IObservable ExecuteDO2Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDO2Enabled_OutputSet = true;
+ IsDO2Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.DO2,
+ "OutputSet");
+ });
+ }
+ private IObservable ExecuteDO2Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDO2Enabled_OutputSet = false;
+ IsDO2Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.DO2,
+ "OutputClear");
+ });
+
+ }
+
+ private IObservable ExecuteDO3Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDO3Enabled_OutputSet = true;
+ IsDO3Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.DO3,
+ "OutputSet");
+ });
+ }
+ private IObservable ExecuteDO3Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDO3Enabled_OutputSet = false;
+ IsDO3Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.DO3,
+ "OutputClear");
+ });
+
+ }
+
+ private IObservable ExecuteDOPort0Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDOPort0Enabled_OutputSet = true;
+ IsDOPort0Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.DOPort0,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteDOPort0Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDOPort0Enabled_OutputSet = false;
+ IsDOPort0Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.DOPort0,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteDOPort1Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDOPort1Enabled_OutputSet = true;
+ IsDOPort1Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.DOPort1,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteDOPort1Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDOPort1Enabled_OutputSet = false;
+ IsDOPort1Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.DOPort1,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteDOPort2Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDOPort2Enabled_OutputSet = true;
+ IsDOPort2Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.DOPort2,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteDOPort2Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDOPort2Enabled_OutputSet = false;
+ IsDOPort2Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.DOPort2,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteSupplyPort0Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsSupplyPort0Enabled_OutputSet = true;
+ IsSupplyPort0Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.SupplyPort0,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteSupplyPort0Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsSupplyPort0Enabled_OutputSet = false;
+ IsSupplyPort0Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.SupplyPort0,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteSupplyPort1Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsSupplyPort1Enabled_OutputSet = true;
+ IsSupplyPort1Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.SupplyPort1,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteSupplyPort1Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsSupplyPort1Enabled_OutputSet = false;
+ IsSupplyPort1Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.SupplyPort1,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteSupplyPort2Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsSupplyPort2Enabled_OutputSet = true;
+ IsSupplyPort2Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.SupplyPort2,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteSupplyPort2Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsSupplyPort2Enabled_OutputSet = false;
+ IsSupplyPort2Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.SupplyPort2,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteDIOPort0Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDIO0Enabled_PortDIOSet = true;
+ IsDIO0Enabled_PortDIOClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOSetAsync(value),
+ PortDigitalIOS.DIO0,
+ "PortDIOSet");
+ });
+ }
+
+ private IObservable ExecuteDIOPort0Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDIO0Enabled_PortDIOSet = false;
+ IsDIO0Enabled_PortDIOClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOClearAsync(value),
+ PortDigitalIOS.DIO0,
+ "PortDIOClear");
+ });
+ }
+
+ private IObservable ExecuteDIOPort1Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDIO1Enabled_PortDIOSet = true;
+ IsDIO1Enabled_PortDIOClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOSetAsync(value),
+ PortDigitalIOS.DIO1,
+ "PortDIOSet");
+ });
+ }
+
+ private IObservable ExecuteDIOPort1Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDIO1Enabled_PortDIOSet = false;
+ IsDIO1Enabled_PortDIOClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOClearAsync(value),
+ PortDigitalIOS.DIO1,
+ "PortDIOClear");
+ });
+ }
+
+ private IObservable ExecuteDIOPort2Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDIO2Enabled_PortDIOSet = true;
+ IsDIO2Enabled_PortDIOClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOSetAsync(value),
+ PortDigitalIOS.DIO2,
+ "PortDIOSet");
+ });
+ }
+
+ private IObservable ExecuteDIOPort2Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsDIO2Enabled_PortDIOSet = false;
+ IsDIO2Enabled_PortDIOClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOClearAsync(value),
+ PortDigitalIOS.DIO2,
+ "PortDIOClear");
+ });
+ }
+
+ private IObservable ExecuteLed0Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsLed0Enabled_OutputSet = true;
+ IsLed0Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.Led0,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteLed0Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsLed0Enabled_OutputSet = false;
+ IsLed0Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.Led0,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteLed1Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsLed1Enabled_OutputSet = true;
+ IsLed1Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.Led1,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteLed1Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsLed1Enabled_OutputSet = false;
+ IsLed1Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.Led1,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteRgb0Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsRgb0Enabled_OutputSet = true;
+ IsRgb0Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.Rgb0,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteRgb0Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsRgb0Enabled_OutputSet = false;
+ IsRgb0Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.Rgb0,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecuteRgb1Set()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsRgb1Enabled_OutputSet = true;
+ IsRgb1Enabled_OutputClear = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ DigitalOutputs.Rgb1,
+ "OutputSet");
+ });
+ }
+
+ private IObservable ExecuteRgb1Clear()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsRgb1Enabled_OutputSet = false;
+ IsRgb1Enabled_OutputClear = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ DigitalOutputs.Rgb1,
+ "OutputClear");
+ });
+ }
+
+ private IObservable ExecutePwmDO0Start()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsPwmDO0Enabled_PwmStart = true;
+ IsPwmDO0Enabled_PwmStop = false;
+
+ await WriteAndLogAsync(
+ value => _device.WritePwmFrequencyDO0Async(value),
+ PwmFrequencyDO0,
+ "PwmFrequencyDO0");
+ await WriteAndLogAsync(
+ value => _device.WritePwmDutyCycleDO0Async(value),
+ PwmDutyCycleDO0,
+ "PwmDutyCycleDO0");
+ await WriteAndLogAsync(
+ value => _device.WritePwmStartAsync(value),
+ PwmOutputs.PwmDO0,
+ "PwmStart");
+ });
+
+ }
+ private IObservable ExecutePwmDO0Stop()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsPwmDO0Enabled_PwmStart = false;
+ IsPwmDO0Enabled_PwmStop = true;
+
+ await WriteAndLogAsync(
+ value => _device.WritePwmStopAsync(value),
+ PwmOutputs.PwmDO0,
+ "PwmStop");
+ });
+
+ }
+ private IObservable ExecutePwmDO1Start()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsPwmDO1Enabled_PwmStart = true;
+ IsPwmDO1Enabled_PwmStop = false;
+
+ await WriteAndLogAsync(
+ value => _device.WritePwmFrequencyDO1Async(value),
+ PwmFrequencyDO1,
+ "PwmFrequencyDO1");
+ await WriteAndLogAsync(
+ value => _device.WritePwmDutyCycleDO1Async(value),
+ PwmDutyCycleDO1,
+ "PwmDutyCycleDO1");
+ await WriteAndLogAsync(
+ value => _device.WritePwmStartAsync(value),
+ PwmOutputs.PwmDO1,
+ "PwmStart");
+ });
+ }
+
+ private IObservable ExecutePwmDO1Stop()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsPwmDO1Enabled_PwmStart = false;
+ IsPwmDO1Enabled_PwmStop = true;
+
+ await WriteAndLogAsync(
+ value => _device.WritePwmStopAsync(value),
+ PwmOutputs.PwmDO1,
+ "PwmStop");
+ });
+ }
+
+ private IObservable ExecutePwmDO2Start()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsPwmDO2Enabled_PwmStart = true;
+ IsPwmDO2Enabled_PwmStop = false;
+
+ await WriteAndLogAsync(
+ value => _device.WritePwmFrequencyDO2Async(value),
+ PwmFrequencyDO2,
+ "PwmFrequencyDO2");
+ await WriteAndLogAsync(
+ value => _device.WritePwmDutyCycleDO2Async(value),
+ PwmDutyCycleDO2,
+ "PwmDutyCycleDO2");
+ await WriteAndLogAsync(
+ value => _device.WritePwmStartAsync(value),
+ PwmOutputs.PwmDO2,
+ "PwmStart");
+ });
+ }
+
+ private IObservable ExecutePwmDO2Stop()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsPwmDO2Enabled_PwmStart = false;
+ IsPwmDO2Enabled_PwmStop = true;
+
+ await WriteAndLogAsync(
+ value => _device.WritePwmStopAsync(value),
+ PwmOutputs.PwmDO2,
+ "PwmStop");
+ });
+ }
+
+ private IObservable ExecutePwmDO3Start()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsPwmDO3Enabled_PwmStart = true;
+ IsPwmDO3Enabled_PwmStop = false;
+
+ await WriteAndLogAsync(
+ value => _device.WritePwmFrequencyDO3Async(value),
+ PwmFrequencyDO3,
+ "PwmFrequencyDO3");
+ await WriteAndLogAsync(
+ value => _device.WritePwmDutyCycleDO3Async(value),
+ PwmDutyCycleDO3,
+ "PwmDutyCycleDO3");
+ await WriteAndLogAsync(
+ value => _device.WritePwmStartAsync(value),
+ PwmOutputs.PwmDO3,
+ "PwmStart");
+ });
+ }
+
+ private IObservable ExecutePwmDO3Stop()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsPwmDO3Enabled_PwmStart = false;
+ IsPwmDO3Enabled_PwmStop = true;
+
+ await WriteAndLogAsync(
+ value => _device.WritePwmStopAsync(value),
+ PwmOutputs.PwmDO3,
+ "PwmStop");
+ });
+ }
+
+ private IObservable ExecuteServoOuput2Start()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsServoOutput2Enabled_EnableServos = true;
+ IsServoOutput2Enabled_DisableServos = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteEnableServosAsync(value),
+ ServoOutputs.ServoOutput2,
+ "EnableServos");
+ });
+ }
+
+ private IObservable ExecuteServoOuput2Stop()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsServoOutput2Enabled_EnableServos = false;
+ IsServoOutput2Enabled_DisableServos = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteDisableServosAsync(value),
+ ServoOutputs.ServoOutput2,
+ "DisableServos");
+ });
+ }
+
+ private IObservable ExecuteServoOuput3Start()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsServoOutput3Enabled_EnableServos = true;
+ IsServoOutput3Enabled_DisableServos = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteEnableServosAsync(value),
+ ServoOutputs.ServoOutput3,
+ "EnableServos");
+ });
+ }
+
+ private IObservable ExecuteServoOuput3Stop()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsServoOutput3Enabled_EnableServos = false;
+ IsServoOutput3Enabled_DisableServos = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteDisableServosAsync(value),
+ ServoOutputs.ServoOutput3,
+ "DisableServos");
+ });
+ }
+
+ private IObservable ExecuteCamera0Start()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsCameraOutput0Enabled_StartCameras = true;
+ IsCameraOutput0Enabled_StopCameras = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteStartCamerasAsync(value),
+ CameraOutputs.CameraOutput0,
+ "StartCameras");
+ });
+ }
+
+ private IObservable ExecuteCamera0Stop()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsCameraOutput0Enabled_StartCameras = false;
+ IsCameraOutput0Enabled_StopCameras = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteStopCamerasAsync(value),
+ CameraOutputs.CameraOutput0,
+ "StopCameras");
+ });
+ }
+
+ private IObservable ExecuteCamera1Start()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsCameraOutput1Enabled_StartCameras = true;
+ IsCameraOutput1Enabled_StopCameras = false;
+
+ await WriteAndLogAsync(
+ value => _device.WriteStartCamerasAsync(value),
+ CameraOutputs.CameraOutput1,
+ "StartCameras");
+ });
+ }
+
+ private IObservable ExecuteCamera1Stop()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ return;
+
+ IsCameraOutput1Enabled_StartCameras = false;
+ IsCameraOutput1Enabled_StopCameras = true;
+
+ await WriteAndLogAsync(
+ value => _device.WriteStopCamerasAsync(value),
+ CameraOutputs.CameraOutput1,
+ "StopCameras");
+ });
+ }
+
+ private IObservable LoadUsbInformation()
+ {
+ return Observable.Start(() =>
+ {
+ var devices = SerialPort.GetPortNames();
+
+ if (OperatingSystem.IsMacOS())
+ // except with Bluetooth in the name
+ Ports = new ObservableCollection(devices.Where(d => d.Contains("cu.")).Except(devices.Where(d => d.Contains("Bluetooth"))));
+ else
+ Ports = new ObservableCollection(devices);
+
+ Console.WriteLine("Loaded USB information");
+ //Log.Information("Loaded USB information");
+ });
+ }
+
+ private async Task ConnectAndGetBaseInfo()
+ {
+ if (string.IsNullOrEmpty(SelectedPort))
+ throw new Exception("invalid parameter");
+
+ if (Connected)
+ {
+ _device?.Dispose();
+ _device = null;
+ Connected = false;
+ SentMessages.Clear();
+ return;
+ }
+
+ try
+ {
+ using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
+ _device = await Harp.Behavior.Device.CreateAsync(SelectedPort, cts.Token);
+ }
+ catch (OperationCanceledException ex)
+ {
+ Console.WriteLine($"Error connecting to device with error: {ex}");
+ //Log.Error(ex, "Error connecting to device with error: {Exception}", ex);
+ var messageBoxStandardWindow = MessageBoxManager
+ .GetMessageBoxStandard("Unexpected device found",
+ "Timeout when trying to connect to a device. Most likely not an Harp device.",
+ icon: Icon.Error);
+ await messageBoxStandardWindow.ShowAsync();
+ _device?.Dispose();
+ _device = null;
+ return;
+
+ }
+ catch (HarpException ex)
+ {
+ Console.WriteLine($"Error connecting to device with error: {ex}");
+ //Log.Error(ex, "Error connecting to device with error: {Exception}", ex);
+
+ var messageBoxStandardWindow = MessageBoxManager
+ .GetMessageBoxStandard("Unexpected device found",
+ ex.Message,
+ icon: Icon.Error);
+ await messageBoxStandardWindow.ShowAsync();
+
+ _device?.Dispose();
+ _device = null;
+ return;
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ Console.WriteLine($"COM port still in use and most likely not the expected Harp device");
+ var messageBoxStandardWindow = MessageBoxManager
+ .GetMessageBoxStandard("Unexpected device found",
+ $"COM port still in use and most likely not the expected Harp device.{Environment.NewLine}Specific error: {ex.Message}",
+ icon: Icon.Error);
+ await messageBoxStandardWindow.ShowAsync();
+
+ _device?.Dispose();
+ _device = null;
+ return;
+ }
+
+ // Clear the sent messages list
+ SentMessages.Clear();
+
+ //Log.Information("Attempting connection to port \'{SelectedPort}\'", SelectedPort);
+ Console.WriteLine($"Attempting connection to port \'{SelectedPort}\'");
+
+ DeviceID = await _device.ReadWhoAmIAsync();
+ DeviceName = await _device.ReadDeviceNameAsync();
+ HardwareVersion = await _device.ReadHardwareVersionAsync();
+ FirmwareVersion = await _device.ReadFirmwareVersionAsync();
+ try
+ {
+ // some devices may not have a serial number
+ SerialNumber = await _device.ReadSerialNumberAsync();
+ }
+ catch (HarpException)
+ {
+ // Device does not have a serial number, simply continue by ignoring the exception
+ }
+
+ DigitalInputState = await _device.ReadDigitalInputStateAsync();
+ OutputSet = await _device.ReadOutputSetAsync();
+ OutputClear = await _device.ReadOutputClearAsync();
+ OutputToggle = await _device.ReadOutputToggleAsync();
+ OutputState = await _device.ReadOutputStateAsync();
+ PortDIOSet = await _device.ReadPortDIOSetAsync();
+ PortDIOClear = await _device.ReadPortDIOClearAsync();
+ PortDIOToggle = await _device.ReadPortDIOToggleAsync();
+ PortDIOState = await _device.ReadPortDIOStateAsync();
+ PortDIODirection = await _device.ReadPortDIODirectionAsync();
+ PortDIOStateEvent = await _device.ReadPortDIOStateEventAsync();
+ AnalogData = await _device.ReadAnalogDataAsync();
+ OutputPulseEnable = await _device.ReadOutputPulseEnableAsync();
+ PulseDOPort0 = await _device.ReadPulseDOPort0Async();
+ PulseDOPort1 = await _device.ReadPulseDOPort1Async();
+ PulseDOPort2 = await _device.ReadPulseDOPort2Async();
+ PulseSupplyPort0 = await _device.ReadPulseSupplyPort0Async();
+ PulseSupplyPort1 = await _device.ReadPulseSupplyPort1Async();
+ PulseSupplyPort2 = await _device.ReadPulseSupplyPort2Async();
+ PulseLed0 = await _device.ReadPulseLed0Async();
+ PulseLed1 = await _device.ReadPulseLed1Async();
+ PulseRgb0 = await _device.ReadPulseRgb0Async();
+ PulseRgb1 = await _device.ReadPulseRgb1Async();
+ PulseDO0 = await _device.ReadPulseDO0Async();
+ PulseDO1 = await _device.ReadPulseDO1Async();
+ PulseDO2 = await _device.ReadPulseDO2Async();
+ PulseDO3 = await _device.ReadPulseDO3Async();
+ PwmFrequencyDO0 = await _device.ReadPwmFrequencyDO0Async();
+ PwmFrequencyDO1 = await _device.ReadPwmFrequencyDO1Async();
+ PwmFrequencyDO2 = await _device.ReadPwmFrequencyDO2Async();
+ PwmFrequencyDO3 = await _device.ReadPwmFrequencyDO3Async();
+ PwmDutyCycleDO0 = await _device.ReadPwmDutyCycleDO0Async();
+ PwmDutyCycleDO1 = await _device.ReadPwmDutyCycleDO1Async();
+ PwmDutyCycleDO2 = await _device.ReadPwmDutyCycleDO2Async();
+ PwmDutyCycleDO3 = await _device.ReadPwmDutyCycleDO3Async();
+ PwmStart = await _device.ReadPwmStartAsync();
+ PwmStop = await _device.ReadPwmStopAsync();
+ RgbAll = await _device.ReadRgbAllAsync();
+ Rgb0 = await _device.ReadRgb0Async();
+ Rgb1 = await _device.ReadRgb1Async();
+ Led0Current = await _device.ReadLed0CurrentAsync();
+ Led1Current = await _device.ReadLed1CurrentAsync();
+ Led0MaxCurrent = await _device.ReadLed0MaxCurrentAsync();
+ Led1MaxCurrent = await _device.ReadLed1MaxCurrentAsync();
+ EventEnable = await _device.ReadEventEnableAsync();
+ StartCameras = await _device.ReadStartCamerasAsync();
+ StopCameras = await _device.ReadStopCamerasAsync();
+ EnableServos = await _device.ReadEnableServosAsync();
+ DisableServos = await _device.ReadDisableServosAsync();
+ EnableEncoders = await _device.ReadEnableEncodersAsync();
+ EncoderMode = await _device.ReadEncoderModeAsync();
+ Camera0Frame = await _device.ReadCamera0FrameAsync();
+ Camera0Frequency = await _device.ReadCamera0FrequencyAsync();
+ Camera1Frame = await _device.ReadCamera1FrameAsync();
+ Camera1Frequency = await _device.ReadCamera1FrequencyAsync();
+ ServoMotor2Period = await _device.ReadServoMotor2PeriodAsync();
+ ServoMotor2Pulse = await _device.ReadServoMotor2PulseAsync();
+ ServoMotor3Period = await _device.ReadServoMotor3PeriodAsync();
+ ServoMotor3Pulse = await _device.ReadServoMotor3PulseAsync();
+ EncoderReset = await _device.ReadEncoderResetAsync();
+ EnableSerialTimestamp = await _device.ReadEnableSerialTimestampAsync();
+ MimicPort0IR = await _device.ReadMimicPort0IRAsync();
+ MimicPort1IR = await _device.ReadMimicPort1IRAsync();
+ MimicPort2IR = await _device.ReadMimicPort2IRAsync();
+ MimicPort0Valve = await _device.ReadMimicPort0ValveAsync();
+ MimicPort1Valve = await _device.ReadMimicPort1ValveAsync();
+ MimicPort2Valve = await _device.ReadMimicPort2ValveAsync();
+ PokeInputFilter = await _device.ReadPokeInputFilterAsync();
+
+ RgbAllAdapter.UpdateFromRegister(RgbAll);
+ Rgb0Adapter.UpdateFromRegister(Rgb0);
+ Rgb1Adapter.UpdateFromRegister(Rgb1);
+ Rgb0Adapter.LinkToParent(RgbAllAdapter, 0);
+ Rgb1Adapter.LinkToParent(RgbAllAdapter, 1);
+
+ // generate observable for the _deviceSync
+ _deviceEventsObservable = GenerateEventMessages();
+
+ Connected = true;
+
+ //Log.Information("Connected to device");
+ Console.WriteLine("Connected to device");
+ }
+
+ public IObservable GenerateEventMessages()
+ {
+ return Observable.Create(async (observer, cancellationToken) =>
+ {
+ // Loop until cancellation is requested or the device is no longer available.
+ while (!cancellationToken.IsCancellationRequested && _device != null)
+ {
+ // Capture local reference and check for null.
+ var device = _device;
+ if (device == null)
+ {
+ observer.OnCompleted();
+ break;
+ }
+
+ try
+ {
+ // Check if PortDI event is enabled
+ if (IsPortDIEnabled)
+ {
+ var result = await device.ReadPortDIOSetAsync(cancellationToken);
+ PortDIOSet = result;
+ observer.OnNext($"PortDI: {result}");
+ }
+
+ // Check if PortDIO event is enabled
+ if (IsPortDIOEnabled)
+ {
+ var result = await device.ReadPortDIOSetAsync(cancellationToken);
+ PortDIOSet = result;
+ observer.OnNext($"PortDIO: {result}");
+ }
+
+ // Check if AnalogData event is enabled
+ if (IsAnalogDataEnabled)
+ {
+ var result = await device.ReadAnalogDataAsync(cancellationToken);
+ AnalogData = result;
+ observer.OnNext($"AnalogData: {result}");
+ }
+
+ // Check if Camera0 event is enabled
+ if (IsCamera0Enabled)
+ {
+ var result = await device.ReadCamera0FrameAsync(cancellationToken);
+ // Update the corresponding property with the result
+ Camera0Frame = result;
+ observer.OnNext($"Camera0: {result}");
+ }
+
+ // Check if Camera1 event is enabled
+ if (IsCamera1Enabled)
+ {
+ var result = await device.ReadCamera1FrameAsync(cancellationToken);
+ Camera1Frame = result;
+ observer.OnNext($"Camera1: {result}");
+ }
+
+ var digitalInputStateResult = await device.ReadDigitalInputStateAsync(cancellationToken);
+ DigitalInputState = digitalInputStateResult;
+ observer.OnNext($"DigitalInputState: {digitalInputStateResult}");
+
+ var portDIOStateEventResult = await device.ReadPortDIOStateEventAsync(cancellationToken);
+ PortDIOStateEvent = portDIOStateEventResult;
+ observer.OnNext($"PortDIOStateEvent: {portDIOStateEventResult}");
+
+ // NOTE: These in the yaml are yet not considered events but write
+ var outputStateResult = await device.ReadOutputStateAsync(cancellationToken);
+ OutputState = outputStateResult;
+ observer.OnNext($"OutputState: {outputStateResult}");
+
+ // Wait a short while before polling again. Adjust delay as necessary.
+ await Task.Delay(TimeSpan.FromMilliseconds(10), cancellationToken);
+ }
+ catch (OperationCanceledException)
+ {
+ break;
+ }
+ catch (Exception ex)
+ {
+ observer.OnError(ex);
+ break;
+ }
+ }
+ observer.OnCompleted();
+ return Disposable.Empty;
+ });
+ }
+
+ private IObservable SaveConfiguration(bool savePermanently)
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device == null)
+ throw new Exception("You need to connect to the device first");
+
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOSetAsync(value),
+ PortDIOSet,
+ "PortDIOSet");
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOClearAsync(value),
+ PortDIOClear,
+ "PortDIOClear");
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOToggleAsync(value),
+ PortDIOToggle,
+ "PortDIOToggle");
+ await WriteAndLogAsync(
+ value => _device.WritePortDIOStateAsync(value),
+ PortDIOState,
+ "PortDIOState");
+ await WriteAndLogAsync(
+ value => _device.WritePortDIODirectionAsync(value),
+ PortDIODirection,
+ "PortDIODirection");
+ await WriteAndLogAsync(
+ value => _device.WriteOutputPulseEnableAsync(value),
+ OutputPulseEnable,
+ "OutputPulseEnable");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDOPort0Async(value),
+ PulseDOPort0,
+ "PulseDOPort0");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDOPort1Async(value),
+ PulseDOPort1,
+ "PulseDOPort1");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDOPort2Async(value),
+ PulseDOPort2,
+ "PulseDOPort2");
+ await WriteAndLogAsync(
+ value => _device.WritePulseSupplyPort0Async(value),
+ PulseSupplyPort0,
+ "PulseSupplyPort0");
+ await WriteAndLogAsync(
+ value => _device.WritePulseSupplyPort1Async(value),
+ PulseSupplyPort1,
+ "PulseSupplyPort1");
+ await WriteAndLogAsync(
+ value => _device.WritePulseSupplyPort2Async(value),
+ PulseSupplyPort2,
+ "PulseSupplyPort2");
+ await WriteAndLogAsync(
+ value => _device.WritePulseLed0Async(value),
+ PulseLed0,
+ "PulseLed0");
+ await WriteAndLogAsync(
+ value => _device.WritePulseLed1Async(value),
+ PulseLed1,
+ "PulseLed1");
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb0Async(value),
+ PulseRgb0,
+ "PulseRgb0");
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb1Async(value),
+ PulseRgb1,
+ "PulseRgb1");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDO0Async(value),
+ PulseDO0,
+ "PulseDO0");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDO1Async(value),
+ PulseDO1,
+ "PulseDO1");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDO2Async(value),
+ PulseDO2,
+ "PulseDO2");
+ await WriteAndLogAsync(
+ value => _device.WritePulseDO3Async(value),
+ PulseDO3,
+ "PulseDO3");
+ await WriteAndLogAsync(
+ value => _device.WritePwmFrequencyDO0Async(value),
+ PwmFrequencyDO0,
+ "PwmFrequencyDO0");
+ await WriteAndLogAsync(
+ value => _device.WritePwmFrequencyDO1Async(value),
+ PwmFrequencyDO1,
+ "PwmFrequencyDO1");
+ await WriteAndLogAsync(
+ value => _device.WritePwmFrequencyDO2Async(value),
+ PwmFrequencyDO2,
+ "PwmFrequencyDO2");
+ await WriteAndLogAsync(
+ value => _device.WritePwmFrequencyDO3Async(value),
+ PwmFrequencyDO3,
+ "PwmFrequencyDO3");
+ await WriteAndLogAsync(
+ value => _device.WritePwmDutyCycleDO0Async(value),
+ PwmDutyCycleDO0,
+ "PwmDutyCycleDO0");
+ await WriteAndLogAsync(
+ value => _device.WritePwmDutyCycleDO1Async(value),
+ PwmDutyCycleDO1,
+ "PwmDutyCycleDO1");
+ await WriteAndLogAsync(
+ value => _device.WritePwmDutyCycleDO2Async(value),
+ PwmDutyCycleDO2,
+ "PwmDutyCycleDO2");
+ await WriteAndLogAsync(
+ value => _device.WritePwmDutyCycleDO3Async(value),
+ PwmDutyCycleDO3,
+ "PwmDutyCycleDO3");
+ await WriteAndLogAsync(
+ value => _device.WritePwmStartAsync(value),
+ PwmStart,
+ "PwmStart");
+ await WriteAndLogAsync(
+ value => _device.WritePwmStopAsync(value),
+ PwmStop,
+ "PwmStop");
+
+
+ var c0 = Rgb0Adapter.Color;
+ var c1 = Rgb1Adapter.Color;
+ RgbAll = new RgbAllPayload(
+ c0.R, c0.G, c0.B,
+ c1.R, c1.G, c1.B);
+
+ Rgb0 = new RgbPayload(c0.R, c0.G, c0.B);
+ Rgb1 = new RgbPayload(c1.R, c1.G, c1.B);
+
+ await WriteAndLogAsync(
+ value => _device.WriteRgb0Async(value),
+ Rgb0,
+ "Rgb0");
+ await WriteAndLogAsync(
+ value => _device.WriteRgb1Async(value),
+ Rgb1,
+ "Rgb1");
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb0Async(value),
+ PulseRgb0,
+ "PulseRgb0");
+ await WriteAndLogAsync(
+ value => _device.WritePulseRgb1Async(value),
+ PulseRgb1,
+ "PulseRgb1");
+ await WriteAndLogAsync(
+ value => _device.WriteRgbAllAsync(value),
+ RgbAll,
+ "RgbAll");
+ await WriteAndLogAsync(
+ value => _device.WriteLed0CurrentAsync(value),
+ Led0Current,
+ "Led0Current");
+ await WriteAndLogAsync(
+ value => _device.WriteLed1CurrentAsync(value),
+ Led1Current,
+ "Led1Current");
+ await WriteAndLogAsync(
+ value => _device.WriteLed0MaxCurrentAsync(value),
+ Led0MaxCurrent,
+ "Led0MaxCurrent");
+ await WriteAndLogAsync(
+ value => _device.WriteLed1MaxCurrentAsync(value),
+ Led1MaxCurrent,
+ "Led1MaxCurrent");
+ await WriteAndLogAsync(
+ value => _device.WriteEventEnableAsync(value),
+ EventEnable,
+ "EventEnable");
+ await WriteAndLogAsync(
+ value => _device.WriteStartCamerasAsync(value),
+ StartCameras,
+ "StartCameras");
+ await WriteAndLogAsync(
+ value => _device.WriteEnableServosAsync(value),
+ EnableServos,
+ "EnableServos");
+ await WriteAndLogAsync(
+ value => _device.WriteDisableServosAsync(value),
+ DisableServos,
+ "DisableServos");
+ await WriteAndLogAsync(
+ value => _device.WriteEnableEncodersAsync(value),
+ EnableEncoders,
+ "EnableEncoders");
+ await WriteAndLogAsync(
+ value => _device.WriteEncoderModeAsync(value),
+ EncoderMode,
+ "EncoderMode");
+ await WriteAndLogAsync(
+ value => _device.WriteCamera0FrequencyAsync(value),
+ Camera0Frequency,
+ "Camera0Frequency");
+ await WriteAndLogAsync(
+ value => _device.WriteCamera1FrequencyAsync(value),
+ Camera1Frequency,
+ "Camera1Frequency");
+ await WriteAndLogAsync(
+ value => _device.WriteServoMotor2PeriodAsync(value),
+ ServoMotor2Period,
+ "ServoMotor2Period");
+ await WriteAndLogAsync(
+ value => _device.WriteServoMotor2PulseAsync(value),
+ ServoMotor2Pulse,
+ "ServoMotor2Pulse");
+ await WriteAndLogAsync(
+ value => _device.WriteServoMotor3PeriodAsync(value),
+ ServoMotor3Period,
+ "ServoMotor3Period");
+ await WriteAndLogAsync(
+ value => _device.WriteServoMotor3PulseAsync(value),
+ ServoMotor3Pulse,
+ "ServoMotor3Pulse");
+ await WriteAndLogAsync(
+ value => _device.WriteEncoderResetAsync(value),
+ EncoderReset,
+ "EncoderReset");
+ await WriteAndLogAsync(
+ value => _device.WriteEnableSerialTimestampAsync(value),
+ EnableSerialTimestamp,
+ "EnableSerialTimestamp");
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort0IRAsync(value),
+ MimicPort0IR,
+ "MimicPort0IR");
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort1IRAsync(value),
+ MimicPort1IR,
+ "MimicPort1IR");
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort2IRAsync(value),
+ MimicPort2IR,
+ "MimicPort2IR");
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort0ValveAsync(value),
+ MimicPort0Valve,
+ "MimicPort0Valve");
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort1ValveAsync(value),
+ MimicPort1Valve,
+ "MimicPort1Valve");
+ await WriteAndLogAsync(
+ value => _device.WriteMimicPort2ValveAsync(value),
+ MimicPort2Valve,
+ "MimicPort2Valve");
+ await WriteAndLogAsync(
+ value => _device.WritePokeInputFilterAsync(value),
+ PokeInputFilter,
+ "PokeInputFilter");
+ await WriteAndLogAsync(
+ value => _device.WriteOutputSetAsync(value),
+ OutputSet,
+ "OutputSet");
+ await WriteAndLogAsync(
+ value => _device.WriteOutputClearAsync(value),
+ OutputClear,
+ "OutputClear");
+
+ IsEncoderPort2Enabled_EncoderReset = false; // Uncheck after apply
+
+ // Save the configuration to the device permanently
+ if (savePermanently)
+ {
+ // To prevent multiple calls to the device while it is resetting
+ _deviceEventsSubscription?.Dispose();
+ _deviceEventsSubscription = null;
+
+ await WriteAndLogAsync(
+ value => _device.WriteResetDeviceAsync(value),
+ ResetFlags.Save,
+ "SavePermanently");
+
+ // Wait to ensure the device is ready after the reset
+ await Task.Delay(4000);
+
+ // Re-subscribe to the device events observable
+ SubscribeToEvents();
+ }
+ });
+ }
+
+ private IObservable ResetConfiguration()
+ {
+ return Observable.StartAsync(async () =>
+ {
+ if (_device != null)
+ {
+ await WriteAndLogAsync(
+ value => _device.WriteResetDeviceAsync(value),
+ ResetFlags.RestoreDefault,
+ "ResetDevice");
+ }
+ });
+ }
+
+ private async Task WriteAndLogAsync(Func writeFunc, T value, string registerName)
+ {
+ if (_device == null)
+ throw new Exception("Device is not connected");
+
+ await writeFunc(value);
+
+ // Log the message to the SentMessages collection on the UI thread
+ RxApp.MainThreadScheduler.Schedule(() =>
+ {
+ SentMessages.Add($"{DateTime.Now:HH:mm:ss.fff} - Write {registerName}: {value}");
+ });
+ }
+
+ private void SubscribeToEvents()
+ {
+ _deviceEventsSubscription = _deviceEventsObservable
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(
+ msg => HarpEvents.Add(msg.ToString()),
+ ex => Debug.WriteLine($"Error in device events: {ex}")
+ );
+ }
+
+}
diff --git a/App/Harp.Behavior.Design/ViewModels/ViewModelBase.cs b/App/Harp.Behavior.Design/ViewModels/ViewModelBase.cs
new file mode 100644
index 0000000..4da88a3
--- /dev/null
+++ b/App/Harp.Behavior.Design/ViewModels/ViewModelBase.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Reactive.Linq;
+using Avalonia;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Avalonia.Styling;
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+
+namespace Harp.Behavior.Design.ViewModels;
+
+public class ViewModelBase : ReactiveObject
+{
+ [Reactive] public bool IsDarkMode { get; set; }
+ [Reactive] public IBrush IconColor { get; set; }
+
+ public ViewModelBase()
+ {
+ // Get the current theme on Application.Current!.RequestedTheme
+ var currentTheme = Application.Current!.RequestedThemeVariant;
+ IsDarkMode = currentTheme == ThemeVariant.Dark ||
+ (currentTheme == ThemeVariant.Default && IsSystemInDarkMode());
+
+ // set initial color
+ IconColor = IsDarkMode ? Brushes.White : Brushes.Black;
+
+ // update icon color when IsDarkMode changes
+ this.WhenAnyValue(x => x.IsDarkMode)
+ .Subscribe(isDarkMode =>
+ {
+ IconColor = isDarkMode ? Brushes.White : Brushes.Black;
+ });
+
+ // Subscribe to changes in IsDarkMode
+ this.WhenAnyValue(x => x.IsDarkMode)
+ .Skip(1) // Skip the initial value to avoid unnecessary theme change on initialization
+ .Subscribe(isDarkMode => Application.Current.RequestedThemeVariant = isDarkMode ? ThemeVariant.Dark : ThemeVariant.Light);
+ }
+
+ private bool IsSystemInDarkMode()
+ {
+ // detect using avalonia if system is in dark mode
+ var colors = Application.Current!.PlatformSettings!.GetColorValues();
+ return colors.ThemeVariant == PlatformThemeVariant.Dark;
+ }
+}
diff --git a/App/Harp.Behavior.Design/Views/About.axaml b/App/Harp.Behavior.Design/Views/About.axaml
new file mode 100644
index 0000000..679d06d
--- /dev/null
+++ b/App/Harp.Behavior.Design/Views/About.axaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+ The Behavior GUI application allows to configure the Behavior device, developed by the Hardware and Software Platform at the Champalimaud Foundation.
+
+ The Behavior is a Harp device and has all the inherent Harp Devices functionalities.
+
+ The GUI was developed using [.NET](https://dotnet.microsoft.com/), [AvaloniaUI](https://avaloniaui.net/) with ReactiveUI and makes direct use of the [Bonsai.Harp](https://github.com/bonsai-rx/harp) library.
+
+ As with other Harp devices, the Behavior can also be used in [Bonsai](https://bonsai-rx.org/).
+
+
+
diff --git a/App/Harp.Behavior.Design/Views/About.axaml.cs b/App/Harp.Behavior.Design/Views/About.axaml.cs
new file mode 100644
index 0000000..6888a64
--- /dev/null
+++ b/App/Harp.Behavior.Design/Views/About.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace Harp.Behavior.Design.Views;
+
+public partial class About : Window
+{
+ public About()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/App/Harp.Behavior.Design/Views/MainWindow.axaml b/App/Harp.Behavior.Design/Views/MainWindow.axaml
new file mode 100644
index 0000000..d4edc32
--- /dev/null
+++ b/App/Harp.Behavior.Design/Views/MainWindow.axaml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/App/Harp.Behavior.Design/Views/MainWindow.axaml.cs b/App/Harp.Behavior.Design/Views/MainWindow.axaml.cs
new file mode 100644
index 0000000..c01d983
--- /dev/null
+++ b/App/Harp.Behavior.Design/Views/MainWindow.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace Harp.Behavior.Design.Views;
+
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/App/Harp.Behavior.Design/Views/MyDeviceView.axaml b/App/Harp.Behavior.Design/Views/MyDeviceView.axaml
new file mode 100644
index 0000000..d7eb063
--- /dev/null
+++ b/App/Harp.Behavior.Design/Views/MyDeviceView.axaml
@@ -0,0 +1,2055 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enable digital input port events
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+ Enable digital input/output port events
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+ Enable analog data acquisition events
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+ Enable Camera 0 frame acquisition events
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+ Enable Camera 1 frame acquisition events
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Digital Input 3 control and status
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Digital Output 0 control and status
+
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Start
+
+
+ Stop
+
+
+
+
+
+
+
+
+
+ Digital Output 1 control and status
+
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Start
+
+
+ Stop
+
+
+
+
+
+
+
+
+
+ Digital Output 2 control and status
+
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Start
+
+
+ Stop
+
+
+
+
+
+
+
+
+
+ Digital Output 3 control and status
+
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Start
+
+
+ Stop
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Port 0 digital output control/ Poke 0 LED
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+ Port 0 supply output control/ Poke 0 Valve
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DIO 0 state event status
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ False
+
+
+
+
+
+
+
+
+
+
+ Digital input port 0 status/ Poke 0 Infrared
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Port 1 digital output control/ Poke 1 LED
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+ Port 1 supply output control/ Poke 1 Valve
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DIO 1 state event status
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ False
+
+
+
+
+
+
+
+
+
+
+ Digital input port 1 status/ Poke 1 Infrared
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Port 2 digital output control/ Poke 2 LED
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+ Port 2 supply output control/ Poke 2 Valve
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DIO 2 state event status
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ False
+
+
+
+
+
+
+
+
+
+
+ Digital input port 2 status/ Poke 2 Infrared
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LED 0 current and pulse configuration
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LED 1 current and pulse configuration
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RGB 0 color and pulse configuration
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RGB 1 color and pulse configuration
+
+
+
+
+
+
+
+
+
+
+ Set
+
+
+ Clear
+
+
+
+
+
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Voltage reading from analog input 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Voltage reading from analog input 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Camera 0 control and status
+
+
+
+
+
+
+
+
+
+
+ Start
+
+
+ Stop
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Camera 1 control and status
+
+
+
+
+
+
+
+
+
+
+ Start
+
+
+ Stop
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Servo Motor 2 (DO2) control
+
+
+
+
+
+
+ Enable
+
+
+ Disable
+
+
+
+
+
+
+
+
+ Period of the servo motor in DO2, in microseconds (2-65534)
+
+
+
+
+
+
+
+ Pulse width of the servo motor in DO2, in microseconds (6-65530)
+
+
+
+
+
+
+
+
+
+
+
+
+ Servo Motor 3 (DO3) control
+
+
+
+
+
+
+ Enable
+
+
+ Disable
+
+
+
+
+
+
+
+
+ Period of the servo motor in DO3, in microseconds (2-65534)
+
+
+
+
+
+
+
+ Pulse width of the servo motor in DO3, in microseconds (6-65530)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Current encoder counter value
+
+
+
+
+
+
+
+
+
+
+ Enable encoder on Port 2
+
+
+ Port 2
+
+
+
+
+
+
+ Encoder operation mode
+
+
+
+
+
+
+
+
+
+
+ Reset encoder counter to zero
+
+
+ Reset
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EnableSerialTimestamp register value (0-255)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/Harp.Behavior.Design/Views/MyDeviceView.axaml.cs b/App/Harp.Behavior.Design/Views/MyDeviceView.axaml.cs
new file mode 100644
index 0000000..dfe1469
--- /dev/null
+++ b/App/Harp.Behavior.Design/Views/MyDeviceView.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace Harp.Behavior.Design.Views;
+
+public partial class BehaviorView : UserControl
+{
+ public BehaviorView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/App/Harp.Behavior.nsi b/App/Harp.Behavior.nsi
new file mode 100644
index 0000000..bc9186f
Binary files /dev/null and b/App/Harp.Behavior.nsi differ
diff --git a/App/LICENSE b/App/LICENSE
new file mode 100644
index 0000000..47539f7
--- /dev/null
+++ b/App/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021-2023 Hardware & Software Platform, Champalimaud Foundation
+
+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.
\ No newline at end of file
diff --git a/App/README.md b/App/README.md
new file mode 100644
index 0000000..f0d1f46
--- /dev/null
+++ b/App/README.md
@@ -0,0 +1,92 @@
+# Harp.Behavior.App
+
+This repository contains the Behavior GUI application for device configuration, developed by the Hardware & Software Platform at the Champalimaud Foundation.
+
+The Behavior is a Harp device with all the inherent functionalities of Harp devices.
+
+The GUI was developed using [.NET](https://dotnet.microsoft.com/), [AvaloniaUI](https://avaloniaui.net/) with ReactiveUI and makes direct use of the [Bonsai.Harp](https://github.com/bonsai-rx/harp) library.
+
+As with other Harp devices, the Behavior can also be used in [Bonsai](bonsai-rx.org/) using the [Harp.Behavior](https://github.com/harp-tech/device.behavior) package.
+
+## Installation
+
+Go to the Releases page to download the latest version for your Operating System.
+
+Currently there are x64 builds for Windows and Linux. Mac builds will be available in the future.
+
+Portable builds are also available.
+
+### Linux
+
+Since the application accesses the serial port, your user needs to be on the `dialout` group or equivalent.
+
+There might be other alternatives to this, but at least on Ubuntu and Fedora que command that you need to run to add your user to the `dialout` group is:
+
+```sh
+sudo usermod -a -G dialout
+```
+
+## For developers
+
+### Build Windows installer using NSIS manually
+
+- Install NSIS 3 on your Windows machine
+- Build and publish the application using the .NET 8 SDK command-line tools
+ ```
+ dotnet publish Harp.Behavior.App.sln -r win-x64 -f net8.0 --self-contained /p:Configuration=Release
+ ```
+- Run makesis to generate the installer
+ ```
+ makensis.exe /DVERSION_MAJOR=1 /DVERSION_MINOR=2 /DVERSION_BUILD=0 /DARCHITECTURE=win-x64 .\Harp.Behavior.nsi
+ // Adjust the version numbers and architecture as needed (e.g., arm64)
+ ```
+- The installer will be available at `.\bin\Release\net8.0\\Harp.Behavior.App.vx.x.x-.exe`
+
+### Build .app image for macOS
+
+The project uses dotnet-bundle (https://github.com/egramtel/dotnet-bundle) to generate a .app image for macOS.
+
+To build the .app image, run the following commands on the solution folder:
+
+```sh
+dotnet restore -r osx-x64 -p:TargetFramework=net8.0
+dotnet msbuild -t:BundleApp -p:RuntimeIdentifier=osx-x64 -property:Configuration=Release -p:UseAppHost=true -p:TargetFramework=net8.0
+```
+
+### Build tar.gz package for Linux (either in Linux or WSL)
+
+To build the tar.gz package, run the following commands on the solution folder:
+
+First you need to make sure that the [`dotnet-packaging`](https://github.com/quamotion/dotnet-packaging) tool is installed
+
+Run the following commands to install the tool:
+
+```sh
+dotnet tool install --global dotnet-tarball
+```
+
+Then run the following commands to build the tar.gz package:
+
+```sh
+dotnet restore -r linux-x64 -p:TargetFramework=net8.0
+dotnet msbuild -p:RuntimeIdentifier=linux-x64 -property:Configuration=Release -p:UseAppHost=true -p:TargetFramework=net8.0 /t:CreateTarball
+```
+
+## Roadmap
+
+See the [open issues](https://github.com/harp-tech/device.behavior/issues) for a list of proposed features (and known issues).
+
+## Contributing
+
+Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+## Authors
+
+Hardware & Software Platform of the Champalimaud Foundation.
\ No newline at end of file