diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6bb49f --- /dev/null +++ b/.gitignore @@ -0,0 +1,222 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* +*ncrunchsolution.user + +# 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 +# TODO: 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 + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# 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/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# 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 + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# nCrunch items +*.ncrunchsolution +*.DotSettings +*.ncrunchproject diff --git a/ASCOM.LynxAstro.DewController.Switch.Validation.txt b/ASCOM.LynxAstro.DewController.Switch.Validation.txt new file mode 100644 index 0000000..c87bb2f --- /dev/null +++ b/ASCOM.LynxAstro.DewController.Switch.Validation.txt @@ -0,0 +1,349 @@ +Conform Report Hash (V1): 5A431672269EA8E8AC10FDAB37C418D8AC06BDA9D013E7B57D8D46B4494A80B03209D002BC052DA6FDDC36320956A15ECA4AA1ED59B40D7BE27F35854C5B8CAF + + +ConformanceCheck ASCOM Device Conformance Checker Version 6.5.7500.22514, Build time: 14/07/2020 13:30:28 +ConformanceCheck Running on: ASCOM Platform 6.5 SP1 6.5.1.3234 + +ConformanceCheck Driver ProgID: ASCOM.LynxAstro.DewController.Switch + +Error handling +Error number for "Not Implemented" is: 80040400 +Error number for "Invalid Value 1" is: 80040405 +Error number for "Value Not Set 1" is: 80040402 +Error number for "Value Not Set 2" is: 80040403 +Error messages will not be interpreted to infer state. + +19:28:43.452 Driver Access Checks OK +19:28:44.135 AccessChecks OK Successfully created driver using late binding +19:28:44.176 AccessChecks OK Successfully connected using late binding +19:28:44.179 AccessChecks INFO The driver is a .NET object +19:28:44.183 AccessChecks INFO The AssemblyQualifiedName is: ASCOM.LynxAstro.DewController.Switch, ASCOM.LynxAstro.DewController.Switch, +19:28:44.186 AccessChecks INFO The driver implements interface: ASCOM.DeviceInterface.ISwitchV2 +19:28:44.850 AccessChecks INFO Device does not expose interface ISwitch +19:28:44.876 AccessChecks INFO Device exposes interface ISwitchV2 +19:28:45.546 AccessChecks OK Successfully created driver using driver access toolkit +19:28:45.574 AccessChecks OK Successfully connected using driver access toolkit + +Conform is using ASCOM.DriverAccess.Switch to get a Switch object +19:28:45.612 ConformanceCheck OK Driver instance created successfully +19:28:45.700 ConformanceCheck OK Connected OK + +Common Driver Methods +19:28:45.750 InterfaceVersion OK 2 +19:28:45.790 Connected OK True +19:28:45.836 Description OK ASCOM Switch Driver for LynxAstro.DewController +19:28:45.883 DriverInfo OK ASCOM Switch Driver for LynxAstro.DewController .net driver. Version: 6.5.1.0 +19:28:45.928 DriverVersion OK 6.5.1.0 +19:28:45.975 Name OK LynxAstro.DewController +19:28:46.022 CommandString INFO Conform cannot test the CommandString method +19:28:46.026 CommandBlind INFO Conform cannot test the CommandBlind method +19:28:46.031 CommandBool INFO Conform cannot test the CommandBool method +19:28:46.036 Action INFO Conform cannot test the Action method +19:28:46.041 SupportedActions OK Driver returned an empty action list + +Properties +19:28:46.198 MaxSwitch OK 4 + +Methods +19:28:46.314 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: CanWrite +19:28:46.320 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: CanWrite +19:28:46.326 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: GetSwitch +19:28:46.331 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: GetSwitch +19:28:46.337 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: GetSwitchDescription +19:28:46.343 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: GetSwitchDescription +19:28:46.350 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: GetSwitchName +19:28:46.356 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: GetSwitchName +19:28:46.362 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: GetSwitchValue +19:28:46.368 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: GetSwitchValue +19:28:46.374 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: MaxSwitchValue +19:28:46.380 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: MaxSwitchValue +19:28:46.387 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: MinSwitchValue +19:28:46.393 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: MinSwitchValue +19:28:46.400 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: SetSwitch +19:28:46.406 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: SetSwitch +19:28:46.414 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: SetSwitchValue +19:28:46.420 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: SetSwitchValue +19:28:46.427 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID below 0 was used in method: SwitchStep +19:28:46.434 SwitchNumber OK Switch device threw an InvalidOperationException when a switch ID above MaxSwitch was used in method: SwitchStep +19:28:49.553 GetSwitchName OK Found switch 0 +19:28:49.559 GetSwitchName OK Name: +19:28:49.566 GetSwitchDescription OK Description: Control Knob +19:28:49.572 MinSwitchValue OK Minimum: 0 +19:28:49.579 MaxSwitchValue OK Maximum: 1023 +19:28:49.586 SwitchStep OK Step size: 1 +19:28:49.593 SwitchStep OK Step size is greater than zero +19:28:49.600 SwitchStep OK Step size is less than the range of possible values +19:28:49.607 SwitchStep OK The switch range is an integer multiple of the step size. +19:28:49.615 CanWrite OK CanWrite: True +19:28:49.623 GetSwitch OK False +19:28:49.633 GetSwitchValue OK 0 +19:28:49.690 SetSwitch OK GetSwitch returned False after SetSwitch(False) +19:28:49.699 SetSwitch OK GetSwitchValue returned MINIMUM_VALUE after SetSwitch(False) +19:28:49.799 SetSwitch OK GetSwitch read True after SetSwitch(True) +19:28:49.809 SetSwitch OK GetSwitchValue returned MAXIMUM_VALUE after SetSwitch(True) +19:28:49.953 SetSwitchValue OK GetSwitch returned False after SetSwitchValue(MINIMUM_VALUE) +19:28:49.961 SetSwitchValue OK GetSwitchValue returned MINIMUM_VALUE after SetSwitchValue(MINIMUM_VALUE) +19:28:50.014 SetSwitchValue OK Switch threw an InvalidOperationException when a value below SwitchMinimum was set: -1 +19:28:50.155 SetSwitchValue OK GetSwitch returned True after SetSwitchValue(MAXIMUM_VALUE) +19:28:50.165 SetSwitchValue OK GetSwitchValue returned MAXIMUM_VALUE after SetSwitchValue(MAXIMUM_VALUE) +19:28:50.215 SetSwitchValue OK Switch threw an InvalidOperationException when a value above SwitchMaximum was set: 1024 +19:28:50.309 SetSwitchValue INFO Testing with steps that are 0% offset from integer SwitchStep values +19:28:50.402 SetSwitchValue Offset: 0% OK Set and read match: 0 +19:28:50.572 SetSwitchValue Offset: 0% OK Set and read match: 102 +19:28:50.743 SetSwitchValue Offset: 0% OK Set and read match: 205 +19:28:50.913 SetSwitchValue Offset: 0% OK Set and read match: 307 +19:28:51.083 SetSwitchValue Offset: 0% OK Set and read match: 409 +19:28:51.253 SetSwitchValue Offset: 0% OK Set and read match: 512 +19:28:51.423 SetSwitchValue Offset: 0% OK Set and read match: 614 +19:28:51.594 SetSwitchValue Offset: 0% OK Set and read match: 716 +19:28:51.764 SetSwitchValue Offset: 0% OK Set and read match: 818 +19:28:51.934 SetSwitchValue Offset: 0% OK Set and read match: 921 +19:28:52.105 SetSwitchValue Offset: 0% OK Set and read match: 1023 +19:28:52.226 SetSwitchValue INFO Testing with steps that are 25% offset from integer SwitchStep values +19:28:52.320 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 0.25, Read: 0 +19:28:52.491 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 102.25, Read: 102 +19:28:52.661 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 205.25, Read: 205 +19:28:52.831 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 307.25, Read: 307 +19:28:53.002 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 409.25, Read: 409 +19:28:53.172 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 512.25, Read: 512 +19:28:53.342 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 614.25, Read: 614 +19:28:53.512 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 716.25, Read: 716 +19:28:53.682 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 818.25, Read: 818 +19:28:53.853 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 921.25, Read: 921 +19:28:54.005 SetSwitchValue INFO Testing with steps that are 50% offset from integer SwitchStep values +19:28:54.100 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 0.5, Read: 1 +19:28:54.270 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 102.5, Read: 103 +19:28:54.440 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 205.5, Read: 206 +19:28:54.611 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 307.5, Read: 308 +19:28:54.781 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 409.5, Read: 410 +19:28:54.951 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 512.5, Read: 513 +19:28:55.121 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 614.5, Read: 615 +19:28:55.291 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 716.5, Read: 717 +19:28:55.462 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 818.5, Read: 819 +19:28:55.632 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 921.5, Read: 922 +19:28:55.785 SetSwitchValue INFO Testing with steps that are 75% offset from integer SwitchStep values +19:28:55.879 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 0.75, Read: 1 +19:28:56.050 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 102.75, Read: 103 +19:28:56.220 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 205.75, Read: 206 +19:28:56.390 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 307.75, Read: 308 +19:28:56.561 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 409.75, Read: 410 +19:28:56.731 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 512.75, Read: 513 +19:28:56.901 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 614.75, Read: 615 +19:28:57.071 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 716.75, Read: 717 +19:28:57.241 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 818.75, Read: 819 +19:28:57.396 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 921.75, Read: 922 +19:28:57.551 SetSwitchValue OK Switch has been reset to its original state + +19:28:57.692 GetSwitchName OK Found switch 1 +19:28:57.700 GetSwitchName OK Name: +19:28:57.710 GetSwitchDescription OK Description: Control Knob +19:28:57.719 MinSwitchValue OK Minimum: 0 +19:28:57.729 MaxSwitchValue OK Maximum: 1023 +19:28:57.738 SwitchStep OK Step size: 1 +19:28:57.747 SwitchStep OK Step size is greater than zero +19:28:57.756 SwitchStep OK Step size is less than the range of possible values +19:28:57.766 SwitchStep OK The switch range is an integer multiple of the step size. +19:28:57.775 CanWrite OK CanWrite: True +19:28:57.786 GetSwitch OK True +19:28:57.798 GetSwitchValue OK 470 +19:28:57.860 SetSwitch OK GetSwitch returned False after SetSwitch(False) +19:28:57.871 SetSwitch OK GetSwitchValue returned MINIMUM_VALUE after SetSwitch(False) +19:28:58.015 SetSwitch OK GetSwitch read True after SetSwitch(True) +19:28:58.030 SetSwitch OK GetSwitchValue returned MAXIMUM_VALUE after SetSwitch(True) +19:28:58.216 SetSwitchValue OK GetSwitch returned False after SetSwitchValue(MINIMUM_VALUE) +19:28:58.227 SetSwitchValue OK GetSwitchValue returned MINIMUM_VALUE after SetSwitchValue(MINIMUM_VALUE) +19:28:58.276 SetSwitchValue OK Switch threw an InvalidOperationException when a value below SwitchMinimum was set: -1 +19:28:58.417 SetSwitchValue OK GetSwitch returned True after SetSwitchValue(MAXIMUM_VALUE) +19:28:58.429 SetSwitchValue OK GetSwitchValue returned MAXIMUM_VALUE after SetSwitchValue(MAXIMUM_VALUE) +19:28:58.524 SetSwitchValue OK Switch threw an InvalidOperationException when a value above SwitchMaximum was set: 1024 +19:28:58.616 SetSwitchValue INFO Testing with steps that are 0% offset from integer SwitchStep values +19:28:58.711 SetSwitchValue Offset: 0% OK Set and read match: 0 +19:28:58.882 SetSwitchValue Offset: 0% OK Set and read match: 102 +19:28:59.052 SetSwitchValue Offset: 0% OK Set and read match: 205 +19:28:59.222 SetSwitchValue Offset: 0% OK Set and read match: 307 +19:28:59.392 SetSwitchValue Offset: 0% OK Set and read match: 409 +19:28:59.562 SetSwitchValue Offset: 0% OK Set and read match: 512 +19:28:59.732 SetSwitchValue Offset: 0% OK Set and read match: 614 +19:28:59.903 SetSwitchValue Offset: 0% OK Set and read match: 716 +19:29:00.057 SetSwitchValue Offset: 0% OK Set and read match: 818 +19:29:00.228 SetSwitchValue Offset: 0% OK Set and read match: 921 +19:29:00.398 SetSwitchValue Offset: 0% OK Set and read match: 1023 +19:29:00.520 SetSwitchValue INFO Testing with steps that are 25% offset from integer SwitchStep values +19:29:00.583 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 0.25, Read: 0 +19:29:00.754 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 102.25, Read: 102 +19:29:00.924 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 205.25, Read: 205 +19:29:01.094 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 307.25, Read: 307 +19:29:01.264 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 409.25, Read: 409 +19:29:01.435 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 512.25, Read: 512 +19:29:01.605 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 614.25, Read: 614 +19:29:01.775 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 716.25, Read: 716 +19:29:01.945 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 818.25, Read: 818 +19:29:02.116 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 921.25, Read: 921 +19:29:02.268 SetSwitchValue INFO Testing with steps that are 50% offset from integer SwitchStep values +19:29:02.363 SetSwitchValue Offset: 50% INFO Set/Read differ by >100% of SwitchStep. Set: 0.5, Read: 468 +19:29:02.533 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 102.5, Read: 103 +19:29:02.703 SetSwitchValue Offset: 50% INFO Set/Read differ by >100% of SwitchStep. Set: 205.5, Read: 475 +19:29:02.874 SetSwitchValue Offset: 50% INFO Set/Read differ by >100% of SwitchStep. Set: 307.5, Read: 469 +19:29:03.044 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 409.5, Read: 410 +19:29:03.214 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 512.5, Read: 513 +19:29:03.384 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 614.5, Read: 615 +19:29:03.554 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 716.5, Read: 717 +19:29:03.724 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 818.5, Read: 819 +19:29:03.895 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 921.5, Read: 922 +19:29:04.047 SetSwitchValue INFO Testing with steps that are 75% offset from integer SwitchStep values +19:29:04.142 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 0.75, Read: 1 +19:29:04.312 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 102.75, Read: 103 +19:29:04.467 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 205.75, Read: 206 +19:29:04.637 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 307.75, Read: 308 +19:29:04.808 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 409.75, Read: 410 +19:29:04.978 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 512.75, Read: 513 +19:29:05.148 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 614.75, Read: 615 +19:29:05.318 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 716.75, Read: 717 +19:29:05.488 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 818.75, Read: 819 +19:29:05.658 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 921.75, Read: 922 +19:29:05.814 SetSwitchValue OK Switch has been reset to its original state + +19:29:05.954 GetSwitchName OK Found switch 2 +19:29:05.963 GetSwitchName OK Name: +19:29:05.972 GetSwitchDescription OK Description: Control Knob +19:29:05.981 MinSwitchValue OK Minimum: 0 +19:29:05.991 MaxSwitchValue OK Maximum: 1023 +19:29:06.001 SwitchStep OK Step size: 1 +19:29:06.010 SwitchStep OK Step size is greater than zero +19:29:06.020 SwitchStep OK Step size is less than the range of possible values +19:29:06.029 SwitchStep OK The switch range is an integer multiple of the step size. +19:29:06.039 CanWrite OK CanWrite: True +19:29:06.051 GetSwitch OK False +19:29:06.061 GetSwitchValue OK 0 +19:29:06.122 SetSwitch OK GetSwitch returned False after SetSwitch(False) +19:29:06.133 SetSwitch OK GetSwitchValue returned MINIMUM_VALUE after SetSwitch(False) +19:29:06.231 SetSwitch OK GetSwitch read True after SetSwitch(True) +19:29:06.244 SetSwitch OK GetSwitchValue returned MAXIMUM_VALUE after SetSwitch(True) +19:29:06.431 SetSwitchValue OK GetSwitch returned False after SetSwitchValue(MINIMUM_VALUE) +19:29:06.442 SetSwitchValue OK GetSwitchValue returned MINIMUM_VALUE after SetSwitchValue(MINIMUM_VALUE) +19:29:06.492 SetSwitchValue OK Switch threw an InvalidOperationException when a value below SwitchMinimum was set: -1 +19:29:06.634 SetSwitchValue OK GetSwitch returned True after SetSwitchValue(MAXIMUM_VALUE) +19:29:06.646 SetSwitchValue OK GetSwitchValue returned MAXIMUM_VALUE after SetSwitchValue(MAXIMUM_VALUE) +19:29:06.740 SetSwitchValue OK Switch threw an InvalidOperationException when a value above SwitchMaximum was set: 1024 +19:29:06.832 SetSwitchValue INFO Testing with steps that are 0% offset from integer SwitchStep values +19:29:06.896 SetSwitchValue Offset: 0% OK Set and read match: 0 +19:29:07.066 SetSwitchValue Offset: 0% OK Set and read match: 102 +19:29:07.237 SetSwitchValue Offset: 0% OK Set and read match: 205 +19:29:07.407 SetSwitchValue Offset: 0% OK Set and read match: 307 +19:29:07.577 SetSwitchValue Offset: 0% OK Set and read match: 409 +19:29:07.747 SetSwitchValue Offset: 0% OK Set and read match: 512 +19:29:07.917 SetSwitchValue Offset: 0% OK Set and read match: 614 +19:29:08.088 SetSwitchValue Offset: 0% OK Set and read match: 716 +19:29:08.258 SetSwitchValue Offset: 0% OK Set and read match: 818 +19:29:08.428 SetSwitchValue Offset: 0% OK Set and read match: 921 +19:29:08.599 SetSwitchValue Offset: 0% OK Set and read match: 1023 +19:29:08.720 SetSwitchValue INFO Testing with steps that are 25% offset from integer SwitchStep values +19:29:08.814 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 0.25, Read: 0 +19:29:08.985 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 102.25, Read: 102 +19:29:09.155 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 205.25, Read: 205 +19:29:09.325 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 307.25, Read: 307 +19:29:09.481 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 409.25, Read: 409 +19:29:09.650 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 512.25, Read: 512 +19:29:09.821 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 614.25, Read: 614 +19:29:09.991 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 716.25, Read: 716 +19:29:10.161 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 818.25, Read: 818 +19:29:10.331 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 921.25, Read: 921 +19:29:10.484 SetSwitchValue INFO Testing with steps that are 50% offset from integer SwitchStep values +19:29:10.578 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 0.5, Read: 1 +19:29:10.749 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 102.5, Read: 103 +19:29:10.921 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 205.5, Read: 206 +19:29:11.059 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 307.5, Read: 308 +19:29:11.229 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 409.5, Read: 410 +19:29:11.400 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 512.5, Read: 513 +19:29:11.569 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 614.5, Read: 615 +19:29:11.739 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 716.5, Read: 717 +19:29:11.910 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 818.5, Read: 819 +19:29:12.080 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 921.5, Read: 922 +19:29:12.233 SetSwitchValue INFO Testing with steps that are 75% offset from integer SwitchStep values +19:29:12.327 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 0.75, Read: 1 +19:29:12.497 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 102.75, Read: 103 +19:29:12.668 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 205.75, Read: 206 +19:29:12.838 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 307.75, Read: 308 +19:29:13.008 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 409.75, Read: 410 +19:29:13.178 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 512.75, Read: 513 +19:29:13.349 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 614.75, Read: 615 +19:29:13.519 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 716.75, Read: 717 +19:29:13.689 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 818.75, Read: 819 +19:29:13.859 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 921.75, Read: 922 +19:29:14.014 SetSwitchValue OK Switch has been reset to its original state + +19:29:14.155 GetSwitchName OK Found switch 3 +19:29:14.165 GetSwitchName OK Name: +19:29:14.174 GetSwitchDescription OK Description: Control Knob +19:29:14.184 MinSwitchValue OK Minimum: 0 +19:29:14.194 MaxSwitchValue OK Maximum: 1023 +19:29:14.205 SwitchStep OK Step size: 1 +19:29:14.214 SwitchStep OK Step size is greater than zero +19:29:14.224 SwitchStep OK Step size is less than the range of possible values +19:29:14.233 SwitchStep OK The switch range is an integer multiple of the step size. +19:29:14.243 CanWrite OK CanWrite: True +19:29:14.254 GetSwitch OK False +19:29:14.265 GetSwitchValue OK 0 +19:29:14.385 SetSwitch OK GetSwitch returned False after SetSwitch(False) +19:29:14.396 SetSwitch OK GetSwitchValue returned MINIMUM_VALUE after SetSwitch(False) +19:29:14.494 SetSwitch OK GetSwitch read True after SetSwitch(True) +19:29:14.506 SetSwitch OK GetSwitchValue returned MAXIMUM_VALUE after SetSwitch(True) +19:29:14.694 SetSwitchValue OK GetSwitch returned False after SetSwitchValue(MINIMUM_VALUE) +19:29:14.707 SetSwitchValue OK GetSwitchValue returned MINIMUM_VALUE after SetSwitchValue(MINIMUM_VALUE) +19:29:14.802 SetSwitchValue OK Switch threw an InvalidOperationException when a value below SwitchMinimum was set: -1 +19:29:14.943 SetSwitchValue OK GetSwitch returned True after SetSwitchValue(MAXIMUM_VALUE) +19:29:14.954 SetSwitchValue OK GetSwitchValue returned MAXIMUM_VALUE after SetSwitchValue(MAXIMUM_VALUE) +19:29:15.049 SetSwitchValue OK Switch threw an InvalidOperationException when a value above SwitchMaximum was set: 1024 +19:29:15.142 SetSwitchValue INFO Testing with steps that are 0% offset from integer SwitchStep values +19:29:15.236 SetSwitchValue Offset: 0% OK Set and read match: 0 +19:29:15.407 SetSwitchValue Offset: 0% OK Set and read match: 102 +19:29:15.577 SetSwitchValue Offset: 0% OK Set and read match: 205 +19:29:15.747 SetSwitchValue Offset: 0% OK Set and read match: 307 +19:29:15.917 SetSwitchValue Offset: 0% OK Set and read match: 409 +19:29:16.087 SetSwitchValue Offset: 0% OK Set and read match: 512 +19:29:16.258 SetSwitchValue Offset: 0% OK Set and read match: 614 +19:29:16.428 SetSwitchValue Offset: 0% OK Set and read match: 716 +19:29:16.583 SetSwitchValue Offset: 0% OK Set and read match: 818 +19:29:16.753 SetSwitchValue Offset: 0% OK Set and read match: 921 +19:29:16.924 SetSwitchValue Offset: 0% OK Set and read match: 1023 +19:29:17.045 SetSwitchValue INFO Testing with steps that are 25% offset from integer SwitchStep values +19:29:17.139 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 0.25, Read: 0 +19:29:17.310 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 102.25, Read: 102 +19:29:17.480 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 205.25, Read: 205 +19:29:17.650 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 307.25, Read: 307 +19:29:17.821 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 409.25, Read: 409 +19:29:17.991 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 512.25, Read: 512 +19:29:18.161 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 614.25, Read: 614 +19:29:18.331 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 716.25, Read: 716 +19:29:18.501 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 818.25, Read: 818 +19:29:18.671 SetSwitchValue Offset: 25% INFO Set/Read differ by 20-30% of SwitchStep. Set: 921.25, Read: 921 +19:29:18.824 SetSwitchValue INFO Testing with steps that are 50% offset from integer SwitchStep values +19:29:18.918 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 0.5, Read: 1 +19:29:19.089 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 102.5, Read: 103 +19:29:19.260 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 205.5, Read: 206 +19:29:19.415 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 307.5, Read: 308 +19:29:19.584 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 409.5, Read: 410 +19:29:19.754 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 512.5, Read: 513 +19:29:19.925 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 614.5, Read: 615 +19:29:20.095 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 716.5, Read: 717 +19:29:20.265 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 818.5, Read: 819 +19:29:20.435 SetSwitchValue Offset: 50% INFO Set/Read differ by 40-50% of SwitchStep. Set: 921.5, Read: 922 +19:29:20.588 SetSwitchValue INFO Testing with steps that are 75% offset from integer SwitchStep values +19:29:20.682 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 0.75, Read: 1 +19:29:20.853 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 102.75, Read: 103 +19:29:21.023 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 205.75, Read: 206 +19:29:21.194 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 307.75, Read: 308 +19:29:21.364 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 409.75, Read: 410 +19:29:21.534 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 512.75, Read: 513 +19:29:21.704 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 614.75, Read: 615 +19:29:21.874 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 716.75, Read: 717 +19:29:22.045 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 818.75, Read: 819 +19:29:22.199 SetSwitchValue Offset: 75% INFO Set/Read differ by 20-30% of SwitchStep. Set: 921.75, Read: 922 +19:29:22.354 SetSwitchValue OK Switch has been reset to its original state + + +Conformance test complete + +No errors, warnings or issues found: your driver passes ASCOM validation!! + +Driver Hash Value: 2324CFC7AD89514CA3132538F5949BD837ABDF01A2CB27A7E8B4F11F6E34A60F1D0A054FDD9B71AAAEBDE250B552785A4FD3EE2521E13C2AFFDD32F423269B1E diff --git a/LynxAstro.DewController.Setup/AscomLocalServer.wxs b/LynxAstro.DewController.Setup/AscomLocalServer.wxs new file mode 100644 index 0000000..a6f9ca1 --- /dev/null +++ b/LynxAstro.DewController.Setup/AscomLocalServer.wxs @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Setup/AscomSwitchDriver.wxs b/LynxAstro.DewController.Setup/AscomSwitchDriver.wxs new file mode 100644 index 0000000..ef902a6 --- /dev/null +++ b/LynxAstro.DewController.Setup/AscomSwitchDriver.wxs @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Setup/Config.wxi b/LynxAstro.DewController.Setup/Config.wxi new file mode 100644 index 0000000..506f9c2 --- /dev/null +++ b/LynxAstro.DewController.Setup/Config.wxi @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Setup/Directories.wxs b/LynxAstro.DewController.Setup/Directories.wxs new file mode 100644 index 0000000..435128a --- /dev/null +++ b/LynxAstro.DewController.Setup/Directories.wxs @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Setup/FeatureTree.wxs b/LynxAstro.DewController.Setup/FeatureTree.wxs new file mode 100644 index 0000000..74a0983 --- /dev/null +++ b/LynxAstro.DewController.Setup/FeatureTree.wxs @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Setup/InstallationUI.wxs b/LynxAstro.DewController.Setup/InstallationUI.wxs new file mode 100644 index 0000000..a52ab86 --- /dev/null +++ b/LynxAstro.DewController.Setup/InstallationUI.wxs @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + "1"]]> + + 1 + + NOT Installed + Installed AND PATCH + + 1 + LicenseAccepted = "1" + + 1 + 1 + NOT WIXUI_DONTVALIDATEPATH + "1"]]> + WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" + 1 + 1 + + NOT Installed + Installed AND NOT PATCH + Installed AND PATCH + + 1 + + 1 + 1 + 1 + + + + + + + diff --git a/LynxAstro.DewController.Setup/License.rtf b/LynxAstro.DewController.Setup/License.rtf new file mode 100644 index 0000000..db6e68d Binary files /dev/null and b/LynxAstro.DewController.Setup/License.rtf differ diff --git a/LynxAstro.DewController.Setup/LynxAstro.DewController.Setup.wixproj b/LynxAstro.DewController.Setup/LynxAstro.DewController.Setup.wixproj new file mode 100644 index 0000000..0cd3350 --- /dev/null +++ b/LynxAstro.DewController.Setup/LynxAstro.DewController.Setup.wixproj @@ -0,0 +1,74 @@ + + + + Debug + x86 + 3.10 + {1073a462-d9a4-4c72-9c3f-a345b307153a} + 2.0 + LynxAstro.DewController.Setup + Package + + + bin\$(Configuration)\ + obj\$(Configuration)\ + Debug + + + bin\$(Configuration)\ + obj\$(Configuration)\ + + + + + + + + + + + + LynxAstro.DewController.Switch + {64308775-bd4a-469c-bcab-3ed830b811af} + True + True + Binaries;Content;Satellites + INSTALLFOLDER + + + LynxAstro.DewController + {c708e487-e3a9-4073-a545-294b88674225} + True + True + Binaries;Content;Satellites + INSTALLFOLDER + + + + + $(WixExtDir)\WixUIExtension.dll + WixUIExtension + + + $(WixExtDir)\WixNetFxExtension.dll + WixNetFxExtension + + + + + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Setup/Product.wxs b/LynxAstro.DewController.Setup/Product.wxs new file mode 100644 index 0000000..e2e47d8 --- /dev/null +++ b/LynxAstro.DewController.Setup/Product.wxs @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + = "6.4.1"]]> + + + + + + + + + + + + + + + + + diff --git a/LynxAstro.DewController.Switch.UnitTests/LynxAstro.DewController.Switch.UnitTests.csproj b/LynxAstro.DewController.Switch.UnitTests/LynxAstro.DewController.Switch.UnitTests.csproj new file mode 100644 index 0000000..399bd6d --- /dev/null +++ b/LynxAstro.DewController.Switch.UnitTests/LynxAstro.DewController.Switch.UnitTests.csproj @@ -0,0 +1,120 @@ + + + + + + Debug + AnyCPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F} + Library + Properties + LynxAstro.DewController.Switch.UnitTests + LynxAstro.DewController.Switch.UnitTests + v4.7.2 + 512 + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Astrometry.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Attributes.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Cache.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Controls.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.DeviceInterfaces.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.DriverAccess.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Exceptions.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Internal.Extensions.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.SettingsProvider.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Utilities.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Utilities.Video.dll + + + ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll + + + ..\packages\Moq.4.16.0\lib\net45\Moq.dll + + + ..\packages\NUnit.3.13.1\lib\net45\nunit.framework.dll + + + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + + {64308775-bd4a-469c-bcab-3ed830b811af} + LynxAstro.DewController.Switch + + + {c708e487-e3a9-4073-a545-294b88674225} + LynxAstro.DewController + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Switch.UnitTests/Properties/AssemblyInfo.cs b/LynxAstro.DewController.Switch.UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1573b92 --- /dev/null +++ b/LynxAstro.DewController.Switch.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LynxAstro.DewController.Switch.UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LynxAstro.DewController.Switch.UnitTests")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("afa5a4a4-188c-4180-b910-7c5ba7d5c11f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/LynxAstro.DewController.Switch.UnitTests/SwitchUnitTests.cs b/LynxAstro.DewController.Switch.UnitTests/SwitchUnitTests.cs new file mode 100644 index 0000000..12f4d80 --- /dev/null +++ b/LynxAstro.DewController.Switch.UnitTests/SwitchUnitTests.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using ASCOM.LynxAstro.DewController; +using Moq; +using NUnit.Framework; + +namespace LynxAstro.DewController.Switch.UnitTests +{ + [TestFixture] + public class SwitchUnitTests + { + private ASCOM.LynxAstro.DewController.Switch _switch; + private Mock _sharedResourcesWrapperMock; + + private ProfileProperties _profileProperties; + + [SetUp] + public void Setup() + { + _profileProperties = new ProfileProperties() + { + ComPort = "TestCom1", + SwitchNames = new List(), + TraceLogger = false + }; + + _sharedResourcesWrapperMock = new Mock(); + _sharedResourcesWrapperMock.Setup(x => x.ReadProfile()).Returns(() => _profileProperties); + + _switch = new ASCOM.LynxAstro.DewController.Switch(_sharedResourcesWrapperMock.Object); + } + + [Test] + public void CheckThatClassCreatedProperly() + { + Assert.That(_switch, Is.Not.Null); + } + } +} diff --git a/LynxAstro.DewController.Switch.UnitTests/packages.config b/LynxAstro.DewController.Switch.UnitTests/packages.config new file mode 100644 index 0000000..f485460 --- /dev/null +++ b/LynxAstro.DewController.Switch.UnitTests/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Switch/ASCOM.ico b/LynxAstro.DewController.Switch/ASCOM.ico new file mode 100644 index 0000000..9bf8f41 Binary files /dev/null and b/LynxAstro.DewController.Switch/ASCOM.ico differ diff --git a/LynxAstro.DewController.Switch/ASCOM.png b/LynxAstro.DewController.Switch/ASCOM.png new file mode 100644 index 0000000..a83b77b Binary files /dev/null and b/LynxAstro.DewController.Switch/ASCOM.png differ diff --git a/LynxAstro.DewController.Switch/ASCOMDriverTemplate.snk b/LynxAstro.DewController.Switch/ASCOMDriverTemplate.snk new file mode 100644 index 0000000..5ab2945 Binary files /dev/null and b/LynxAstro.DewController.Switch/ASCOMDriverTemplate.snk differ diff --git a/LynxAstro.DewController.Switch/LynxAstro.DewController.Switch.csproj b/LynxAstro.DewController.Switch/LynxAstro.DewController.Switch.csproj new file mode 100644 index 0000000..b888fe3 --- /dev/null +++ b/LynxAstro.DewController.Switch/LynxAstro.DewController.Switch.csproj @@ -0,0 +1,151 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {64308775-BD4A-469C-BCAB-3ED830B811AF} + Library + Properties + ASCOM.LynxAstro.DewController + ASCOM.LynxAstro.DewController.Switch + + + + + 3.5 + v4.7.2 + ASCOM.ico + true + ASCOMDriverTemplate.snk + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + AnyCPU + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AnyCPU + false + false + + + + + + + + + + + + + + + 3.5 + + + + + + + + + + + True + True + Resources.resx + + + True + True + Settings.settings + + + + + Designer + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + {c708e487-e3a9-4073-a545-294b88674225} + LynxAstro.DewController + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Switch/Properties/AssemblyInfo.cs b/LynxAstro.DewController.Switch/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0d50e61 --- /dev/null +++ b/LynxAstro.DewController.Switch/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +// TODO - Add your authorship information here +[assembly: AssemblyTitle("ASCOM.LynxAstro.DewController.Switch")] +[assembly: AssemblyDescription("ASCOM Switch driver for LynxAstro.DewController")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The ASCOM Initiative")] +[assembly: AssemblyProduct("ASCOM Switch driver for LynxAstro.DewController")] +[assembly: AssemblyCopyright("Copyright © 2021 The ASCOM Initiative")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(true)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a567b01c-a066-45ce-af3d-0192bad973ea")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +// +// TODO - Set your driver's version here +[assembly: AssemblyVersion("6.5.1.0")] +[assembly: AssemblyFileVersion("6.5.1.0")] diff --git a/LynxAstro.DewController.Switch/Properties/Resources.Designer.cs b/LynxAstro.DewController.Switch/Properties/Resources.Designer.cs new file mode 100644 index 0000000..4fb3684 --- /dev/null +++ b/LynxAstro.DewController.Switch/Properties/Resources.Designer.cs @@ -0,0 +1,96 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASCOM.LynxAstro.DewController.Properties +{ + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASCOM.LynxAstro.DewController.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ASCOM + { + get + { + object obj = ResourceManager.GetObject("ASCOM", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon DefaultIcon + { + get + { + object obj = ResourceManager.GetObject("DefaultIcon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + } +} diff --git a/LynxAstro.DewController.Switch/Properties/Resources.resx b/LynxAstro.DewController.Switch/Properties/Resources.resx new file mode 100644 index 0000000..e522d9e --- /dev/null +++ b/LynxAstro.DewController.Switch/Properties/Resources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\ASCOM.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\ASCOM.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/LynxAstro.DewController.Switch/Properties/Settings.Designer.cs b/LynxAstro.DewController.Switch/Properties/Settings.Designer.cs new file mode 100644 index 0000000..54a2225 --- /dev/null +++ b/LynxAstro.DewController.Switch/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASCOM.LynxAstro.DewController.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/LynxAstro.DewController.Switch/Properties/Settings.settings b/LynxAstro.DewController.Switch/Properties/Settings.settings new file mode 100644 index 0000000..8e615f2 --- /dev/null +++ b/LynxAstro.DewController.Switch/Properties/Settings.settings @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.Switch/ReadMe.htm b/LynxAstro.DewController.Switch/ReadMe.htm new file mode 100644 index 0000000..cbb8f37 --- /dev/null +++ b/LynxAstro.DewController.Switch/ReadMe.htm @@ -0,0 +1,147 @@ + + + + + Untitled Document + + + + + + + + + + + +
+

ASCOM Switch Driver (C#)

+
+



+

+

You have just created the skeleton of an ASCOM +Switch driver in C#. It produces an in-process +(assembly) based driver.

+
+

Prior to developing your first driver, please +familiarize yourself with the developer +information we've provided at the ASCOM Initiative web site +(internet required). +

+

You must do the following in order to complete +your implementation:

+
    +
  1. Switch to the Debug configuration + and build the template now. It should build without errors. +

    +
  2. Add a test project to the + solution. There are templates that can be used to add either a + console or a Windows Forms application:

    +
+
    +
  • Select the ASCOM + Test Forms App (CS) or ASCOM + Test Console App (CS) template.

    +
  • Set a name for the test + application and click on OK.

    +
  • In the Wizard: set the same device + type and model name as for the driver and select Create to build the + test project.

    +
  • Set the Test Application to Run at + Startup.

    +
  • Click on Debug and the test + application should run. You should be able to select your + application in the chooser. Selecting Properties should show the + default setup dialog for your driver.

    +
  • Trying to continue will generate + errors because the additional properties have not been implemented.

    +
+
    +
  1. Go through the Driver.cs file and + replace the System.NotImplemented exceptions with code to implement + your driver's functionality. See the ASCOM ISwitchV2 + spec. If a property or method is not implemented in your driver the + System.NotImplemented exception must be replaced by an + ASCOM.PropertyNotImplemented or ASCOM.MethodNotImplemented + exception.

    +
  2. Customize the Setup Dialog (SetupDialogForm) to provide the + settings and other controls for your driver. You can bind settings + directly to controls on your dialog form, there's no need to manage + settings manually. A custom Settings class takes care of managing + your settings behind the scenes. +

    +
+

Notes:

+
    +
  • Successfully building the driver, + as well as using regasm + on the assembly, registers it for both COM and ASCOM (the Chooser). + See the code in the ASCOM Registration region of Driver.vb. +

    +
  • Doing a Clean for the project, as + well doing a regasm + -u on the assembly, unregisters it for both COM and ASCOM + (the Chooser). +

    +
  • Place a breakpoint in your driver class constructor, then + start debugging (go, F5). Your breakpoint will be hit when the test + application creates an instance of your driver (after selecting it + in the Chooser). You can now single step, examine variables, etc. + Please review the test application and make changes and additions to + activate various parts of your driver during debugging.

    +
  • The project's Debug configuration is already configured (The + test application creates an instance of your driver (after selecting + it in the Chooser). You can now single step, examine variables, etc. + Please review the test application and feel free to make changes and + additions to activate various parts of your driver during debugging. +

    +
+
+ + + + + + + +
+ + + + + +
+

ASCOM Initiative

+
+
+



+

+
+

The ASCOM Initiative consists of a group of astronomy software + developers and instrument vendors whose goals are to promote the + driver/client model and scripting automation. +

+

See the ASCOM + web site for more information. Please participate in the + ASCOM-Talk + Groups.IO forum. +

+
+
+



+

+

+

+ + \ No newline at end of file diff --git a/LynxAstro.DewController.Switch/Resources/ASCOM.bmp b/LynxAstro.DewController.Switch/Resources/ASCOM.bmp new file mode 100644 index 0000000..55516c7 Binary files /dev/null and b/LynxAstro.DewController.Switch/Resources/ASCOM.bmp differ diff --git a/LynxAstro.DewController.Switch/Switch.cs b/LynxAstro.DewController.Switch/Switch.cs new file mode 100644 index 0000000..624101f --- /dev/null +++ b/LynxAstro.DewController.Switch/Switch.cs @@ -0,0 +1,695 @@ +#define Switch + +using System; +using System.Runtime.InteropServices; +using ASCOM.Utilities; +using ASCOM.DeviceInterface; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; + +namespace ASCOM.LynxAstro.DewController +{ + // + // Your driver's DeviceID is ASCOM.LynxAstro.DewController.Switch + // + // The Guid attribute sets the CLSID for ASCOM.LynxAstro.DewController.Switch + // The ClassInterface/None attribute prevents an empty interface called + // _LynxAstro.DewController from being created and used as the [default] interface + // + // TODO Replace the not implemented exceptions with code to implement the function or + // throw the appropriate ASCOM exception. + // + + /// + /// ASCOM Switch Driver for LynxAstro.DewController. + /// + [Guid("3a29744a-f33f-4843-a7e0-6938d9bd50ba")] + [ProgId("ASCOM.LynxAstro.DewController.Switch")] + [ServedClassName("LynxAstro.DewController")] + [ClassInterface(ClassInterfaceType.None)] + public class Switch : ISwitchV2 + { + /// + /// ASCOM DeviceID (COM ProgID) for this driver. + /// The DeviceID is used by ASCOM applications to load the driver at runtime. + /// + internal static string DriverId = Marshal.GenerateProgIdForType(MethodBase.GetCurrentMethod().DeclaringType ?? throw new System.InvalidOperationException()); + + /// + /// Driver description that displays in the ASCOM Chooser. + /// + private static string DriverDescription = "ASCOM Switch Driver for LynxAstro.DewController"; + + protected static string ComPort; // Variables to hold the currrent device configuration + protected static TraceLogger Tl; + + protected readonly ISharedResourcesWrapper SharedResourcesWrapper; + + /// + /// Initializes a new instance of the class. + /// Must be public for COM registration. + /// + /// + public Switch() + { + SharedResourcesWrapper = new SharedResourcesWrapper(); + + Initialise(nameof(Switch)); + } + + public Switch(ISharedResourcesWrapper sharedResourcesWrapper) + { + SharedResourcesWrapper = sharedResourcesWrapper; + + Initialise(nameof(Switch)); + } + + + protected void Initialise(string className) + { + Tl = new TraceLogger("", $"LynxAstro.DewController.{className}"); + + ReadProfile(); // Read device configuration from the ASCOM Profile store + + IsConnected = false; // Initialise connected to false + + LogMessage(className, "Completed initialisation"); + LogMessage(className, $"Driver version: {DriverVersion}"); + } + + // + // PUBLIC COM INTERFACE ISwitchV2 IMPLEMENTATION + // + + #region Common properties and methods. + + /// + /// Displays the Setup Dialog form. + /// If the user clicks the OK button to dismiss the form, then + /// the new settings are saved, otherwise the old values are reloaded. + /// THIS IS THE ONLY PLACE WHERE SHOWING USER INTERFACE IS ALLOWED! + /// + public void SetupDialog() + { + LogMessage("SetupDialog", "Opening setup dialog"); + SharedResourcesWrapper.SetupDialog(); + ReadProfile(); + LogMessage("SetupDialog", "complete"); + } + + public ArrayList SupportedActions + { + get + { + Tl.LogMessage("SupportedActions Get", "Returning empty arraylist"); + return new ArrayList(); + } + } + + public string Action(string actionName, string actionParameters) + { + LogMessage("", "Action {0}, parameters {1} not implemented", actionName, actionParameters); + throw new ASCOM.ActionNotImplementedException("Action " + actionName + " is not implemented by this driver"); + } + + public void CommandBlind(string command, bool raw) + { + CheckConnected("CommandBlind"); + SharedResourcesWrapper.SendBlind(command); + } + + public bool CommandBool(string command, bool raw) + { + CheckConnected("CommandBool"); + throw new ASCOM.MethodNotImplementedException("CommandBool"); + } + + public string CommandString(string command, bool raw) + { + CheckConnected("CommandString"); + return SharedResourcesWrapper.SendString(command); + } + + public void Dispose() + { + // Clean up the trace logger and util objects + Tl.Enabled = false; + Tl.Dispose(); + Tl = null; + } + + private string DecodeResult(string pattern, string encodedString) + { + var decodedString = encodedString.Substring(pattern.Length).TrimEnd('#'); + return decodedString; + } + + public bool Connected + { + get + { + LogMessage("Connected", "Get {0}", IsConnected); + return IsConnected; + } + set + { + LogMessage("Connected", "Set {0}", value); + if (value == IsConnected) + return; + + if (value) + { + try + { + ReadProfile(); + + LogMessage("Connected Set", "Connecting to port {0}", ComPort); + var connectionInfo = SharedResourcesWrapper.Connect("Serial", DriverId, Tl); + try + { + LogMessage("Connected Set", $"Connected to port {ComPort}. Version:{SharedResourcesWrapper.FirmwareVersion}"); + + IsConnected = true; + + LogMessage("Connected Set", $"Connected OK."); + + var maxSwitch = MaxSwitch; + } + catch (Exception) + { + SharedResourcesWrapper.Disconnect("Serial", DriverId); + throw; + } + } + catch (Exception ex) + { + LogMessage("Connected Set", "Error connecting to port {0} - {1}", ComPort, ex.Message); + } + } + else + { + LogMessage("Connected Set", "Disconnecting from port {0}", ComPort); + SharedResourcesWrapper.Disconnect("Serial", DriverId); + IsConnected = false; + } + } + } + + public string Description + { + get + { + Tl.LogMessage("Description Get", DriverDescription); + return DriverDescription; + } + } + + public string DriverInfo + { + get + { + string driverInfo = $"{Description} .net driver. Version: {DriverVersion}"; + LogMessage("DriverInfo Get", driverInfo); + return driverInfo; + } + } + + public string DriverVersion + { + get + { + Version version = Assembly.GetExecutingAssembly().GetName().Version; + string driverVersion = $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; + LogMessage("DriverVersion Get", driverVersion); + return driverVersion; + } + } + + public short InterfaceVersion + { + // set by the driver wizard + get + { + LogMessage("InterfaceVersion Get", "2"); + return Convert.ToInt16("2"); + } + } + + public string Name + { + get + { + string name = "LynxAstro.DewController"; + Tl.LogMessage("Name Get", name); + return name; + } + } + + #endregion + + #region ISwitchV2 Implementation + + private short numSwitch = 0; + private List switchNames = new List(); + + /// + /// The number of switches managed by this driver + /// + public short MaxSwitch + { + get + { + CheckConnected("MaxSwitch Get"); + + var result = SharedResourcesWrapper.SendString(":GD#"); + var decoded = DecodeResult(":GD", result); + + this.numSwitch = short.Parse(decoded); + + //Command: :GD# + //Purpose: Get the device type, i.e.the number of channels the dew controller has. + //Response: :GDX# where X is either 1 or 4 depending on the number of channels this device has + + Tl.LogMessage("MaxSwitch Get", numSwitch.ToString()); + return this.numSwitch; + } + } + + /// + /// Return the name of switch n + /// + /// The switch number to return + /// + /// The name of the switch + /// + public string GetSwitchName(short id) + { + Validate("GetSwitchName", id); + ReadProfile(); + + return switchNames.Count > id ? switchNames[id] : string.Empty; + } + + /// + /// Sets a switch name to a specified value + /// + /// The number of the switch whose name is to be set + /// The name of the switch + public void SetSwitchName(short id, string name) + { + Validate("SetSwitchName", id); + ReadProfile(); + + while (id > switchNames.Count) + switchNames.Add(string.Empty); + + switchNames[id] = name; + + WriteSwitchNames(); + } + + /// + /// Gets the switch description. + /// + /// The id. + /// + public string GetSwitchDescription(short id) + { + Validate("GetSwitchDescription", id); + return "Control Knob"; + } + + /// + /// Reports if the specified switch can be written to. + /// This is false if the switch cannot be written to, for example a limit switch or a sensor. + /// The default is true. + /// + /// The number of the switch whose write state is to be returned + /// true if the switch can be written to, otherwise false. + /// + /// If the method is not implemented + /// If id is outside the range 0 to MaxSwitch - 1 + public bool CanWrite(short id) + { + Validate("CanWrite", id); + // default behavour is to report true + Tl.LogMessage("CanWrite", string.Format("CanWrite({0}) - default true", id)); + return true; + // implementation should report the correct behaviour + //tl.LogMessage("CanWrite", string.Format("CanWrite({0}) - not implemented", id)); + //throw new MethodNotImplementedException("CanWrite"); + } + + #region boolean switch members + + /// + /// Return the state of switch n + /// a multi-value switch must throw a not implemented exception + /// + /// The switch number to return + /// + /// True or false + /// + public bool GetSwitch(short id) + { + var value = GetSwitchValue(id); + return value > 0; + } + + /// + /// Sets a switch to the specified state + /// If the switch cannot be set then throws a MethodNotImplementedException. + /// A multi-value switch must throw a not implemented exception + /// setting it to false will set it to its minimum value. + /// + /// + /// + public void SetSwitch(short id, bool state) + { + Validate("SetSwitch", id); + if (!CanWrite(id)) + { + var str = string.Format("SetSwitch({0}) - Cannot Write", id); + Tl.LogMessage("SetSwitch", str); + throw new MethodNotImplementedException(str); + } + SetSwitchValue(id, state ? 1023 : 0); + } + + #endregion + + #region analogue members + + /// + /// returns the maximum value for this switch + /// boolean switches must return 1.0 + /// + /// + /// + public double MaxSwitchValue(short id) + { + Validate("MaxSwitchValue", id); + // boolean switch implementation: + return 1023; + // or + //tl.LogMessage("MaxSwitchValue", string.Format("MaxSwitchValue({0}) - not implemented", id)); + //throw new MethodNotImplementedException("MaxSwitchValue"); + } + + /// + /// returns the minimum value for this switch + /// boolean switches must return 0.0 + /// + /// + /// + public double MinSwitchValue(short id) + { + Validate("MinSwitchValue", id); + // boolean switch implementation: + return 0; + // or + //tl.LogMessage("MinSwitchValue", string.Format("MinSwitchValue({0}) - not implemented", id)); + //throw new MethodNotImplementedException("MinSwitchValue"); + } + + /// + /// returns the step size that this switch supports. This gives the difference between + /// successive values of the switch. + /// The number of values is ((MaxSwitchValue - MinSwitchValue) / SwitchStep) + 1 + /// boolean switches must return 1.0, giving two states. + /// + /// + /// + public double SwitchStep(short id) + { + Validate("SwitchStep", id); + // boolean switch implementation: + return 1; + // or + //tl.LogMessage("SwitchStep", string.Format("SwitchStep({0}) - not implemented", id)); + //throw new MethodNotImplementedException("SwitchStep"); + } + + /// + /// returns the analogue switch value for switch id + /// boolean switches must throw a not implemented exception + /// + /// + /// + public double GetSwitchValue(short id) + { + Validate("GetSwitchValue", id); + Tl.LogMessage("GetSwitchValue", string.Format("GetSwitchValue({0}) - not implemented", id)); + + var channel = ChannelToLetter(id); + + var encoded = SharedResourcesWrapper.SendString($":GC{channel}#"); + var decoded = DecodeResult(":GC", encoded); + + return double.Parse(decoded.TrimStart(channel)); + //Command: :GCX# where X is the channel A, B, C or D to retrieve. + //Purpose: Get the current power setting for a specific channel. + //Response: :GCXVVVV# where X is the channel A, B, C or D returned and VVVV is the power level between 0 - 1023. + //Possible Errors + //:ER5# = Channel out of range, e.g. channel B on a 1 channel device. + } + + private char ChannelToLetter(short id) + { + UInt16 ord = (UInt16) 'A'; + ord += (ushort)id; + return (char)ord; + } + + /// + /// set the analogue value for this switch. + /// If the switch cannot be set then throws a MethodNotImplementedException. + /// If the value is not between the maximum and minimum then throws an InvalidValueException + /// boolean switches must throw a not implemented exception. + /// + /// + /// + public void SetSwitchValue(short id, double value) + { + Validate("SetSwitchValue", id, value); + if (!CanWrite(id)) + { + Tl.LogMessage("SetSwitchValue", string.Format("SetSwitchValue({0}) - Cannot write", id)); + throw new ASCOM.MethodNotImplementedException(string.Format("SetSwitchValue({0}) - Cannot write", id)); + } + //Tl.LogMessage("SetSwitchValue", string.Format("SetSwitchValue({0}) = {1} - not implemented", id, value)); + //throw new MethodNotImplementedException("SetSwitchValue"); + + + //Command: :SCXVVVV# where X is the channel A, B, C or D to set and VVVV is the power level + //between 0 - 1023.The power level must be 4 digits long so pad with leading zeros if necessary. + //Purpose: Set the current power setting for a specific channel. + //Response: :SC1# indicates success. Run a GA or GC command to verify. + //Possible Errors + //:ER4# = Not enough data received - make sure you zero pad the power level. + //:ER5# = Channel or power level out of range, e.g. channel B on a 1 channel device or power above 1023. + + var channel = ChannelToLetter(id); + + var encoded = SharedResourcesWrapper.SendString($":SC{channel}{value:0000}#"); + var decoded = DecodeResult(":SC", encoded); + + if (decoded != "1") + { + throw new InvalidValueException($"Unable to set switch {{id}} to value {value}"); + } + //return double.Parse(decoded.TrimStart(channel)); + } + + #endregion + #endregion + + #region private methods + + /// + /// Checks that the switch id is in range and throws an InvalidValueException if it isn't + /// + /// The message. + /// The id. + private void Validate(string message, short id) + { + if (id < 0 || id >= numSwitch) + { + Tl.LogMessage(message, string.Format("Switch {0} not available, range is 0 to {1}", id, numSwitch - 1)); + throw new InvalidValueException(message, id.ToString(), string.Format("0 to {0}", numSwitch - 1)); + } + } + + /// + /// Checks that the switch id and value are in range and throws an + /// InvalidValueException if they are not. + /// + /// The message. + /// The id. + /// The value. + private void Validate(string message, short id, double value) + { + Validate(message, id); + var min = MinSwitchValue(id); + var max = MaxSwitchValue(id); + if (value < min || value > max) + { + Tl.LogMessage(message, string.Format("Value {1} for Switch {0} is out of the allowed range {2} to {3}", id, value, min, max)); + throw new InvalidValueException(message, value.ToString(), string.Format("Switch({0}) range {1} to {2}", id, min, max)); + } + } + + /// + /// Checks that the number of states for the switch is correct and throws a methodNotImplemented exception if not. + /// Boolean switches must have 2 states and multi-value switches more than 2. + /// + /// + /// + /// + //private void Validate(string message, short id, bool expectBoolean) + //{ + // Validate(message, id); + // var ns = (int)(((MaxSwitchValue(id) - MinSwitchValue(id)) / SwitchStep(id)) + 1); + // if ((expectBoolean && ns != 2) || (!expectBoolean && ns <= 2)) + // { + // tl.LogMessage(message, string.Format("Switch {0} has the wriong number of states", id, ns)); + // throw new MethodNotImplementedException(string.Format("{0}({1})", message, id)); + // } + //} + + #endregion + + #region Private properties and methods + // here are some useful properties and methods that can be used as required + // to help with driver development + + #region ASCOM Registration + + // Register or unregister driver for ASCOM. This is harmless if already + // registered or unregistered. + // + /// + /// Register or unregister the driver with the ASCOM Platform. + /// This is harmless if the driver is already registered/unregistered. + /// + /// If true, registers the driver, otherwise unregisters it. + private static void RegUnregASCOM(bool bRegister) + { + using (var P = new ASCOM.Utilities.Profile()) + { + P.DeviceType = "Switch"; + if (bRegister) + { + P.Register(DriverId, DriverDescription); + } + else + { + P.Unregister(DriverId); + } + } + } + + /// + /// This function registers the driver with the ASCOM Chooser and + /// is called automatically whenever this class is registered for COM Interop. + /// + /// Type of the class being registered, not used. + /// + /// This method typically runs in two distinct situations: + /// + /// + /// In Visual Studio, when the project is successfully built. + /// For this to work correctly, the option Register for COM Interop + /// must be enabled in the project settings. + /// + /// During setup, when the installer registers the assembly for COM Interop. + /// + /// This technique should mean that it is never necessary to manually register a driver with ASCOM. + /// + [ComRegisterFunction] + public static void RegisterASCOM(Type t) + { + RegUnregASCOM(true); + } + + /// + /// This function unregisters the driver from the ASCOM Chooser and + /// is called automatically whenever this class is unregistered from COM Interop. + /// + /// Type of the class being registered, not used. + /// + /// This method typically runs in two distinct situations: + /// + /// + /// In Visual Studio, when the project is cleaned or prior to rebuilding. + /// For this to work correctly, the option Register for COM Interop + /// must be enabled in the project settings. + /// + /// During uninstall, when the installer unregisters the assembly from COM Interop. + /// + /// This technique should mean that it is never necessary to manually unregister a driver from ASCOM. + /// + [ComUnregisterFunction] + public static void UnregisterASCOM(Type t) + { + RegUnregASCOM(false); + } + + #endregion + + protected bool IsConnected { get; set; } + + /// + /// Use this function to throw an exception if we aren't connected to the hardware + /// + /// + private void CheckConnected(string message) + { + if (!IsConnected) + { + throw new ASCOM.NotConnectedException(message); + } + } + + /// + /// Read the device configuration from the ASCOM Profile store + /// + protected void ReadProfile() + { + var profileProperties = SharedResourcesWrapper.ReadProfile(); + Tl.Enabled = profileProperties.TraceLogger; + ComPort = profileProperties.ComPort; + + switchNames.Clear(); + switchNames.AddRange(profileProperties.SwitchNames); + + LogMessage("ReadProfile", $"Trace logger enabled: {Tl.Enabled}"); + LogMessage("ReadProfile", $"Com Port: {ComPort}"); + + } + + protected void WriteSwitchNames() + { + var profileProperties = SharedResourcesWrapper.ReadProfile(); + + profileProperties.SwitchNames.Clear(); + profileProperties.SwitchNames.AddRange(switchNames); + + SharedResourcesWrapper.WriteProfile(profileProperties); + } + + /// + /// Log helper function that takes formatted strings and arguments + /// + /// + /// + /// + internal void LogMessage(string identifier, string message, params object[] args) + { + var msg = string.Format(message, args); + Tl.LogMessage(identifier, msg); + } + #endregion + } +} diff --git a/LynxAstro.DewController.Switch/app.config b/LynxAstro.DewController.Switch/app.config new file mode 100644 index 0000000..c8fc52c --- /dev/null +++ b/LynxAstro.DewController.Switch/app.config @@ -0,0 +1,8 @@ + + + + +
+ + + diff --git a/LynxAstro.DewController.TestConsole/LynxAstro.DewController.TestConsole.csproj b/LynxAstro.DewController.TestConsole/LynxAstro.DewController.TestConsole.csproj new file mode 100644 index 0000000..905009c --- /dev/null +++ b/LynxAstro.DewController.TestConsole/LynxAstro.DewController.TestConsole.csproj @@ -0,0 +1,64 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {D5207217-61C7-4E94-8097-91DBACE57D2A} + Exe + Properties + ASCOM.LynxAstro.DewController + ASCOM.LynxAstro.DewController.TestConsole + v4.7.2 + + + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.TestConsole/Program.cs b/LynxAstro.DewController.TestConsole/Program.cs new file mode 100644 index 0000000..e7787a2 --- /dev/null +++ b/LynxAstro.DewController.TestConsole/Program.cs @@ -0,0 +1,78 @@ +// This implements a console application that can be used to test an ASCOM driver +// + +// This is used to define code in the template that is specific to one class implementation +// unused code can be deleted and this definition removed. + +#define Switch +// remove this to bypass the code that uses the chooser to select the driver +//#define UseChooser + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ASCOM +{ + class Program + { + static void Main(string[] args) + { + // Uncomment the code that's required +#if UseChooser + // choose the device + string id = ASCOM.DriverAccess.Switch.Choose(""); + if (string.IsNullOrEmpty(id)) + return; + // create this device + ASCOM.DriverAccess.Switch device = new ASCOM.DriverAccess.Switch(id); +#else + // this can be replaced by this code, it avoids the chooser and creates the driver class directly. + ASCOM.DriverAccess.Switch device = new ASCOM.DriverAccess.Switch("ASCOM.LynxAstro.DewController.Switch"); +#endif + // now run some tests, adding code to your driver so that the tests will pass. + // these first tests are common to all drivers. + Console.WriteLine("name " + device.Name); + Console.WriteLine("description " + device.Description); + Console.WriteLine("DriverInfo " + device.DriverInfo); + Console.WriteLine("driverVersion " + device.DriverVersion); + + // TODO add more code to test the driver. + device.Connected = true; + + Console.WriteLine("Number of switches: " + device.MaxSwitch); + + for (short i = 0; i < device.MaxSwitch; i++) + { + var min = device.MinSwitchValue(i); + var max = device.MaxSwitchValue(i); + var step = device.SwitchStep(i); + + var value = device.GetSwitchValue(i); + + Console.WriteLine($"switch {i}: {min} - {max} stepsize {step}: {value}"); + } + + for (short i = 0; i < device.MaxSwitch; i++) + { + device.SetSwitchValue(i, 500); + } + + for (short i = 0; i < device.MaxSwitch; i++) + { + var min = device.MinSwitchValue(i); + var max = device.MaxSwitchValue(i); + var step = device.SwitchStep(i); + + var value = device.GetSwitchValue(i); + + Console.WriteLine($"switch {i}: {min} - {max} stepsize {step}: {value}"); + } + + device.Connected = false; + Console.WriteLine("Press Enter to finish"); + Console.ReadLine(); + } + } +} diff --git a/LynxAstro.DewController.TestConsole/Properties/AssemblyInfo.cs b/LynxAstro.DewController.TestConsole/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3622a8c --- /dev/null +++ b/LynxAstro.DewController.TestConsole/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LynxAstro.DewController Test Application")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ASCOM Initiative")] +[assembly: AssemblyProduct("LynxAstro.DewController")] +[assembly: AssemblyCopyright("Copyright © ASCOM Initiative 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c7008f94-e3b9-4481-b720-3b56557860c6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("6.4.0.0")] +[assembly: AssemblyFileVersion("6.4.0.0")] diff --git a/LynxAstro.DewController.TestConsole/app.config b/LynxAstro.DewController.TestConsole/app.config new file mode 100644 index 0000000..5c22b42 --- /dev/null +++ b/LynxAstro.DewController.TestConsole/app.config @@ -0,0 +1,3 @@ + + + diff --git a/LynxAstro.DewController.UnitTests/LynxAstro.DewController.UnitTests.csproj b/LynxAstro.DewController.UnitTests/LynxAstro.DewController.UnitTests.csproj new file mode 100644 index 0000000..ac7edec --- /dev/null +++ b/LynxAstro.DewController.UnitTests/LynxAstro.DewController.UnitTests.csproj @@ -0,0 +1,115 @@ + + + + + + Debug + AnyCPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD} + Library + Properties + LynxAstro.DewController.UnitTests + LynxAstro.DewController.UnitTests + v4.7.2 + 512 + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Astrometry.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Attributes.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Cache.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Controls.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.DeviceInterfaces.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.DriverAccess.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Exceptions.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Internal.Extensions.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.SettingsProvider.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Utilities.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Utilities.Video.dll + + + ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll + + + ..\packages\Moq.4.16.0\lib\net45\Moq.dll + + + ..\packages\NUnit.3.13.1\lib\net45\nunit.framework.dll + + + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + + + + + {c708e487-e3a9-4073-a545-294b88674225} + LynxAstro.DewController + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.UnitTests/Properties/AssemblyInfo.cs b/LynxAstro.DewController.UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..27ecda4 --- /dev/null +++ b/LynxAstro.DewController.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LynxAstro.DewController.UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LynxAstro.DewController.UnitTests")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bded8ebb-7f9f-4710-8b74-59298c2dfcad")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/LynxAstro.DewController.UnitTests/SharedResourcesUnitTests.cs b/LynxAstro.DewController.UnitTests/SharedResourcesUnitTests.cs new file mode 100644 index 0000000..db1d8ea --- /dev/null +++ b/LynxAstro.DewController.UnitTests/SharedResourcesUnitTests.cs @@ -0,0 +1,177 @@ +using System; +using ASCOM.LynxAstro.DewController; +using ASCOM.Utilities.Interfaces; +using Moq; +using NUnit.Framework; + +namespace LynxAstro.DewController.UnitTests +{ + [TestFixture] + public class SharedResourcesUnitTests + { + private Mock _serialMock; + private Mock _traceLoggerMock; + + [SetUp] + public void Setup() + { + _serialMock = new Mock(); + _serialMock.SetupAllProperties(); + + _traceLoggerMock = new Mock(); + + SharedResources.SharedSerial = _serialMock.Object; + } + + [Test] + public void CheckThatSerialPortIsSetToUseMock() + { + Assert.That(SharedResources.SharedSerial, Is.EqualTo(_serialMock.Object)); + } + + [Test] + public void SendBlind_WhenCalled_Then_ClearsBuffersAndSendsMessage() + { + var expectedMessage = "Test"; + + SharedResources.SendBlind(expectedMessage); + + _serialMock.Verify(x => x.ClearBuffers(), Times.Once); + _serialMock.Verify(x => x.Transmit(expectedMessage), Times.Once); + } + + [Test] + public void SendChar_WhenCalled_ThenSendsMessageAndReadsExpectedNumberOfCharacters() + { + var expectedMessage = "Test"; + var expectedResult = "A"; + + _serialMock.Setup(x => x.ReceiveCounted(1)).Returns(expectedResult); + + var result = SharedResources.SendChar(expectedMessage); + + _serialMock.Verify(x => x.ClearBuffers(), Times.Once); + _serialMock.Verify(x => x.Transmit(expectedMessage), Times.Once); + _serialMock.Verify(x => x.ReceiveCounted(1), Times.Once); + Assert.That(result, Is.EqualTo(expectedResult)); + } + + [Test] + public void SendString_WhenCalled_ThenSendsMessageAndReadsResultUntilTerminatorFound() + { + var expectedMessage = "Test"; + var expectedResult = "TestMessage#"; + + _serialMock.Setup(x => x.ReceiveTerminated("#")).Returns(expectedResult); + + var result = SharedResources.SendString(expectedMessage); + + _serialMock.Verify(x => x.ClearBuffers(), Times.Once); + _serialMock.Verify(x => x.Transmit(expectedMessage), Times.Once); + _serialMock.Verify(x => x.ReceiveTerminated("#"), Times.Once); + Assert.That(result, Is.EqualTo(expectedResult.TrimEnd('#'))); + } + + [Test] + public void ReadTerminated_WhenCalled_ThenReadsResultUntilTerminatorFound() + { + var expectedResult = "TestMessage#"; + + _serialMock.Setup(x => x.ReceiveTerminated("#")).Returns(expectedResult); + + var result = SharedResources.ReadTerminated(); + + _serialMock.Verify(x => x.ReceiveTerminated("#"), Times.Once); + Assert.That(result, Is.EqualTo(expectedResult)); + } + + [Test] + public void ReadCharacters_WhenCalled_ThenReadsSpecificNumberOfCharacters() + { + var numberOfCharacters = 5; + + SharedResources.ReadCharacters(numberOfCharacters); + + _serialMock.Verify(x => x.ReceiveCounted(numberOfCharacters), Times.Once); + } + + [Test] + public void WriteProfile_WhenCalled_WritesExpectedProfileSettings() + { + string DriverId = "ASCOM.LynxAstro.DewController.Switch"; + + Mock profileWrapperMock = new Mock(); + profileWrapperMock.SetupAllProperties(); + + IProfileWrapper profeWrapper = profileWrapperMock.Object; + + Mock profileFactoryMock = new Mock(); + profileFactoryMock.Setup(x => x.Create()).Returns(profileWrapperMock.Object); + + SharedResources.ProfileFactory = profileFactoryMock.Object; + + var profileProperties = new ProfileProperties + { + TraceLogger = false, + ComPort = "TestComPort" + }; + + SharedResources.WriteProfile(profileProperties); + + Assert.That(profeWrapper.DeviceType, Is.EqualTo("Switch")); + profileWrapperMock.Verify(x => x.WriteValue(DriverId, "Trace Level", profileProperties.TraceLogger.ToString()), Times.Once); + profileWrapperMock.Verify(x => x.WriteValue(DriverId, "COM Port", profileProperties.ComPort), Times.Once); + } + + [Test] + public void ReadProfile_WhenCalled_ReturnsExpectedDefaultValues() + { + string DriverId = "ASCOM.LynxAstro.DewController.Switch"; + + string ComPortDefault = "COM1"; + string TraceStateDefault = "false"; + string GuideRateProfileNameDefault = "10.077939"; //67% of sidereal rate + string PrecisionDefault = "Unchanged"; + string GuidingStyleDefault = "Auto"; + string BacklashCompensationDefault = "3000"; + string ReverseFocuserDiectionDefault = "true"; + + Mock profileWrapperMock = new Mock(); + profileWrapperMock.SetupAllProperties(); + + profileWrapperMock.Setup(x => x.DeviceType).Returns("Switch"); + + profileWrapperMock.Setup(x => x.GetValue(DriverId, "Trace Level", string.Empty, TraceStateDefault)) + .Returns(() => + TraceStateDefault); + profileWrapperMock.Setup(x => x.GetValue(DriverId, "COM Port", string.Empty, ComPortDefault)) + .Returns(ComPortDefault); + + profileWrapperMock.Setup(x => + x.GetValue(DriverId, It.IsRegex("^SwitchName_[0-9{1}]$"), string.Empty, It.IsAny())) + .Returns((string driverId, string name, string subKey, string defaultValue) => defaultValue); + + IProfileWrapper profeWrapper = profileWrapperMock.Object; + + Mock profileFactoryMock = new Mock(); + profileFactoryMock.Setup(x => x.Create()).Returns(profileWrapperMock.Object); + + SharedResources.ProfileFactory = profileFactoryMock.Object; + + var profileProperties = SharedResources.ReadProfile(); + + Assert.That(profeWrapper.DeviceType, Is.EqualTo("Switch")); + Assert.That(profileProperties.ComPort, Is.EqualTo(ComPortDefault)); + Assert.That(profileProperties.TraceLogger, Is.EqualTo(bool.Parse(TraceStateDefault))); + } + + [TestCase("TCP")] + [TestCase("Carrier Pigeon")] + public void Connect_WhenDeviceIdIsNotSerial_ThenThrowsException(string deviceId) + { + var result = Assert.Throws(() => { SharedResources.Connect(deviceId, string.Empty, _traceLoggerMock.Object); }); + + Assert.That(result.Message, Is.EqualTo($"deviceId {deviceId} not currently supported")); + } + } +} diff --git a/LynxAstro.DewController.UnitTests/packages.config b/LynxAstro.DewController.UnitTests/packages.config new file mode 100644 index 0000000..f485460 --- /dev/null +++ b/LynxAstro.DewController.UnitTests/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController.sln b/LynxAstro.DewController.sln new file mode 100644 index 0000000..320cb71 --- /dev/null +++ b/LynxAstro.DewController.sln @@ -0,0 +1,89 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29905.134 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LynxAstro.DewController", "LynxAstro.DewController\LynxAstro.DewController.csproj", "{C708E487-E3A9-4073-A545-294B88674225}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LynxAstro.DewController.Switch", "LynxAstro.DewController.Switch\LynxAstro.DewController.Switch.csproj", "{64308775-BD4A-469C-BCAB-3ED830B811AF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{7CAA050A-2EB3-4A21-8A75-131EB0AC9778}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LynxAstro.DewController.UnitTests", "LynxAstro.DewController.UnitTests\LynxAstro.DewController.UnitTests.csproj", "{BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LynxAstro.DewController.Switch.UnitTests", "LynxAstro.DewController.Switch.UnitTests\LynxAstro.DewController.Switch.UnitTests.csproj", "{AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}" +EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "LynxAstro.DewController.Setup", "LynxAstro.DewController.Setup\LynxAstro.DewController.Setup.wixproj", "{1073A462-D9A4-4C72-9C3F-A345B307153A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test Console", "Test Console", "{9B4388AE-0FB9-4C65-9C3F-000CE932E110}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LynxAstro.DewController.TestConsole", "LynxAstro.DewController.TestConsole\LynxAstro.DewController.TestConsole.csproj", "{D5207217-61C7-4E94-8097-91DBACE57D2A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C708E487-E3A9-4073-A545-294B88674225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C708E487-E3A9-4073-A545-294B88674225}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C708E487-E3A9-4073-A545-294B88674225}.Debug|x86.ActiveCfg = Debug|Any CPU + {C708E487-E3A9-4073-A545-294B88674225}.Debug|x86.Build.0 = Debug|Any CPU + {C708E487-E3A9-4073-A545-294B88674225}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C708E487-E3A9-4073-A545-294B88674225}.Release|Any CPU.Build.0 = Release|Any CPU + {C708E487-E3A9-4073-A545-294B88674225}.Release|x86.ActiveCfg = Release|Any CPU + {C708E487-E3A9-4073-A545-294B88674225}.Release|x86.Build.0 = Release|Any CPU + {64308775-BD4A-469C-BCAB-3ED830B811AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64308775-BD4A-469C-BCAB-3ED830B811AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64308775-BD4A-469C-BCAB-3ED830B811AF}.Debug|x86.ActiveCfg = Debug|Any CPU + {64308775-BD4A-469C-BCAB-3ED830B811AF}.Debug|x86.Build.0 = Debug|Any CPU + {64308775-BD4A-469C-BCAB-3ED830B811AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64308775-BD4A-469C-BCAB-3ED830B811AF}.Release|Any CPU.Build.0 = Release|Any CPU + {64308775-BD4A-469C-BCAB-3ED830B811AF}.Release|x86.ActiveCfg = Release|Any CPU + {64308775-BD4A-469C-BCAB-3ED830B811AF}.Release|x86.Build.0 = Release|Any CPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}.Debug|x86.ActiveCfg = Debug|Any CPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}.Debug|x86.Build.0 = Debug|Any CPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}.Release|Any CPU.Build.0 = Release|Any CPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}.Release|x86.ActiveCfg = Release|Any CPU + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD}.Release|x86.Build.0 = Release|Any CPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}.Debug|x86.ActiveCfg = Debug|Any CPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}.Debug|x86.Build.0 = Debug|Any CPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}.Release|Any CPU.Build.0 = Release|Any CPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}.Release|x86.ActiveCfg = Release|Any CPU + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F}.Release|x86.Build.0 = Release|Any CPU + {1073A462-D9A4-4C72-9C3F-A345B307153A}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1073A462-D9A4-4C72-9C3F-A345B307153A}.Debug|Any CPU.Build.0 = Debug|x86 + {1073A462-D9A4-4C72-9C3F-A345B307153A}.Debug|x86.ActiveCfg = Debug|x86 + {1073A462-D9A4-4C72-9C3F-A345B307153A}.Debug|x86.Build.0 = Debug|x86 + {1073A462-D9A4-4C72-9C3F-A345B307153A}.Release|Any CPU.ActiveCfg = Release|x86 + {1073A462-D9A4-4C72-9C3F-A345B307153A}.Release|Any CPU.Build.0 = Release|x86 + {1073A462-D9A4-4C72-9C3F-A345B307153A}.Release|x86.ActiveCfg = Release|x86 + {1073A462-D9A4-4C72-9C3F-A345B307153A}.Release|x86.Build.0 = Release|x86 + {D5207217-61C7-4E94-8097-91DBACE57D2A}.Debug|Any CPU.ActiveCfg = Debug|x86 + {D5207217-61C7-4E94-8097-91DBACE57D2A}.Debug|Any CPU.Build.0 = Debug|x86 + {D5207217-61C7-4E94-8097-91DBACE57D2A}.Debug|x86.ActiveCfg = Debug|x86 + {D5207217-61C7-4E94-8097-91DBACE57D2A}.Debug|x86.Build.0 = Debug|x86 + {D5207217-61C7-4E94-8097-91DBACE57D2A}.Release|Any CPU.ActiveCfg = Release|x86 + {D5207217-61C7-4E94-8097-91DBACE57D2A}.Release|x86.ActiveCfg = Release|x86 + {D5207217-61C7-4E94-8097-91DBACE57D2A}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {BDED8EBB-7F9F-4710-8B74-59298C2DFCAD} = {7CAA050A-2EB3-4A21-8A75-131EB0AC9778} + {AFA5A4A4-188C-4180-B910-7C5BA7D5C11F} = {7CAA050A-2EB3-4A21-8A75-131EB0AC9778} + {D5207217-61C7-4E94-8097-91DBACE57D2A} = {9B4388AE-0FB9-4C65-9C3F-000CE932E110} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {343D05C4-AD6F-4A05-A971-ECAABF3294AF} + EndGlobalSection +EndGlobal diff --git a/LynxAstro.DewController/AssemblyInfo.cs b/LynxAstro.DewController/AssemblyInfo.cs new file mode 100644 index 0000000..61470c7 --- /dev/null +++ b/LynxAstro.DewController/AssemblyInfo.cs @@ -0,0 +1,91 @@ +using System; +using System.Reflection; + +namespace ASCOM.LynxAstro.DewController +{ + public class AssemblyInfo + { + // The assembly information values. + //public readonly string Title = string.Empty; + //public readonly string Description = string.Empty; + //public readonly string Company = string.Empty; + public readonly string Product = string.Empty; + //public readonly string Copyright = string.Empty; + //public readonly string Trademark = string.Empty; + public readonly string AssemblyVersion; + //public readonly string FileVersion = string.Empty; + //public readonly string Guid = string.Empty; + //public readonly string NeutralLanguage = string.Empty; + //public readonly bool IsComVisible; + + // Return a particular assembly attribute value. + private T GetAssemblyAttribute(Assembly assembly) + where T : Attribute + { + // Get attributes of this type. + object[] attributes = assembly.GetCustomAttributes(typeof(T), true); + + // If we didn't get anything, return null. + if (attributes.Length == 0) + return null; + + // Convert the first attribute value into + // the desired type and return it. + return (T)attributes[0]; + } + + // Constructors. + public AssemblyInfo() + : this(Assembly.GetExecutingAssembly()) + { + } + + private AssemblyInfo(Assembly assembly) + { + // Get values from the assembly. + //var titleAttr = GetAssemblyAttribute(assembly); + //if (titleAttr != null) + // Title = titleAttr.Title; + + //var assemblyAttr = GetAssemblyAttribute(assembly); + //if (assemblyAttr != null) + // Description = assemblyAttr.Description; + + //var companyAttr =GetAssemblyAttribute(assembly); + //if (companyAttr != null) + // Company = companyAttr.Company; + + var productAttr = GetAssemblyAttribute(assembly); + if (productAttr != null) + Product = productAttr.Product; + + //var copyrightAttr = GetAssemblyAttribute(assembly); + //if (copyrightAttr != null) + // Copyright = copyrightAttr.Copyright; + + //var trademarkAttr = GetAssemblyAttribute(assembly); + //if (trademarkAttr != null) + // Trademark = trademarkAttr.Trademark; + + var version = assembly.GetName().Version; + AssemblyVersion = $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; + + + //var fileVersionAttr = GetAssemblyAttribute(assembly); + //if (fileVersionAttr != null) FileVersion = + // fileVersionAttr.Version; + + //var guidAttr = GetAssemblyAttribute(assembly); + //if (guidAttr != null) + // Guid = guidAttr.Value; + + //var languageAttr = GetAssemblyAttribute(assembly); + //if (languageAttr != null) + // NeutralLanguage = languageAttr.CultureName; + + //var comAttr = GetAssemblyAttribute(assembly); + //if (comAttr != null) + // IsComVisible = comAttr.Value; + } + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/ClassFactory.cs b/LynxAstro.DewController/ClassFactory.cs new file mode 100644 index 0000000..159e1e7 --- /dev/null +++ b/LynxAstro.DewController/ClassFactory.cs @@ -0,0 +1,244 @@ +using System; +using System.Runtime.InteropServices; +using System.Collections; + +namespace ASCOM.LynxAstro.DewController +{ + + #region C# Definition of IClassFactory + // + // Provide a definition of theCOM IClassFactory interface. + // + [ + ComImport, // This interface originated from COM. + ComVisible(false), // Must not be exposed to COM!!! + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), // Indicate that this interface is not IDispatch-based. + Guid("00000001-0000-0000-C000-000000000046") // This GUID is the actual GUID of IClassFactory. + ] + public interface IClassFactory + { + void CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject); + void LockServer(bool fLock); + } + #endregion + + // + // Universal ClassFactory. Given a type as a parameter of the + // constructor, it implements IClassFactory for any interface + // that the class implements. Magic!!! + // + public class ClassFactory : IClassFactory + { + + #region Access to ole32.dll functions for class factories + + // Define two common GUID objects for public usage. + public static Guid IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); + public static Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}"); + + [Flags] + enum CLSCTX : uint + { + CLSCTX_INPROC_SERVER = 0x1, + CLSCTX_INPROC_HANDLER = 0x2, + CLSCTX_LOCAL_SERVER = 0x4, + CLSCTX_INPROC_SERVER16 = 0x8, + CLSCTX_REMOTE_SERVER = 0x10, + CLSCTX_INPROC_HANDLER16 = 0x20, + CLSCTX_RESERVED1 = 0x40, + CLSCTX_RESERVED2 = 0x80, + CLSCTX_RESERVED3 = 0x100, + CLSCTX_RESERVED4 = 0x200, + CLSCTX_NO_CODE_DOWNLOAD = 0x400, + CLSCTX_RESERVED5 = 0x800, + CLSCTX_NO_CUSTOM_MARSHAL = 0x1000, + CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000, + CLSCTX_NO_FAILURE_LOG = 0x4000, + CLSCTX_DISABLE_AAA = 0x8000, + CLSCTX_ENABLE_AAA = 0x10000, + CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000, + CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, + CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, + CLSCTX_ALL = CLSCTX_SERVER | CLSCTX_INPROC_HANDLER + } + + [Flags] + enum REGCLS : uint + { + REGCLS_SINGLEUSE = 0, + REGCLS_MULTIPLEUSE = 1, + REGCLS_MULTI_SEPARATE = 2, + REGCLS_SUSPENDED = 4, + REGCLS_SURROGATE = 8 + } + // + // CoRegisterClassObject() is used to register a Class Factory + // into COM's internal table of Class Factories. + // + [DllImport("ole32.dll")] + static extern int CoRegisterClassObject( + [In] ref Guid rclsid, + [MarshalAs(UnmanagedType.IUnknown)] object pUnk, + uint dwClsContext, + uint flags, + out uint lpdwRegister); + // + // Called by a COM EXE Server that can register multiple class objects + // to inform COM about all registered classes, and permits activation + // requests for those class objects. + // This function causes OLE to inform the SCM about all the registered + // classes, and begins letting activation requests into the server process. + // + [DllImport("ole32.dll")] + static extern int CoResumeClassObjects(); + // + // Prevents any new activation requests from the SCM on all class objects + // registered within the process. Even though a process may call this API, + // the process still must call CoRevokeClassObject for each CLSID it has + // registered, in the apartment it registered in. + // + [DllImport("ole32.dll")] + static extern int CoSuspendClassObjects(); + // + // CoRevokeClassObject() is used to unregister a Class Factory + // from COM's internal table of Class Factories. + // + [DllImport("ole32.dll")] + static extern int CoRevokeClassObject(uint dwRegister); + #endregion + + #region Constructor and Private ClassFactory Data + + protected Type m_ClassType; + protected Guid m_ClassId; + protected ArrayList m_InterfaceTypes; + protected uint m_ClassContext; + protected uint m_Flags; + protected UInt32 m_locked = 0; + protected uint m_Cookie; + protected string m_progid; + + public ClassFactory(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + m_ClassType = type; + + //PWGS Get the ProgID from the MetaData + m_progid = Marshal.GenerateProgIdForType(type); + m_ClassId = Marshal.GenerateGuidForType(type); // Should be nailed down by [Guid(...)] + m_ClassContext = (uint)CLSCTX.CLSCTX_LOCAL_SERVER; // Default + m_Flags = (uint)REGCLS.REGCLS_MULTIPLEUSE | // Default + (uint)REGCLS.REGCLS_SUSPENDED; + m_InterfaceTypes = new ArrayList(); + foreach (Type T in type.GetInterfaces()) // Save all of the implemented interfaces + m_InterfaceTypes.Add(T); + } + + #endregion + + #region Common ClassFactory Methods + public uint ClassContext + { + get { return m_ClassContext; } + set { m_ClassContext = value; } + } + + public Guid ClassId + { + get { return m_ClassId; } + set { m_ClassId = value; } + } + + public uint Flags + { + get { return m_Flags; } + set { m_Flags = value; } + } + + public bool RegisterClassObject() + { + // Register the class factory + int i = CoRegisterClassObject + ( + ref m_ClassId, + this, + m_ClassContext, + m_Flags, + out m_Cookie + ); + return (i == 0); + } + + public bool RevokeClassObject() + { + int i = CoRevokeClassObject(m_Cookie); + return (i == 0); + } + + public static bool ResumeClassObjects() + { + int i = CoResumeClassObjects(); + return (i == 0); + } + + public static bool SuspendClassObjects() + { + int i = CoSuspendClassObjects(); + return (i == 0); + } + #endregion + + #region IClassFactory Implementations + // + // Implement creation of the type and interface. + // + void IClassFactory.CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject) + { + IntPtr nullPtr = new IntPtr(0); + ppvObject = nullPtr; + + // + // Handle specific requests for implemented interfaces + // + foreach (Type iType in m_InterfaceTypes) + { + if (riid == Marshal.GenerateGuidForType(iType)) + { + ppvObject = Marshal.GetComInterfaceForObject(Activator.CreateInstance(m_ClassType), iType); + return; + } + } + // + // Handle requests for IDispatch or IUnknown on the class + // + if (riid == IID_IDispatch) + { + ppvObject = Marshal.GetIDispatchForObject(Activator.CreateInstance(m_ClassType)); + return; + } + else if (riid == IID_IUnknown) + { + ppvObject = Marshal.GetIUnknownForObject(Activator.CreateInstance(m_ClassType)); + } + else + { + // + // Oops, some interface that the class doesn't implement + // + throw new COMException("No interface", unchecked((int)0x80004002)); + } + } + + void IClassFactory.LockServer(bool bLock) + { + if (bLock) + Server.CountLock(); + else + Server.UncountLock(); + // Always attempt to see if we need to shutdown this server application. + Server.ExitIf(); + } + #endregion + } +} diff --git a/LynxAstro.DewController/ConnectionInfo.cs b/LynxAstro.DewController/ConnectionInfo.cs new file mode 100644 index 0000000..5ac8175 --- /dev/null +++ b/LynxAstro.DewController/ConnectionInfo.cs @@ -0,0 +1,8 @@ +namespace ASCOM.LynxAstro.DewController +{ + public class ConnectionInfo + { + //public int Connections { get; set; } + public int SameDevice { get; set; } + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/GarbageCollection.cs b/LynxAstro.DewController/GarbageCollection.cs new file mode 100644 index 0000000..dfc92f8 --- /dev/null +++ b/LynxAstro.DewController/GarbageCollection.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading; + +namespace ASCOM.LynxAstro.DewController +{ + /// + /// Summary description for GarbageCollection. + /// + class GarbageCollection + { + protected bool m_bContinueThread; + protected bool m_GCWatchStopped; + protected int m_iInterval; + protected ManualResetEvent m_EventThreadEnded; + + public GarbageCollection(int iInterval) + { + m_bContinueThread = true; + m_GCWatchStopped = false; + m_iInterval = iInterval; + m_EventThreadEnded = new ManualResetEvent(false); + } + + public void GCWatch() + { + // Pause for a moment to provide a delay to make threads more apparent. + while (ContinueThread()) + { + GC.Collect(); + Thread.Sleep(m_iInterval); + } + m_EventThreadEnded.Set(); + } + + protected bool ContinueThread() + { + lock (this) + { + return m_bContinueThread; + } + } + + public void StopThread() + { + lock (this) + { + m_bContinueThread = false; + } + } + + public void WaitForThreadToStop() + { + m_EventThreadEnded.WaitOne(); + m_EventThreadEnded.Reset(); + } + } +} diff --git a/LynxAstro.DewController/IProfileFactory.cs b/LynxAstro.DewController/IProfileFactory.cs new file mode 100644 index 0000000..94f0919 --- /dev/null +++ b/LynxAstro.DewController/IProfileFactory.cs @@ -0,0 +1,7 @@ +namespace ASCOM.LynxAstro.DewController +{ + public interface IProfileFactory + { + IProfileWrapper Create(); + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/IProfileWrapper.cs b/LynxAstro.DewController/IProfileWrapper.cs new file mode 100644 index 0000000..6b320d0 --- /dev/null +++ b/LynxAstro.DewController/IProfileWrapper.cs @@ -0,0 +1,10 @@ +using System; +using ASCOM.Utilities.Interfaces; + +namespace ASCOM.LynxAstro.DewController +{ + public interface IProfileWrapper : IProfile, IProfileExtra, IDisposable + { + + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/ISharedResourcesWrapper.cs b/LynxAstro.DewController/ISharedResourcesWrapper.cs new file mode 100644 index 0000000..bbc575c --- /dev/null +++ b/LynxAstro.DewController/ISharedResourcesWrapper.cs @@ -0,0 +1,28 @@ +using System; +using ASCOM.Utilities.Interfaces; + +namespace ASCOM.LynxAstro.DewController +{ + public interface ISharedResourcesWrapper + { + ConnectionInfo Connect(string deviceId, string driverId, ITraceLogger traceLogger); + void Disconnect(string deviceId, string driverId); + + string FirmwareVersion { get; } + + void Lock(Action action); + T Lock(Func func); + + string SendString(string message); + void SendBlind(string message); + string SendChar(string message); + + string ReadTerminated(); + + ProfileProperties ReadProfile(); + + void SetupDialog(); + void WriteProfile(ProfileProperties profileProperties); + void ReadCharacters(int throwAwayCharacters); + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/LocalServer.cs b/LynxAstro.DewController/LocalServer.cs new file mode 100644 index 0000000..f06b4cf --- /dev/null +++ b/LynxAstro.DewController/LocalServer.cs @@ -0,0 +1,640 @@ +// +// ASCOM.LynxAstro.DewController Local COM Server +// +// This is the core of a managed COM Local Server, capable of serving +// multiple instances of multiple interfaces, within a single +// executable. This implementes the equivalent functionality of VB6 +// which has been extensively used in ASCOM for drivers that provide +// multiple interfaces to multiple clients (e.g. Meade Telescope +// and Focuser) as well as hubs (e.g., POTH). +// +// Written by: Robert B. Denny (Version 1.0.1, 29-May-2007) +// Modified by Chris Rowland and Peter Simpson to allow use with multiple devices of the same type March 2011 +// +// +using System; +using System.IO; +using System.Windows.Forms; +using System.Collections; +using System.Runtime.InteropServices; +using System.Reflection; +using ASCOM.Utilities; +using Microsoft.Win32; +using System.Threading; +using System.Security.Principal; +using System.Diagnostics; + +namespace ASCOM.LynxAstro.DewController +{ + public static class Server + { + + #region Access to kernel32.dll, user32.dll, and ole32.dll functions + [Flags] + enum CLSCTX : uint + { + CLSCTX_INPROC_SERVER = 0x1, + CLSCTX_INPROC_HANDLER = 0x2, + CLSCTX_LOCAL_SERVER = 0x4, + CLSCTX_INPROC_SERVER16 = 0x8, + CLSCTX_REMOTE_SERVER = 0x10, + CLSCTX_INPROC_HANDLER16 = 0x20, + CLSCTX_RESERVED1 = 0x40, + CLSCTX_RESERVED2 = 0x80, + CLSCTX_RESERVED3 = 0x100, + CLSCTX_RESERVED4 = 0x200, + CLSCTX_NO_CODE_DOWNLOAD = 0x400, + CLSCTX_RESERVED5 = 0x800, + CLSCTX_NO_CUSTOM_MARSHAL = 0x1000, + CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000, + CLSCTX_NO_FAILURE_LOG = 0x4000, + CLSCTX_DISABLE_AAA = 0x8000, + CLSCTX_ENABLE_AAA = 0x10000, + CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000, + CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, + CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, + CLSCTX_ALL = CLSCTX_SERVER | CLSCTX_INPROC_HANDLER + } + + [Flags] + enum COINIT : uint + { + /// Initializes the thread for multi-threaded object concurrency. + COINIT_MULTITHREADED = 0x0, + /// Initializes the thread for apartment-threaded object concurrency. + COINIT_APARTMENTTHREADED = 0x2, + /// Disables DDE for Ole1 support. + COINIT_DISABLE_OLE1DDE = 0x4, + /// Trades memory for speed. + COINIT_SPEED_OVER_MEMORY = 0x8 + } + + [Flags] + enum REGCLS : uint + { + REGCLS_SINGLEUSE = 0, + REGCLS_MULTIPLEUSE = 1, + REGCLS_MULTI_SEPARATE = 2, + REGCLS_SUSPENDED = 4, + REGCLS_SURROGATE = 8 + } + + + // CoInitializeEx() can be used to set the apartment model + // of individual threads. + [DllImport("ole32.dll")] + static extern int CoInitializeEx(IntPtr pvReserved, uint dwCoInit); + + // CoUninitialize() is used to uninitialize a COM thread. + [DllImport("ole32.dll")] + static extern void CoUninitialize(); + + // PostThreadMessage() allows us to post a Windows Message to + // a specific thread (identified by its thread id). + // We will need this API to post a WM_QUIT message to the main + // thread in order to terminate this application. + [DllImport("user32.dll")] + static extern bool PostThreadMessage(uint idThread, uint Msg, UIntPtr wParam, + IntPtr lParam); + + // GetCurrentThreadId() allows us to obtain the thread id of the + // calling thread. This allows us to post the WM_QUIT message to + // the main thread. + [DllImport("kernel32.dll")] + static extern uint GetCurrentThreadId(); + #endregion + + #region Private Data + private static int objsInUse; // Keeps a count on the total number of objects alive. + private static int serverLocks; // Keeps a lock count on this application. + private static frmMain s_MainForm = null; // Reference to our main form + private static ArrayList s_ComObjectAssys; // Dynamically loaded assemblies containing served COM objects + private static ArrayList s_ComObjectTypes; // Served COM object types + private static ArrayList s_ClassFactories; // Served COM object class factories + private static string s_appId = "{34e5f3f3-29e4-4d87-b861-c079485a5c97}"; // Our AppId + private static readonly Object lockObject = new object(); + #endregion + + // This property returns the main thread's id. + public static uint MainThreadId { get; private set; } // Stores the main thread's thread id. + + // Used to tell if started by COM or manually + public static bool StartedByCOM { get; private set; } // True if server started by COM (-embedding) + + + #region Server Lock, Object Counting, and AutoQuit on COM startup + // Returns the total number of objects alive currently. + public static int ObjectsCount + { + get + { + lock (lockObject) + { + return objsInUse; + } + } + } + + // This method performs a thread-safe incrementation of the objects count. + public static int CountObject() + { + // Increment the global count of objects. + return Interlocked.Increment(ref objsInUse); + } + + // This method performs a thread-safe decrementation the objects count. + public static int UncountObject() + { + // Decrement the global count of objects. + return Interlocked.Decrement(ref objsInUse); + } + + // Returns the current server lock count. + public static int ServerLockCount + { + get + { + lock (lockObject) + { + return serverLocks; + } + } + } + + // This method performs a thread-safe incrementation the + // server lock count. + public static int CountLock() + { + // Increment the global lock count of this server. + return Interlocked.Increment(ref serverLocks); + } + + // This method performs a thread-safe decrementation the + // server lock count. + public static int UncountLock() + { + // Decrement the global lock count of this server. + return Interlocked.Decrement(ref serverLocks); + } + + // AttemptToTerminateServer() will check to see if the objects count and the server + // lock count have both dropped to zero. + // + // If so, and if we were started by COM, we post a WM_QUIT message to the main thread's + // message loop. This will cause the message loop to exit and hence the termination + // of this application. If hand-started, then just trace that it WOULD exit now. + // + public static void ExitIf() + { + lock (lockObject) + { + if ((ObjectsCount <= 0) && (ServerLockCount <= 0)) + { + if (StartedByCOM) + { + UIntPtr wParam = new UIntPtr(0); + IntPtr lParam = new IntPtr(0); + PostThreadMessage(MainThreadId, 0x0012, wParam, lParam); + } + } + } + } + #endregion + + // ----------------- + // PRIVATE FUNCTIONS + // ----------------- + + #region Dynamic Driver Assembly Loader + // + // Load the assemblies that contain the classes that we will serve + // via COM. These will be located in the same folder as + // our executable. + // + private static bool LoadComObjectAssemblies() + { + s_ComObjectAssys = new ArrayList(); + s_ComObjectTypes = new ArrayList(); + + // put everything into one folder, the same as the server. + string assyPath = Assembly.GetEntryAssembly().Location; + assyPath = Path.GetDirectoryName(assyPath); + + DirectoryInfo d = new DirectoryInfo(assyPath); + foreach (FileInfo fi in d.GetFiles("*.dll")) + { + string aPath = fi.FullName; + // + // First try to load the assembly and get the types for + // the class and the class factory. If this doesn't work ???? + // + try + { + Assembly so = Assembly.LoadFrom(aPath); + //PWGS Get the types in the assembly + Type[] types = so.GetTypes(); + foreach (Type type in types) + { + // PWGS Now checks the type rather than the assembly + // Check to see if the type has the ServedClassName attribute, only use it if it does. + MemberInfo info = type; + + object[] attrbutes = info.GetCustomAttributes(typeof(ServedClassNameAttribute), false); + if (attrbutes.Length > 0) + { + //MessageBox.Show("Adding Type: " + type.Name + " " + type.FullName); + s_ComObjectTypes.Add(type); //PWGS - much simpler + s_ComObjectAssys.Add(so); + } + } + } + catch (BadImageFormatException) + { + // Probably an attempt to load a Win32 DLL (i.e. not a .net assembly) + // Just swallow the exception and continue to the next item. + continue; + } + catch (Exception e) + { + MessageBox.Show("Failed to load served COM class assembly " + fi.Name + " - " + e.Message, + "LynxAstro.DewController", MessageBoxButtons.OK, MessageBoxIcon.Stop); + return false; + } + + } + return true; + } + #endregion + + #region COM Registration and Unregistration + // + // Test if running elevated + // + private static bool IsAdministrator + { + get + { + WindowsIdentity i = WindowsIdentity.GetCurrent(); + WindowsPrincipal p = new WindowsPrincipal(i); + return p.IsInRole(WindowsBuiltInRole.Administrator); + } + } + + // + // Elevate by re-running ourselves with elevation dialog + // + private static void ElevateSelf(string arg) + { + ProcessStartInfo si = new ProcessStartInfo(); + si.Arguments = arg; + si.WorkingDirectory = Environment.CurrentDirectory; + si.FileName = Application.ExecutablePath; + si.Verb = "runas"; + try { Process.Start(si); } + catch (System.ComponentModel.Win32Exception) + { + MessageBox.Show("The LynxAstro.DewController was not " + (arg == "/register" ? "registered" : "unregistered") + + " because you did not allow it.", "LynxAstro.DewController", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString(), "LynxAstro.DewController", MessageBoxButtons.OK, MessageBoxIcon.Stop); + } + return; + } + + // + // Do everything to register this for COM. Never use REGASM on + // this exe assembly! It would create InProcServer32 entries + // which would prevent proper activation! + // + // Using the list of COM object types generated during dynamic + // assembly loading, it registers each one for COM as served by our + // exe/local server, as well as registering it for ASCOM. It also + // adds DCOM info for the local server itself, so it can be activated + // via an outboiud connection from TheSky. + // + private static void RegisterObjects() + { + if (!IsAdministrator) + { + ElevateSelf("/register"); + return; + } + // + // If reached here, we're running elevated + // + + Assembly assy = Assembly.GetExecutingAssembly(); + Attribute attr = Attribute.GetCustomAttribute(assy, typeof(AssemblyTitleAttribute)); + string assyTitle = ((AssemblyTitleAttribute)attr).Title; + attr = Attribute.GetCustomAttribute(assy, typeof(AssemblyDescriptionAttribute)); + string assyDescription = ((AssemblyDescriptionAttribute)attr).Description; + + // + // Local server's DCOM/AppID information + // + try + { + // + // HKCR\APPID\appid + // + using (RegistryKey key = Registry.ClassesRoot.CreateSubKey("APPID\\" + s_appId)) + { + key.SetValue(null, assyDescription); + key.SetValue("AppID", s_appId); + key.SetValue("AuthenticationLevel", 1, RegistryValueKind.DWord); + key.SetValue("RunAs", "Interactive User", RegistryValueKind.String); // Added to ensure that only one copy of the local server runs if the user uses both elevated and non-elevated clients concurrently + } + // + // HKCR\APPID\exename.ext + // + using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(string.Format("APPID\\{0}", + Application.ExecutablePath.Substring(Application.ExecutablePath.LastIndexOf('\\') + 1)))) + { + key.SetValue("AppID", s_appId); + } + } + catch (Exception ex) + { + MessageBox.Show("Error while registering the server:\n" + ex.ToString(), + "LynxAstro.DewController", MessageBoxButtons.OK, MessageBoxIcon.Stop); + return; + } + finally + { + } + + // + // For each of the driver assemblies + // + foreach (Type type in s_ComObjectTypes) + { + bool bFail = false; + try + { + // + // HKCR\CLSID\clsid + // + string clsid = Marshal.GenerateGuidForType(type).ToString("B"); + string progid = Marshal.GenerateProgIdForType(type); + //PWGS Generate device type from the Class name + string deviceType = type.Name; + + using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(string.Format("CLSID\\{0}", clsid))) + { + key.SetValue(null, progid); // Could be assyTitle/Desc??, but .NET components show ProgId here + key.SetValue("AppId", s_appId); + using (RegistryKey key2 = key.CreateSubKey("Implemented Categories")) + { + key2.CreateSubKey("{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}"); + } + using (RegistryKey key2 = key.CreateSubKey("ProgId")) + { + key2.SetValue(null, progid); + } + key.CreateSubKey("Programmable"); + using (RegistryKey key2 = key.CreateSubKey("LocalServer32")) + { + key2.SetValue(null, Application.ExecutablePath); + } + } + // + // HKCR\CLSID\progid + // + using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(progid)) + { + key.SetValue(null, assyTitle); + using (RegistryKey key2 = key.CreateSubKey("CLSID")) + { + key2.SetValue(null, clsid); + } + } + // + // ASCOM + // + assy = type.Assembly; + + // Pull the display name from the ServedClassName attribute. + attr = Attribute.GetCustomAttribute(type, typeof(ServedClassNameAttribute)); //PWGS Changed to search type for attribute rather than assembly + string chooserName = ((ServedClassNameAttribute)attr).DisplayName ?? "MultiServer"; + using (var P = new Profile()) + { + P.DeviceType = deviceType; + P.Register(progid, chooserName); + } + } + catch (Exception ex) + { + MessageBox.Show("Error while registering the server:\n" + ex.ToString(), + "LynxAstro.DewController", MessageBoxButtons.OK, MessageBoxIcon.Stop); + bFail = true; + } + finally + { + } + if (bFail) break; + } + } + + // + // Remove all traces of this from the registry. + // + // **TODO** If the above does AppID/DCOM stuff, this would have + // to remove that stuff too. + // + private static void UnregisterObjects() + { + if (!IsAdministrator) + { + ElevateSelf("/unregister"); + return; + } + + // + // Local server's DCOM/AppID information + // + Registry.ClassesRoot.DeleteSubKey(string.Format("APPID\\{0}", s_appId), false); + Registry.ClassesRoot.DeleteSubKey(string.Format("APPID\\{0}", + Application.ExecutablePath.Substring(Application.ExecutablePath.LastIndexOf('\\') + 1)), false); + + // + // For each of the driver assemblies + // + foreach (Type type in s_ComObjectTypes) + { + string clsid = Marshal.GenerateGuidForType(type).ToString("B"); + string progid = Marshal.GenerateProgIdForType(type); + string deviceType = type.Name; + // + // Best efforts + // + // + // HKCR\progid + // + Registry.ClassesRoot.DeleteSubKey(String.Format("{0}\\CLSID", progid), false); + Registry.ClassesRoot.DeleteSubKey(progid, false); + // + // HKCR\CLSID\clsid + // + Registry.ClassesRoot.DeleteSubKey(String.Format("CLSID\\{0}\\Implemented Categories\\{{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}}", clsid), false); + Registry.ClassesRoot.DeleteSubKey(String.Format("CLSID\\{0}\\Implemented Categories", clsid), false); + Registry.ClassesRoot.DeleteSubKey(String.Format("CLSID\\{0}\\ProgId", clsid), false); + Registry.ClassesRoot.DeleteSubKey(String.Format("CLSID\\{0}\\LocalServer32", clsid), false); + Registry.ClassesRoot.DeleteSubKey(String.Format("CLSID\\{0}\\Programmable", clsid), false); + Registry.ClassesRoot.DeleteSubKey(String.Format("CLSID\\{0}", clsid), false); + try + { + // + // ASCOM + // + using (var P = new Profile()) + { + P.DeviceType = deviceType; + P.Unregister(progid); + } + } + catch (Exception) { } + } + } + #endregion + + #region Class Factory Support + // + // On startup, we register the class factories of the COM objects + // that we serve. This requires the class facgtory name to be + // equal to the served class name + "ClassFactory". + // + private static bool RegisterClassFactories() + { + s_ClassFactories = new ArrayList(); + foreach (Type type in s_ComObjectTypes) + { + ClassFactory factory = new ClassFactory(type); // Use default context & flags + s_ClassFactories.Add(factory); + if (!factory.RegisterClassObject()) + { + MessageBox.Show("Failed to register class factory for " + type.Name, + "LynxAstro.DewController", MessageBoxButtons.OK, MessageBoxIcon.Stop); + return false; + } + } + ClassFactory.ResumeClassObjects(); // Served objects now go live + return true; + } + + private static void RevokeClassFactories() + { + ClassFactory.SuspendClassObjects(); // Prevent race conditions + foreach (ClassFactory factory in s_ClassFactories) + factory.RevokeClassObject(); + } + #endregion + + #region Command Line Arguments + // + // ProcessArguments() will process the command-line arguments + // If the return value is true, we carry on and start this application. + // If the return value is false, we terminate this application immediately. + // + private static bool ProcessArguments(string[] args) + { + bool bRet = true; + + // + //**TODO** -Embedding is "ActiveX start". Prohibit non_AX starting? + // + if (args.Length > 0) + { + + switch (args[0].ToLower()) + { + case "-embedding": + StartedByCOM = true; // Indicate COM started us + break; + + case "-register": + case @"/register": + case "-regserver": // Emulate VB6 + case @"/regserver": + RegisterObjects(); // Register each served object + bRet = false; + break; + + case "-unregister": + case @"/unregister": + case "-unregserver": // Emulate VB6 + case @"/unregserver": + UnregisterObjects(); //Unregister each served object + bRet = false; + break; + + default: + MessageBox.Show("Unknown argument: " + args[0] + "\nValid are : -register, -unregister and -embedding", + "LynxAstro.DewController", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + break; + } + } + else + StartedByCOM = false; + + return bRet; + } + #endregion + + #region SERVER ENTRY POINT (main) + // + // ================== + // SERVER ENTRY POINT + // ================== + // + [STAThread] + static void Main(string[] args) + { + if (!LoadComObjectAssemblies()) return; // Load served COM class assemblies, get types + + if (!ProcessArguments(args)) return; // Register/Unregister + + // Initialize critical member variables. + objsInUse = 0; + serverLocks = 0; + MainThreadId = GetCurrentThreadId(); + Thread.CurrentThread.Name = "Main Thread"; + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + s_MainForm = new frmMain(); + if (StartedByCOM) s_MainForm.WindowState = FormWindowState.Minimized; + + // Register the class factories of the served objects + RegisterClassFactories(); + + // Start up the garbage collection thread. + GarbageCollection GarbageCollector = new GarbageCollection(1000); + Thread GCThread = new Thread(new ThreadStart(GarbageCollector.GCWatch)); + GCThread.Name = "Garbage Collection Thread"; + GCThread.Start(); + + // + // Start the message loop. This serializes incoming calls to our + // served COM objects, making this act like the VB6 equivalent! + // + try + { + Application.Run(s_MainForm); + } + finally + { + // Revoke the class factories immediately. + // Don't wait until the thread has stopped before + // we perform revocation!!! + RevokeClassFactories(); + + // Now stop the Garbage Collector thread. + GarbageCollector.StopThread(); + GarbageCollector.WaitForThreadToStop(); + } + } + #endregion + } +} diff --git a/LynxAstro.DewController/LocalServer.snk b/LynxAstro.DewController/LocalServer.snk new file mode 100644 index 0000000..97e4406 Binary files /dev/null and b/LynxAstro.DewController/LocalServer.snk differ diff --git a/LynxAstro.DewController/LynxAstro.DewController.csproj b/LynxAstro.DewController/LynxAstro.DewController.csproj new file mode 100644 index 0000000..ed3df9a --- /dev/null +++ b/LynxAstro.DewController/LynxAstro.DewController.csproj @@ -0,0 +1,191 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {C708E487-E3A9-4073-A545-294B88674225} + WinExe + Properties + ASCOM.LynxAstro.DewController + ASCOM.LynxAstro.DewController.Server + v4.7.2 + + + 2.0 + + + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + MinimumRecommendedRules.ruleset + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x86 + false + + + true + + + LocalServer.snk + + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Astrometry.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Attributes.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Cache.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Controls.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.DeviceInterfaces.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.DriverAccess.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Exceptions.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Internal.Extensions.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.SettingsProvider.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Utilities.dll + + + ..\packages\ASCOM.Platform.6.5.2\lib\net40\ASCOM.Utilities.Video.dll + + + ..\packages\JetBrains.Annotations.2020.3.0\lib\net20\JetBrains.Annotations.dll + + + + + + + + + + + Form + + + frmMain.cs + + + + + + + + + + + + + Designer + frmMain.cs + + + True + True + Resources.resx + + + + Form + + + SetupDialogForm.cs + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SetupDialogForm.cs + Designer + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + \ No newline at end of file diff --git a/LynxAstro.DewController/ProfileFactory.cs b/LynxAstro.DewController/ProfileFactory.cs new file mode 100644 index 0000000..1316bb9 --- /dev/null +++ b/LynxAstro.DewController/ProfileFactory.cs @@ -0,0 +1,10 @@ +namespace ASCOM.LynxAstro.DewController +{ + public class ProfileFactory : IProfileFactory + { + public IProfileWrapper Create() + { + return new ProfileWrapper(); + } + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/ProfileProperties.cs b/LynxAstro.DewController/ProfileProperties.cs new file mode 100644 index 0000000..075c1a6 --- /dev/null +++ b/LynxAstro.DewController/ProfileProperties.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace ASCOM.LynxAstro.DewController +{ + public class ProfileProperties + { + // properies that are part of the profile + public string ComPort { get; set; } + public bool TraceLogger { get; set; } + public List SwitchNames { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/ProfileWrapper.cs b/LynxAstro.DewController/ProfileWrapper.cs new file mode 100644 index 0000000..4b714c3 --- /dev/null +++ b/LynxAstro.DewController/ProfileWrapper.cs @@ -0,0 +1,132 @@ +using System.Collections; +using ASCOM.Utilities; + +namespace ASCOM.LynxAstro.DewController +{ + public class ProfileWrapper : IProfileWrapper + { + private readonly Profile _profile = new Profile(); + + public ArrayList RegisteredDevices(string deviceType) + { + return _profile.RegisteredDevices(deviceType); + } + + public bool IsRegistered(string driverId) + { + return _profile.IsRegistered(driverId); + } + + public void Register(string driverId, string descriptiveName) + { + _profile.Register(driverId, descriptiveName); + } + + public void Unregister(string driverId) + { + _profile.Unregister(driverId); + } + + public string GetValue(string driverId, string name, string subKey, string defaultValue) + { + return _profile.GetValue(driverId, name, subKey, defaultValue); + } + + public void WriteValue(string driverId, string name, string value, string subKey) + { + _profile.WriteValue(driverId, name, value); + } + + public ArrayList Values(string driverId, string subKey) + { + return _profile.Values(driverId, subKey); + } + + public void DeleteValue(string driverId, string name, string subKey) + { + _profile.DeleteValue(driverId, name, subKey); + } + + public void CreateSubKey(string driverId, string subKey) + { + _profile.CreateSubKey(driverId, subKey); + } + + public ArrayList SubKeys(string driverId, string subKey) + { + return _profile.SubKeys(driverId, subKey); + } + + public void DeleteSubKey(string driverId, string subKey) + { + _profile.DeleteSubKey(driverId, subKey); + } + + public string GetProfileXML(string deviceId) + { + return _profile.GetProfileXML(deviceId); + } + + public void SetProfileXML(string deviceId, string xml) + { + _profile.SetProfileXML(deviceId, xml); + } + + public string DeviceType + { + get => _profile.DeviceType; + set => _profile.DeviceType = value; + } + public ArrayList RegisteredDeviceTypes => _profile.RegisteredDeviceTypes; + + public void MigrateProfile(string currentPlatformVersion) + { + _profile.MigrateProfile(currentPlatformVersion); + } + + public void DeleteValue(string driverId, string name) + { + _profile.DeleteValue(driverId, name); + } + + public string GetValue(string driverId, string name) + { + return _profile.GetValue(driverId, name); + } + + public string GetValue(string driverId, string name, string subKey) + { + return _profile.GetValue(driverId, name, subKey); + } + + public ArrayList SubKeys(string driverId) + { + return _profile.SubKeys(driverId); + } + + public ArrayList Values(string driverId) + { + return _profile.Values(driverId); + } + + public void WriteValue(string driverId, string name, string value) + { + _profile.WriteValue(driverId, name, value); + } + + public ASCOMProfile GetProfile(string driverId) + { + return _profile.GetProfile(driverId); + } + + public void SetProfile(string driverId, ASCOMProfile xmlProfileKey) + { + _profile.SetProfile(driverId, xmlProfileKey); + } + + public void Dispose() + { + _profile.Dispose(); + } + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/Properties/AssemblyInfo.cs b/LynxAstro.DewController/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..73d16a3 --- /dev/null +++ b/LynxAstro.DewController/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ASCOM LynxAstro.DewController server")] +[assembly: AssemblyDescription("ASCOM multi-interface server for LynxAstro.DewController")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ASCOM Initiative")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Copyright © 2021, colin")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("6.4.0.0")] +[assembly: AssemblyFileVersion("6.4.0.0")] + +[assembly: ComVisibleAttribute(false)] diff --git a/LynxAstro.DewController/Properties/Resources.Designer.cs b/LynxAstro.DewController/Properties/Resources.Designer.cs new file mode 100644 index 0000000..01bc24f --- /dev/null +++ b/LynxAstro.DewController/Properties/Resources.Designer.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASCOM.LynxAstro.DewController.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASCOM.LynxAstro.DewController.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ASCOM { + get { + object obj = ResourceManager.GetObject("ASCOM", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to {0} Settings ({1}). + /// + internal static string SetupDialogForm_SetupDialogForm__0__Settings___1__ { + get { + return ResourceManager.GetString("SetupDialogForm_SetupDialogForm__0__Settings___1__", resourceCulture); + } + } + } +} diff --git a/LynxAstro.DewController/Properties/Resources.resx b/LynxAstro.DewController/Properties/Resources.resx new file mode 100644 index 0000000..d1d4df2 --- /dev/null +++ b/LynxAstro.DewController/Properties/Resources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\ASCOM.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + {0} Settings ({1}) + + \ No newline at end of file diff --git a/LynxAstro.DewController/ReadMe.htm b/LynxAstro.DewController/ReadMe.htm new file mode 100644 index 0000000..73dcae4 --- /dev/null +++ b/LynxAstro.DewController/ReadMe.htm @@ -0,0 +1,666 @@ + + + + + Untitled Document + + + + + + + + + + + + +
+

+ ASCOM LocalServer (singleton) Host +

+
+

+
+
+ +

+

+ You have just created a local server (singleton) host for one or + more ASCOM driver classes. +

+
+

+ This project implements an ASCOM host server for one or more + driver classes in a single-instance executable. It can be used to + serve multiple instances of a single driver class (hub), provide + driver services for multiple devices (e.g., Telescope and Focuser) to + multiple applications and allow multiple devices of the same type to + be connected. In the latter scenario, the multiple driver classes + will often share one or more resources such as the serial connection + and a microcontroller in the combined device. From the client's + perspective, using the drivers served by the local server is exactly + the same as if the drivers are loaded into the client's process space + (in-proc servers). +

+

+ + NOTE: + + + Unless you are prepared to handle all of the timing issues that arise + when multiple clients are accessing the properties and methods of + your driver(s), stop now. Just because the local server serializes + the calls to your driver(s)' properties and methods does not mean + that there will be no timing or concurrency issues.
+  
For + example, suppose the hub serves instances of a Telescope driver. One + client sets the TargetRightAscension property, then another sets + TargetRightAscension to a different value, then the first client sets + TargetDeclination, then the first client calls SlewToTarget() + followed by the second client calling SlewToTarget(). Besides the + first client's slew command sending the scope to the wrong (and + possibly dangerous) coordinates, there is the problem of the second + client trying to slew a slewing scope. Local server drivers are + tricky to get right. There is no such thing as "ignorance is + bliss" here. + +
+

+

+ + This implementation has changed + from what was defined for Platform 5.5 as follows: + +

+

+ + The drivers are now installed in + the same folder as the local server executable. This makes deployment + cleaner because the whole driver can exist in a single folder + independently of other drivers. + +

+

+ + The ProgId and friendly name as + displayed by the Chooser are defined using attributes. This allows + driver dlls to be identified clearly and so avoids confusion with + other dlls that may be required such as interop dlls. + +

+

+ + Some changes have been made that + will facilitate generating multiple drivers of the same type. + +

+

+ + I've put some additional advice and + comments in the notes below in italics. + +

+

+ You're probably anxious to get going, but you really should read + through the Theory of Operation and + Detailed + Use and Deployment + below. +

+

You must do the following in order to complete your local server:

+
    +
  1. +

    + In the local server's project + properties, Application tab, change BOTH the AssemblyName and the + default assembly name to ASCOM.xxx (e.g., ASCOM.SuperScope). + This + may be done by default now. + +

    +
  2. +
  3. +

    + Add one or more driver skeleton + projects using the in-proc templates. You may use either the C# or + VB templates. Project name is not important (not used in ProgID) + choose something like TelescopeDriver. You will be changing the + substituted project name in these projects below. If you ensure that + the LocalServer and all the driver projects have the same NameSpace + e.g. ASCOM.SuperScope the renaming in section 6a will not be + required. +

    +
  4. +
  5. +

    + Develop and debug these driver + projects as normal in-process assemblies. This will be much simpler + because the driver and test code can be debugged in the same + process. +

    +
  6. +
  7. +

    + Build the LocalServer. +

    +
  8. +
  9. +

    + Set a reference to the local + server project in each of the driver skeleton + projects. +

    +
  10. +
  11. +

    In each skeleton driver project:

    +
      +
    1. +

      + Do a Find In Files for the + project name of the skeleton driver (e.g., TelescopeDriver) and + change it to match the project name of your local server (e.g. + SuperScope). You don't have to do this in the ReadMe.html file. + Everywhere else, however, is IMPORTANT. This sets the correct + namespace, progID, etc. If you're a bit more brave, you can use + Replace in Files. + This may not be needed if the correct + namespace and naming conventions have been followed when the + drivers and local server were generated. + +

      +
    2. +
    3. +

      + In project properties, + Application tab, change the assembly name to + ASCOM.localserverprojectname.drivertype, + (e.g., ASCOM.SuperScope.Telescope). +

      +
    4. +
    5. +

      + In project properties, + Application tab, click Assembly Information... +

      +
        +
      • +

        + Assure that Make assembly COM + visible is on (it should already be on). +

        +
      • +
      • +

        + Edit the Product Name to be the + "friendly name" of your driver as will be shown in the + Chooser. + Not used now, use the ServedClassName attribute + instead. + +

        +
      • +
      +
    6. +
    7. +

      + In project properties, Build tab, + turn off Register for COM Interop. +

      +
    8. +
    9. +

      + Modify the driver class declaration to inherit from + ReferenceCountedObjectBase. Examples:
      C#: +

      +
      +              public class Telescope :
      +              ReferenceCountedObjectBase,
      +              ITelescope
      +            
      +

      + VB: +

      +
      +              Public Class Telescope
      +              '==================================
      +              Inherits ReferenceCountedObjectBase
      +              Implements ITelescope
      +              '==================================
      +            
      +
    10. +
    11. +

      + In driver.cs/driver.vb, remove + the entire ASCOM Registration region +

      +
    12. +
    13. +

      + In driver.cs/driver.vb, remove + the private strings for driver ID and driver description. + They + may be needed internally, and if so should be set from the + associated attributes, ServedClassName for the description and + ProgId for the driver Id. + +

      +
    14. +
    15. +

      + Modify the class attributes by + adding the ServedClassName and ProgID attributes. The + ServedClassName attribute must be the friendly name shown as the + device name in the Chooser and the ProgId the progid of the driver + e.g. ASCOM.SuperScope.Telescope. The class header should look like + this: +

      +

      C#:

      +
      +              
      +                [Guid("0AE8B38D-10A1-4A8D-A5B7-1B050F74B48B")]  // set by the template
      +                [ProgId("ASCOM.SuperScope.Telescope")]
      +                [ServedClassName ("Super Scope Telescope")]
      +                [ClassInterface(ClassInterfaceType.None)]
      +                public class Telescope : ReferenceCountedObjectBase , ITelescope
      +              
      +            
      +
    16. +
    +
  12. +
+

+
+

+
    +
      +

      VB:

      +
      +          
      +            <Guid(“0AE8B38D-10A1-4A8D-A5B7-1B050F74B48B”)>
      +            <ProgId(“ASCOM.SuperScope.Telescope”)>
      +            <ServedClassName(“Super Scope Telescope”)>
      +            Public Class Telescope
      +            '==================================
      +            Inherits ReferenceCountedObjectBase
      +            Implements ITelescope
      +            '==================================
      +          
      +        
      +
    +
+

+ Add the following line to the driver + constructor, this sets the driver ID using the ProgId Attribute: +

+
    +
      +

      C#

      +
      +          
      +            s_csDriverID = Marshal.GenerateProgIdForType(this.GetType());
      +          
      +        
      +

      VB:

      +
      +          
      +            s_csDriverID = Marshal.GenerateProgIdForType(Me.GetType())
      +          
      +        
      +
    +
  1. +

    + Unless you're writing a + single-driver hub, you will have two or more driver types (e.g. + Telescope and Focuser) and thus two or more driver assembly projects + added. Presumably, these drivers need to share some resources (e.g. + a single COM port via Helper.Serial). + Put shared resources into + the SharedResources class provided + . There are some examples that + should give a clue, modify and delete these as required. +

    +
  2. +
  3. +

    + A shared serial port is already + provided (see SharedResources.cs) as SharedResources.SharedSerial + and it is an ASCOM Helper Serial object. You may wish to define + additional shared resources in static member variables with public + static accessor properties as is already done for SharedSerial. + Unfortunately, if you are a Visual Basic programmer, you will have + to make these additions in C#. +

    +
  4. +
  5. +

    + If you are writing a hub and don't + need the serial port, in SharedResources.cs you can remove the + public static SharedSerial property, the m_SharedSerial member in + the private data region, and the line in main that initializes it. + If you don't need any other shared resources for your hub, then you + can remove the SharedResources.cs file completely. +

    +
  6. +
  7. +

    + If you modified the LocalServer, + build it again now. This will refresh the stuff that's visible to + the drivers. +

    +
  8. +
  9. +

    + Build the driver skeletons to + verify that you got all of the namespace and other variable changes. +

    +
  10. +
  11. +

    + The local server dynamically loads the driver assemblies from + the same folder as the local server executable.
    +
    During + development, you'll need to add a post-build task to each of your + driver assembly projects which puts a copy of the driver assembly + into the local server executable folder. Here is an example: +

    +
        copy "$(TargetPath)" "$(SolutionDir)\SuperScope\$(OutDir)\$(TargetFileName)"
    +

    + This assumes that the server project is called “SuperScope”, + and handles using the debug or release build.
    + Note the quotes for + possible path elements with spaces in them. + An alternative is to + set the build path to the required destination instead of the + default path. + +

    +
  12. +
  13. +

    + + Make sure the drivers are + registered through the local server by running it with the /register + parameter, see below for details. + +

    +
  14. +
  15. +

    + IMPORTANT: + With a local server based driver (or hub) it is possible for + multiple clients to control the device(s). It is up to you to + safeguard against abuse. + The sort of thing that's needed is to + have a counter of the number of connections to a device, the + connection is only fully broken when the number of connections is + zero. You may also need code to prevent several drivers from talking + to the hardware at the same time, the lock pattern is useful for + that. + +

    +
  16. +
  17. +

    + You may want to add controls and/or status information to the + main form frmMain of the local server. Please resist the temptation + to turn the local server's main form into a graphical device control + panel. Instead, make a separate application that uses the served + driver(s). A driver is not a program! +

    +
  18. +
+

Notes

+
    +
  • +

    + The local server handles all of + the registration and unregistration for each of its served driver + classes, including the ASCOM Chooser info and the DCOM/AppID info + needed for activation from TheSky. By running the server from a + command line and giving /register or /unregister as the command line + option, it will register or unregister all served classes + (respectively). + Never use REGASM + on the local server executable! + + This can be done in the + Visual Studio IDE by setting the server project to run as startup + and setting the command line argument to /register in Debug – + Start Options. + +

    +
  • +
  • +

    + When you make the installer for + your local server based driver/hub, do not let it register the + executable for COM. Instead, have it activate the installed local + server with the /register option. +

    +
  • +
  • +

    + The ASCOM registration uses the ServedClassName attribute as + the friendly name that will show in the chooser and the ProgId + attribute as the driver Id. +

    +
  • +
  • +

    + The best deployment way is to install all the files in a + folder that's a sub folder of the main driver, so the SuperScope + driver files will be in the folder ...\ASCOM\Telescope\SuperScope. + This can be done in the Inno script by changing the DefaultDirName + like this:
    + DefaultDirName="{cf}\ASCOM\Telescope\SuperScope"
    then + the files can all be installed with DestDir: {app}; +

    +
  • +
+

+ Theory of Operation +

+

+ The local server is an executable which can provide multiple + instances of multiple drivers to multiple clients. This capability is + needed for two applications: +

+
    +
  • +

    + A hub, which allows multiple + clients to share a single device +

    +
  • +
  • +

    + A device which provides multiple services, such as a + telescope which has a focuser built-in where both the telescope and + focuser are controlled by the same serial connection and different + client programs need to control to the focuser and telescope. +

    +
  • +
+

+ By simply dropping suitably developed driver assemblies into the + same folder as the local server executable, the local server will + find them and register them for COM and ASCOM and serve any number of + instances of the drivers' interfaces to any number of client + programs. It does this by locating and loading the driver assemblies, + analysing them to detect their classes and interfaces, and + implementing a class factory that can create instances of them for + clients. +

+

+ A driver is an assembly which contains a class that implements + one of the ASCOM standard driver interfaces and inherits the + ReferenceCountedObjectBase class of the local server. Apart from + that, driver assemblies are identical to those that are used + in-process (DLL-type). The instructions above detail the steps needed + to convert an in-process driver into one that can be served by the + local server. +

+

+ The name of the local server is important, so we provide it as a + template from which you can create a local server for your + produce. To make this clear, let's assume that your company AlphaTech + produces a telescope system which contains a microcontroller that is + able to control not only the telescope mount, but also a focuser and + a camera rotator. The mount, focuser, and rotator are all controlled + via commands sent through a common serial line connecting the + computer to the microcontroller, so you need a local server. In + ASCOM, then, you probably want your system to appear as + AlphaTech.Telescope, AlphaTech.Focuser, and AlphaTech.Rotator. Then + you would name the local server AlphaTech. Be sure to give this due + consideration before creating the template, the project name is the + name of your local server. + Is this still correct? I get the + impression that ASCOM.AlphaTech.Server would be OK. + +

+

+ The fact that driver classes inherit from the local server's + ReferenceCountedObjectBase class allows the local server to maintain + a reference count on the driver class. If a client creates an + instance of a served driver, the local server automatically starts up + and provides an instance of the class to the client. Once started the + local server can provide additional instances of any of its served + driver classes. If the reference count of all served classes drops to + zero as a result of clients releasing their instances, the local + server will automatically exit. +

+

+ Registration services provided include not only the basic COM + class registration, but also DCOM/AppID info needed to use the served + classes from outbound connections from Software Bisque's TheSky. It + also registers the served classes for the ASCOM Chooser. The + "friendly" name of each served driver that appears in the + chooser comes from the driver's ServedClassName attribute. This also + used to identify a driver so that non driver dlls, such as Interop + dlls can be ignored. The COM ProgID for each served driver is + specified in the ProgId attribute - ASCOM.localservername.drivertype, + for example, ASCOM.AlphaTech.Telescope, where AlphaTech is the local + server name and Telescope is the type of the driver. Unregistering + removes all of this information from the system. Specifying the + ProgId as an attribute allows multiple driver assemblies to be + generated using the same source and namespace. This is used to + provide multiple instances of the same driver, each with a different + ProgId and so able to be registered separately. +

+

+ Driver DLLs are identified for registering/unregistering because + they contain a type with the ServedClassName attribute. Only these + will be registered for Com and ASCOM. This has changed; in Platform + 5 there was no attribute and the local server attempted to register + all dlls. The new behaviour allows support dlls such as interop dlls + to be included without them being registered incorrectly. There was + also an interim version where the ServedClassName attribute was on + the assembly, not the class. + All these previous versions, and the + new drivers will operate together with Platform 6, the changes are + local to the individual drivers. + +

+

+ Detailed Use and Deployment +

+

+ Once you have built your local server and the served driver class + assemblies, here's how to use it. To register the served classes, + activate the local server from a shell command line with the option + /register (or /regserver, for VB6 compatibility): +

+
+      C:\xxx> localserver.exe /register
+    
+

+ To unregister the local server and its drivers, activate the local + server from a shell command line with the option /unregister (or + /unregserver for VB6 compatibility): +

+
+      C:\xxx> localserver.exe /unregister
+    
+

+ When the operating system starts the local server in response to a + client creating one of it's served driver classes, the command option + /embedding is included. The local server's code detects this and sets + a variable that you can use. +

+

+ When deploying a hub or set of drivers + with the local server, you'll have to arrange for the local server + and the driver assemblies to be placed together in a folder in the + ASCOM driver folder. Any support files, such as Interop DLLs can be + put in the same fiolder. That's all you need to do, the local server + will find them in the same folder as it is located in. +

+
+ + + + + + + +
+ + + + + +
+

ASCOM Initiative

+
+ +
+

+
+
+ +

+
+

+ The ASCOM Initiative consists of a group of astronomy software + developers and instrument vendors whose goals are to promote the + driver/client model and scripting automation. +

+

+ See the + ASCOM + web site + for more information. Please participate in the + + ASCOM-Talk + Group + . +

+
+
+

+
+
+ +

+

+
+
+ +

+ + \ No newline at end of file diff --git a/LynxAstro.DewController/ReferenceCountedObject.cs b/LynxAstro.DewController/ReferenceCountedObject.cs new file mode 100644 index 0000000..fe86948 --- /dev/null +++ b/LynxAstro.DewController/ReferenceCountedObject.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; + +namespace ASCOM.LynxAstro.DewController +{ + [ComVisible(false)] + public class ReferenceCountedObjectBase + { + public ReferenceCountedObjectBase() + { + // We increment the global count of objects. + Server.CountObject(); + } + + ~ReferenceCountedObjectBase() + { + // We decrement the global count of objects. + Server.UncountObject(); + // We then immediately test to see if we the conditions + // are right to attempt to terminate this server application. + Server.ExitIf(); + } + } +} diff --git a/LynxAstro.DewController/Resources/ASCOM.png b/LynxAstro.DewController/Resources/ASCOM.png new file mode 100644 index 0000000..a83b77b Binary files /dev/null and b/LynxAstro.DewController/Resources/ASCOM.png differ diff --git a/LynxAstro.DewController/SetupDialogForm.cs b/LynxAstro.DewController/SetupDialogForm.cs new file mode 100644 index 0000000..4e8d169 --- /dev/null +++ b/LynxAstro.DewController/SetupDialogForm.cs @@ -0,0 +1,89 @@ +using System; +using System.IO.Ports; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using ASCOM.LynxAstro.DewController.Properties; + +namespace ASCOM.LynxAstro.DewController +{ + + [ComVisible(false)] // Form not registered for COM! + public partial class SetupDialogForm : Form + { + public SetupDialogForm() + { + InitializeComponent(); + + var assemblyInfo = new AssemblyInfo(); + + Text = string.Format(Resources.SetupDialogForm_SetupDialogForm__0__Settings___1__, assemblyInfo.Product, assemblyInfo.AssemblyVersion); + } + + private void cmdOK_Click(object sender, EventArgs e) // OK button event handler + { + + } + + private void cmdCancel_Click(object sender, EventArgs e) // Cancel button event handler + { + Close(); + } + + private void BrowseToAscom(object sender, EventArgs e) // Click on ASCOM logo event handler + { + try + { + System.Diagnostics.Process.Start("https://ascom-standards.org/"); + } + catch (System.ComponentModel.Win32Exception noBrowser) + { + if (noBrowser.ErrorCode == -2147467259) + MessageBox.Show(noBrowser.Message); + } + catch (System.Exception other) + { + MessageBox.Show(other.Message); + } + } + + public void SetProfile(ProfileProperties profileProperties) + { + chkTrace.Checked = profileProperties.TraceLogger; + // set the list of com ports to those that are currently available + comboBoxComPort.Items.Clear(); + comboBoxComPort.Items.AddRange(SerialPort.GetPortNames().ToArray()); // use System.IO because it's static + // select the current port if possible + if (comboBoxComPort.Items.Contains(profileProperties.ComPort)) + { + comboBoxComPort.SelectedItem = profileProperties.ComPort; + } + } + + public ProfileProperties GetProfile() + { + var profileProperties = new ProfileProperties + { + TraceLogger = chkTrace.Checked, + ComPort = comboBoxComPort.SelectedItem.ToString(), + }; + + return profileProperties; + } + + public void SetReadOnlyMode() + { + foreach (Control control in Controls) + { + control.Enabled = false; + } + + cmdCancel.Enabled = true; + //cmdOK.Enabled = false; + //comboBoxComPort.Enabled = false; + //chkTrace.Enabled = false; + //txtGuideRate.Enabled = false; + //cboPrecision.Enabled = false; + } + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/SetupDialogForm.designer.cs b/LynxAstro.DewController/SetupDialogForm.designer.cs new file mode 100644 index 0000000..3d4fa24 --- /dev/null +++ b/LynxAstro.DewController/SetupDialogForm.designer.cs @@ -0,0 +1,149 @@ +namespace ASCOM.LynxAstro.DewController +{ + partial class SetupDialogForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cmdOK = new System.Windows.Forms.Button(); + this.cmdCancel = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.picASCOM = new System.Windows.Forms.PictureBox(); + this.label2 = new System.Windows.Forms.Label(); + this.chkTrace = new System.Windows.Forms.CheckBox(); + this.comboBoxComPort = new System.Windows.Forms.ComboBox(); + ((System.ComponentModel.ISupportInitialize)(this.picASCOM)).BeginInit(); + this.SuspendLayout(); + // + // cmdOK + // + this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cmdOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.cmdOK.Location = new System.Drawing.Point(281, 112); + this.cmdOK.Name = "cmdOK"; + this.cmdOK.Size = new System.Drawing.Size(59, 24); + this.cmdOK.TabIndex = 0; + this.cmdOK.Text = "OK"; + this.cmdOK.UseVisualStyleBackColor = true; + this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click); + // + // cmdCancel + // + this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cmdCancel.Location = new System.Drawing.Point(281, 142); + this.cmdCancel.Name = "cmdCancel"; + this.cmdCancel.Size = new System.Drawing.Size(59, 25); + this.cmdCancel.TabIndex = 1; + this.cmdCancel.Text = "Cancel"; + this.cmdCancel.UseVisualStyleBackColor = true; + this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(123, 31); + this.label1.TabIndex = 2; + this.label1.Text = "Construct your driver\'s setup dialog here."; + // + // picASCOM + // + this.picASCOM.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.picASCOM.Cursor = System.Windows.Forms.Cursors.Hand; + this.picASCOM.Image = global::ASCOM.LynxAstro.DewController.Properties.Resources.ASCOM; + this.picASCOM.Location = new System.Drawing.Point(292, 9); + this.picASCOM.Name = "picASCOM"; + this.picASCOM.Size = new System.Drawing.Size(48, 56); + this.picASCOM.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.picASCOM.TabIndex = 3; + this.picASCOM.TabStop = false; + this.picASCOM.Click += new System.EventHandler(this.BrowseToAscom); + this.picASCOM.DoubleClick += new System.EventHandler(this.BrowseToAscom); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 90); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(58, 13); + this.label2.TabIndex = 5; + this.label2.Text = "Comm Port"; + // + // chkTrace + // + this.chkTrace.AutoSize = true; + this.chkTrace.Location = new System.Drawing.Point(77, 118); + this.chkTrace.Name = "chkTrace"; + this.chkTrace.Size = new System.Drawing.Size(69, 17); + this.chkTrace.TabIndex = 6; + this.chkTrace.Text = "Trace on"; + this.chkTrace.UseVisualStyleBackColor = true; + // + // comboBoxComPort + // + this.comboBoxComPort.FormattingEnabled = true; + this.comboBoxComPort.Location = new System.Drawing.Point(77, 87); + this.comboBoxComPort.Name = "comboBoxComPort"; + this.comboBoxComPort.Size = new System.Drawing.Size(90, 21); + this.comboBoxComPort.TabIndex = 7; + // + // SetupDialogForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(350, 175); + this.Controls.Add(this.comboBoxComPort); + this.Controls.Add(this.chkTrace); + this.Controls.Add(this.label2); + this.Controls.Add(this.picASCOM); + this.Controls.Add(this.label1); + this.Controls.Add(this.cmdCancel); + this.Controls.Add(this.cmdOK); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SetupDialogForm"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "LynxAstro.DewController Setup"; + ((System.ComponentModel.ISupportInitialize)(this.picASCOM)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cmdOK; + private System.Windows.Forms.Button cmdCancel; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.PictureBox picASCOM; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.CheckBox chkTrace; + private System.Windows.Forms.ComboBox comboBoxComPort; + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/SetupDialogForm.resx b/LynxAstro.DewController/SetupDialogForm.resx new file mode 100644 index 0000000..d58980a --- /dev/null +++ b/LynxAstro.DewController/SetupDialogForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/LynxAstro.DewController/SharedResources.cs b/LynxAstro.DewController/SharedResources.cs new file mode 100644 index 0000000..a94085f --- /dev/null +++ b/LynxAstro.DewController/SharedResources.cs @@ -0,0 +1,380 @@ +// +// ================ +// Shared Resources +// ================ +// +// This class is a container for all shared resources that may be needed +// by the drivers served by the Local Server. +// +// NOTES: +// +// * ALL DECLARATIONS MUST BE STATIC HERE!! INSTANCES OF THIS CLASS MUST NEVER BE CREATED! +// +// Written by: Bob Denny 29-May-2007 +// Modified by Chris Rowland and Peter Simpson to hamdle multiple hardware devices March 2011 +// + +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using ASCOM.Utilities; +using ASCOM.Utilities.Interfaces; +using JetBrains.Annotations; + +namespace ASCOM.LynxAstro.DewController +{ + /// + /// The resources shared by all drivers and devices, in this example it's a serial port with a shared SendMessage method + /// an idea for locking the message and handling connecting is given. + /// In reality extensive changes will probably be needed. + /// Multiple drivers means that several applications connect to the same hardware device, aka a hub. + /// Multiple devices means that there are more than one instance of the hardware, such as two focusers. + /// In this case there needs to be multiple instances of the hardware connector, each with it's own connection count. + /// + public static class SharedResources + { + // object used for locking to prevent multiple drivers accessing common code at the same time + private static readonly object LockObject = new object(); + + // Shared serial port. This will allow multiple drivers to use one single serial port. + private static ISerial _sSharedSerial; // Shared serial port + + // + // Public access to shared resources + // + + #region single serial port connector + + // + // this region shows a way that a single serial port could be connected to by multiple + // drivers. + // + // Connected is used to handle the connections to the port. + // + // SendMessage is a way that messages could be sent to the hardware without + // conflicts between different drivers. + // + // All this is for a single connection, multiple connections would need multiple ports + // and a way to handle connecting and disconnection from them - see the + // multi driver handling section for ideas. + // + + /// + /// Shared serial port. Do not directly access this method. + /// + public static ISerial SharedSerial + { + get => _sSharedSerial ?? (_sSharedSerial = new Serial()); + set => _sSharedSerial = value; + } + + public static IProfileFactory ProfileFactory + { + get => _profileFactory ?? (_profileFactory = new ProfileFactory()); + set => _profileFactory = value; + } + + //todo add code to ensure that there is a minimum gap between commands. 5ms as default. + public static void SendBlind(string message) + { + lock (LockObject) + { + SharedSerial.ClearBuffers(); + SharedSerial.Transmit(message); + } + } + + /// + /// Example of a shared SendMessage method, the lock + /// prevents different drivers tripping over one another. + /// It needs error handling and assumes that the message will be sent unchanged + /// and that the reply will always be terminated by a "#" character. + /// + /// + /// + public static string SendString(string message) + { + lock (LockObject) + { + SharedSerial.ClearBuffers(); + SharedSerial.Transmit(message); + return SharedSerial.ReceiveTerminated("#").TrimEnd('#'); + } + } + + public static string SendChar(string message) + { + lock (LockObject) + { + SharedSerial.ClearBuffers(); + SharedSerial.Transmit(message); + return SharedSerial.ReceiveCounted(1); + } + } + + public static string ReadTerminated() + { + lock (LockObject) + { + return SharedSerial.ReceiveTerminated("#"); + } + } + + public static void ReadCharacters(int throwAwayCharacters) + { + lock (LockObject) + { + SharedSerial.ReceiveCounted(throwAwayCharacters); + } + } + + #endregion + + #region Profile + + private const string DriverId = "ASCOM.LynxAstro.DewController.Switch"; + + // Constants used for Profile persistence + private const string ComPortProfileName = "COM Port"; + private const string TraceStateProfileName = "Trace Level"; + private const string SwitchNameProfileName = "SwitchName_{0}"; + + public static void WriteProfile(ProfileProperties profileProperties) + { + lock (LockObject) + { + using (IProfileWrapper driverProfile = ProfileFactory.Create()) + { + driverProfile.DeviceType = "Switch"; + driverProfile.WriteValue(DriverId, TraceStateProfileName, profileProperties.TraceLogger.ToString()); + driverProfile.WriteValue(DriverId, ComPortProfileName, profileProperties.ComPort); + } + } + } + + private const string ComPortDefault = "COM1"; + private const string TraceStateDefault = "false"; + + private const string noSwitchValue = "no_switch_value"; + + public static ProfileProperties ReadProfile() + { + lock (LockObject) + { + ProfileProperties profileProperties = new ProfileProperties(); + using (IProfileWrapper driverProfile = ProfileFactory.Create()) + { + driverProfile.DeviceType = "Switch"; + profileProperties.ComPort = driverProfile.GetValue(DriverId, ComPortProfileName, string.Empty, ComPortDefault); + profileProperties.TraceLogger = Convert.ToBoolean(driverProfile.GetValue(DriverId, TraceStateProfileName, string.Empty, TraceStateDefault)); + + var switchNo = 0; + var finished = false; + profileProperties.SwitchNames.Clear(); + do + { + var switchName = string.Format(SwitchNameProfileName, switchNo); + var switchValue = driverProfile.GetValue(DriverId, switchName, string.Empty, noSwitchValue); + + finished = switchValue == noSwitchValue; + + if (!finished) + profileProperties.SwitchNames.Add(switchValue); + + switchNo++; + } while (!finished); + + } + + return profileProperties; + } + } + + #endregion + + #region SetupDialog + + public static void SetupDialog() + { + var profileProperties = ReadProfile(); + + using (SetupDialogForm f = new SetupDialogForm()) + { + f.SetProfile(profileProperties); + + if (IsConnected()) + { + f.SetReadOnlyMode(); + } + + var result = f.ShowDialog(); + if (result == DialogResult.OK) + { + profileProperties = f.GetProfile(); + + WriteProfile(profileProperties); // Persist device configuration values to the ASCOM Profile store + } + } + } + + #endregion + + #region Multi Driver handling + + public static string FirmwareVersion { get; private set; } = string.Empty; + + // this section illustrates how multiple drivers could be handled, + // it's for drivers where multiple connections to the hardware can be made and ensures that the + // hardware is only disconnected from when all the connected devices have disconnected. + + // It is NOT a complete solution! This is to give ideas of what can - or should be done. + // + // An alternative would be to move the hardware control here, handle connecting and disconnecting, + // and provide the device with a suitable connection to the hardware. + // + /// + /// dictionary carrying device connections. + /// The Key is the connection number that identifies the device, it could be the COM port name, + /// USB ID or IP Address, the Value is the DeviceHardware class + /// + private static readonly Dictionary ConnectedDevices = new Dictionary(); + + private static readonly Dictionary ConnectedDeviceIds = new Dictionary(); + private static IProfileFactory _profileFactory; + + + /// + /// This is called in the driver Connect(true) property, + /// it add the device id to the list of devices if it's not there and increments the device count. + /// + /// + /// + /// + public static ConnectionInfo Connect(string deviceId, string driverId, ITraceLogger traceLogger) + { + lock (LockObject) + { + if (!ConnectedDevices.ContainsKey(deviceId)) + ConnectedDevices.Add(deviceId, new DeviceHardware()); + + if (!ConnectedDeviceIds.ContainsKey(driverId)) + ConnectedDeviceIds.Add(driverId, new DeviceHardware()); + + if (deviceId == "Serial") + { + if (ConnectedDevices[deviceId].Count == 0) + { + var profileProperties = ReadProfile(); + SharedSerial.PortName = profileProperties.ComPort; + //SharedSerial.DTREnable = profileProperties.RtsDtrEnabled; + //SharedSerial.RTSEnable = profileProperties.RtsDtrEnabled; + SharedSerial.DataBits = 8; + SharedSerial.StopBits = SerialStopBits.One; + SharedSerial.Parity = SerialParity.None; + SharedSerial.Speed = SerialSpeed.ps9600; + SharedSerial.Handshake = SerialHandshake.None; + SharedSerial.Connected = true; + } + + try + { + var encodedString = SendString(":GV#"); + FirmwareVersion = DecodeResult(":GV", encodedString ); + //Command: :GV# + //Purpose: Get the devices firmware version. + //Response: :GVXXXXXXXX# where X is a version string, e.g. 1.0. + } + catch (Exception ex) + { + traceLogger.LogIssue("Connect", $"Error getting telescope information \"{ex.Message}\" setting to LX200 Classic mode."); + FirmwareVersion = "Unknown"; + } + } + else + throw new ArgumentException($"deviceId {deviceId} not currently supported"); + + ConnectedDevices[deviceId].Count++; // increment the value + ConnectedDeviceIds[driverId].Count++; // increment the value + + return new ConnectionInfo + { + //Connections = ConnectedDevices[deviceId].Count, + SameDevice = ConnectedDeviceIds[driverId].Count + }; + } + } + + private static string DecodeResult(string pattern, string encodedString) + { + var decodedString = encodedString.Substring( pattern.Length ).TrimEnd('#'); + return decodedString; + } + + public static void Disconnect(string deviceId, string driverId) + { + lock (LockObject) + { + if (ConnectedDevices.ContainsKey(deviceId)) + { + ConnectedDevices[deviceId].Count--; + if (ConnectedDevices[deviceId].Count <= 0) + { + ConnectedDevices.Remove(deviceId); + if (deviceId == "Serial") + { + SharedSerial.Connected = false; + } + } + } + + if (ConnectedDeviceIds.ContainsKey(driverId)) + { + ConnectedDeviceIds[driverId].Count--; + } + } + } + + private static bool IsConnected() + { + foreach (var device in ConnectedDevices) + { + if (device.Value.Count > 0) + return true; + } + + return false; + } + + #endregion + + public static void Lock(Action action) + { + lock (LockObject) + { + action(); + } + } + + public static T Lock(Func func) + { + lock (LockObject) + { + return func(); + } + } + + /// + /// Skeleton of a hardware class, all this does is hold a count of the connections, + /// in reality extra code will be needed to handle the hardware in some way + /// + private class DeviceHardware + { + internal int Count { set; get; } + + internal DeviceHardware() + { + Count = 0; + } + } + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/SharedResourcesWrapper.cs b/LynxAstro.DewController/SharedResourcesWrapper.cs new file mode 100644 index 0000000..b4da760 --- /dev/null +++ b/LynxAstro.DewController/SharedResourcesWrapper.cs @@ -0,0 +1,70 @@ +using System; +using ASCOM.Utilities.Interfaces; + +namespace ASCOM.LynxAstro.DewController +{ + public class SharedResourcesWrapper : ISharedResourcesWrapper + { + public ConnectionInfo Connect(string deviceId, string driverId, ITraceLogger traceLogger) + { + return SharedResources.Connect(deviceId, driverId, traceLogger); + } + + public void Disconnect(string deviceId, string driverId) + { + SharedResources.Disconnect(deviceId, driverId); + } + + public string FirmwareVersion => SharedResources.FirmwareVersion; + + public void Lock(Action action) + { + SharedResources.Lock(action); + } + + public T Lock(Func func) + { + return SharedResources.Lock(func); + } + + public string SendString(string message) + { + return SharedResources.SendString(message); + } + + public void SendBlind(string message) + { + SharedResources.SendBlind(message); + } + + public string SendChar(string message) + { + return SharedResources.SendChar(message); + } + + public string ReadTerminated() + { + return SharedResources.ReadTerminated(); + } + + public void ReadCharacters(int throwAwayCharacters) + { + SharedResources.ReadCharacters(throwAwayCharacters); + } + + public ProfileProperties ReadProfile() + { + return SharedResources.ReadProfile(); + } + + public void SetupDialog() + { + SharedResources.SetupDialog(); + } + + public void WriteProfile(ProfileProperties profileProperties) + { + SharedResources.WriteProfile(profileProperties); + } + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/frmMain.Designer.cs b/LynxAstro.DewController/frmMain.Designer.cs new file mode 100644 index 0000000..b9d0061 --- /dev/null +++ b/LynxAstro.DewController/frmMain.Designer.cs @@ -0,0 +1,63 @@ +using System; + +namespace ASCOM.LynxAstro.DewController +{ + partial class frmMain + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(12, 10); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(199, 33); + this.label1.TabIndex = 0; + this.label1.Text = "This is an ASCOM driver, not a program for you to use."; + // + // frmMain + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(233, 52); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "frmMain"; + this.Text = "LynxAstro.DewController Driver Server"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label label1; + + } +} + diff --git a/LynxAstro.DewController/frmMain.cs b/LynxAstro.DewController/frmMain.cs new file mode 100644 index 0000000..aee7282 --- /dev/null +++ b/LynxAstro.DewController/frmMain.cs @@ -0,0 +1,15 @@ +using System.Windows.Forms; + +namespace ASCOM.LynxAstro.DewController +{ + public partial class frmMain : Form + { + delegate void SetTextCallback(string text); + + public frmMain() + { + InitializeComponent(); + } + + } +} \ No newline at end of file diff --git a/LynxAstro.DewController/frmMain.resx b/LynxAstro.DewController/frmMain.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/LynxAstro.DewController/frmMain.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/LynxAstro.DewController/packages.config b/LynxAstro.DewController/packages.config new file mode 100644 index 0000000..a95f5c2 --- /dev/null +++ b/LynxAstro.DewController/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/build.build b/build.build new file mode 100644 index 0000000..c70e15a --- /dev/null +++ b/build.build @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file