├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── validate-openapi.yml ├── .gitignore ├── .redocly.lint-ignore.yaml ├── ASCOM Remote.sln ├── BuildRemote.cmd ├── Documentation ├── ASCOM Alpaca API Reference.docx ├── ASCOM Alpaca API Reference.pdf ├── ASCOM Distributed Architecture.docx ├── ASCOM Distributed Architecture.pdf ├── ASCOM Remote Installation and Configuration.docx ├── ASCOM Remote Installation and Configuration.pdf ├── ASCOM Remote.vsdx ├── Alpaca Introduction.docx ├── Alpaca Introduction.pdf ├── AlpacaImageBytes.docx ├── AlpacaImageBytes.pdf └── ImageBytesDiagrams.vsdx ├── LICENSE ├── NuGet.config ├── README.md ├── Remote Server Key.snk ├── Remote Server ├── ASCOM.ico ├── ASCOMAlpacaMidRes.jpg ├── AlpacaConfiguredDevice.cs ├── AlpacaDeviceDescription.cs ├── App.config ├── Configuration.cs ├── ConfigurationManager.cs ├── ConfiguredDevice.cs ├── DriverHostForm.Designer.cs ├── DriverHostForm.cs ├── DriverHostForm.resx ├── Entity Classes │ └── ActiveObject.cs ├── ExtensionMethods.cs ├── HideTabControlBorders.cs ├── HostPc.cs ├── InvalidParameterException.cs ├── ProfileDevice.cs ├── Program.cs ├── Remote Server.csproj ├── RequestData.cs ├── ResponseClasses │ ├── AlpacaConfiguredDevicesResponse.cs │ ├── AlpacaDescriptionResponse.cs │ ├── AlpacaDiscoveryResponse.cs │ ├── AxisRatesResponse.cs │ ├── Base64ArrayHandOffResponse.cs │ ├── Base64ArrayJsonResponse.cs │ ├── BoolResponse.cs │ ├── ConfigurationResponse.cs │ ├── DateAndTimeResponse.cs │ ├── DoubleArray2DResponse.cs │ ├── DoubleArray3DResponse.cs │ ├── DoubleResponse.cs │ ├── ImageArrayResponseBase.cs │ ├── IntArray1DResponse.cs │ ├── IntArray2DResponse.cs │ ├── IntArray3DResponse.cs │ ├── IntResponse.cs │ ├── MethodResponse.cs │ ├── ProfileResponse.cs │ ├── RateResponse.cs │ ├── RestResponseBase.cs │ ├── ShortArray2DResponse.cs │ ├── ShortArray3DResponse.cs │ ├── ShortResponse.cs │ ├── StringArrayResponse.cs │ ├── StringListResponse.cs │ ├── StringResponse.cs │ └── TrackingRatesResponse.cs ├── ServedDevice.cs ├── ServerForm.Designer.cs ├── ServerForm.cs ├── ServerForm.resx ├── Settings.cs ├── SetupForm.Designer.cs ├── SetupForm.cs ├── SetupForm.resx ├── Shared Constants.cs ├── SharedResources.cs ├── StringValue.cs ├── TraceLoggerPlus.cs ├── Updates │ ├── GitHubReleases.cs │ └── Updates.cs ├── WindowsErrorCodes.cs ├── ascomicon.ico ├── servedDevice.Designer.cs └── servedDevice.resx ├── SetNetworkPermissions ├── ASCOM.ico ├── Options.cs ├── Program.cs ├── SetNetworkPermissions.csproj ├── app.config └── app.manifest ├── Setup ├── ASCOM Remote Setup.iss ├── ASCOM.ico ├── ASCOMLogo.bmp └── NewWizardImage.bmp ├── SignASCOMRemote.cmd ├── Swagger ├── AlpacaDeviceAPI_v1.yaml ├── AlpacaDeviceAPI_v1P7.yaml ├── AlpacaManagementAPI_v1.yaml ├── bugt300square.jpg ├── favicon-16x16.png ├── favicon-32x32.png ├── index.html ├── oauth2-redirect.html └── redoclyconfig.yaml └── redocly.yaml /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # VSSpell001: Spell Check 4 | dotnet_diagnostic.VSSpell001.severity = none 5 | csharp_using_directive_placement = outside_namespace:silent 6 | csharp_prefer_simple_using_statement = true:suggestion 7 | csharp_prefer_braces = true:silent 8 | csharp_style_namespace_declarations = block_scoped:silent 9 | csharp_style_prefer_method_group_conversion = true:silent 10 | csharp_style_prefer_top_level_statements = true:silent 11 | csharp_style_expression_bodied_methods = false:silent 12 | csharp_style_expression_bodied_constructors = false:silent 13 | csharp_style_expression_bodied_operators = false:silent 14 | csharp_style_expression_bodied_properties = true:silent 15 | csharp_style_expression_bodied_indexers = true:silent 16 | csharp_style_expression_bodied_accessors = true:silent 17 | csharp_style_expression_bodied_lambdas = true:silent 18 | csharp_style_expression_bodied_local_functions = false:silent 19 | csharp_style_throw_expression = true:suggestion 20 | csharp_style_prefer_null_check_over_type_check = true:suggestion 21 | csharp_prefer_simple_default_expression = true:suggestion 22 | csharp_style_prefer_local_over_anonymous_function = true:suggestion 23 | csharp_style_prefer_index_operator = true:suggestion 24 | csharp_style_prefer_range_operator = true:suggestion 25 | csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion 26 | csharp_style_prefer_tuple_swap = true:suggestion 27 | csharp_style_prefer_utf8_string_literals = true:suggestion 28 | csharp_style_inlined_variable_declaration = true:suggestion 29 | csharp_style_deconstructed_variable_declaration = true:suggestion 30 | csharp_style_unused_value_assignment_preference = unused_local_variable:suggestion 31 | csharp_style_unused_value_expression_statement_preference = unused_local_variable:silent 32 | csharp_indent_labels = one_less_than_current 33 | csharp_space_around_binary_operators = before_and_after 34 | 35 | [*.{cs,vb}] 36 | #### Naming styles #### 37 | 38 | # Naming rules 39 | 40 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 41 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 42 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 43 | 44 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 45 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 46 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 47 | 48 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 49 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 50 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 51 | 52 | # Symbol specifications 53 | 54 | dotnet_naming_symbols.interface.applicable_kinds = interface 55 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 56 | dotnet_naming_symbols.interface.required_modifiers = 57 | 58 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 59 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 60 | dotnet_naming_symbols.types.required_modifiers = 61 | 62 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 63 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 64 | dotnet_naming_symbols.non_field_members.required_modifiers = 65 | 66 | # Naming styles 67 | 68 | dotnet_naming_style.begins_with_i.required_prefix = I 69 | dotnet_naming_style.begins_with_i.required_suffix = 70 | dotnet_naming_style.begins_with_i.word_separator = 71 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 72 | 73 | dotnet_naming_style.pascal_case.required_prefix = 74 | dotnet_naming_style.pascal_case.required_suffix = 75 | dotnet_naming_style.pascal_case.word_separator = 76 | dotnet_naming_style.pascal_case.capitalization = pascal_case 77 | 78 | dotnet_naming_style.pascal_case.required_prefix = 79 | dotnet_naming_style.pascal_case.required_suffix = 80 | dotnet_naming_style.pascal_case.word_separator = 81 | dotnet_naming_style.pascal_case.capitalization = pascal_case 82 | dotnet_style_coalesce_expression = true:suggestion 83 | dotnet_style_null_propagation = true:suggestion 84 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 85 | dotnet_style_prefer_auto_properties = true:silent 86 | dotnet_style_object_initializer = true:suggestion 87 | dotnet_style_collection_initializer = true:suggestion 88 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion 89 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 90 | dotnet_style_prefer_conditional_expression_over_return = true:silent 91 | dotnet_style_explicit_tuple_names = true:suggestion 92 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 93 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 94 | dotnet_style_prefer_compound_assignment = true:suggestion 95 | dotnet_style_prefer_simplified_interpolation = true:suggestion 96 | dotnet_style_namespace_match_folder = true:suggestion 97 | dotnet_style_readonly_field = true:suggestion 98 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 99 | dotnet_style_predefined_type_for_member_access = true:silent 100 | dotnet_style_operator_placement_when_wrapping = beginning_of_line 101 | tab_width = 4 102 | indent_size = 4 103 | end_of_line = crlf 104 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Tell Git Linguist to ignore Swagger content files when deciding what language 3 | # is used in the repository 4 | ############################################################################### 5 | Swagger/** linguist-documentation=true 6 | -------------------------------------------------------------------------------- /.github/workflows/validate-openapi.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | validate_openapi: 5 | runs-on: ubuntu-latest 6 | name: Validate OpenAPI definitions 7 | steps: 8 | - uses: actions/checkout@v4 9 | - name: Install OpenAPI validator 10 | run: npm install -g @redocly/cli 11 | - name: Validate Managment API 12 | run: redocly lint --config Swagger/redoclyconfig.yaml Swagger/AlpacaManagementAPI_v1.yaml 13 | - name: Validate Alpaca Device API 14 | run: redocly lint --config Swagger/redoclyconfig.yaml Swagger/AlpacaDeviceAPI_v1.yaml 15 | env: 16 | NODE_NO_WARNINGS: 1 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | Build 290 | /Swagger/AlpacaDeviceAPI_v1.yaml.org2 291 | -------------------------------------------------------------------------------- /.redocly.lint-ignore.yaml: -------------------------------------------------------------------------------- 1 | # This file instructs Redocly's linter to ignore the rules contained for specific parts of your API. 2 | # See https://redoc.ly/docs/cli/ for more information. 3 | Swagger/AlpacaManagementAPI_v1.yaml: 4 | # /setup endpoint must always be present 5 | operation-4xx-response: 6 | - '#/paths/~1setup/get/responses' 7 | -------------------------------------------------------------------------------- /ASCOM Remote.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33627.172 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetNetworkPermissions", "SetNetworkPermissions\SetNetworkPermissions.csproj", "{FF57CAEA-63F3-4F4F-9252-734796B74731}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Remote Server", "Remote Server\Remote Server.csproj", "{15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0B2836E8-6FC8-4616-8E83-CBCEB4E01784}" 11 | ProjectSection(SolutionItems) = preProject 12 | .editorconfig = .editorconfig 13 | NuGet.config = NuGet.config 14 | Remote Server Key.snk = Remote Server Key.snk 15 | EndProjectSection 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Debug|x64 = Debug|x64 21 | Debug|x86 = Debug|x86 22 | Release|Any CPU = Release|Any CPU 23 | Release|x64 = Release|x64 24 | Release|x86 = Release|x86 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Debug|x64.ActiveCfg = Debug|x64 30 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Debug|x64.Build.0 = Debug|x64 31 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Debug|x86.ActiveCfg = Debug|x86 32 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Debug|x86.Build.0 = Debug|x86 33 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Release|x64.ActiveCfg = Release|x64 36 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Release|x64.Build.0 = Release|x64 37 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Release|x86.ActiveCfg = Release|x86 38 | {FF57CAEA-63F3-4F4F-9252-734796B74731}.Release|x86.Build.0 = Release|x86 39 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Debug|x64.ActiveCfg = Debug|x64 42 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Debug|x64.Build.0 = Debug|x64 43 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Debug|x86.ActiveCfg = Debug|x86 44 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Debug|x86.Build.0 = Debug|x86 45 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Release|x64.ActiveCfg = Release|x64 48 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Release|x64.Build.0 = Release|x64 49 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Release|x86.ActiveCfg = Release|x86 50 | {15D5962B-2A5E-4B07-98B6-D4D4A888F9B7}.Release|x86.Build.0 = Release|x86 51 | EndGlobalSection 52 | GlobalSection(SolutionProperties) = preSolution 53 | HideSolutionNode = FALSE 54 | EndGlobalSection 55 | GlobalSection(ExtensibilityGlobals) = postSolution 56 | SolutionGuid = {171EDC5A-81C8-4545-BB43-723B4AE9B622} 57 | EndGlobalSection 58 | EndGlobal 59 | -------------------------------------------------------------------------------- /BuildRemote.cmd: -------------------------------------------------------------------------------- 1 | @echo on 2 | @echo Setting up variables 3 | call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 4 | @echo Selecting Remote directory 5 | 6 | SET RemoteDirectory=%cd% 7 | @echo Current directory: %RemoteDirectory% 8 | 9 | @echo Restoring packages 10 | msbuild /t:restore /p:configuration=Debug;Platform="Any CPU" 11 | 12 | @echo Building Debug AnyCPU in directory %cd% 13 | msbuild /t:rebuild /p:configuration=Debug;Platform="Any CPU" 14 | 15 | cd %RemoteDirectory% 16 | @echo Building Release AnyCPU in directory %cd% 17 | msbuild /t:rebuild /p:configuration=Release;Platform="Any CPU" 18 | 19 | cd %RemoteDirectory% 20 | rmdir /s /q "publish" 21 | @echo Publishing ASCOM Remote x86 22 | dotnet publish "remote server\remote server.csproj" --runtime win-x86 --self-contained -p:Configuration=Debug -p:Platform="x86" -p:publishsinglefile=true -o publish\remote\x86\ 23 | 24 | @echo Publishing ASCOM Remote x64 25 | dotnet publish "remote server\remote server.csproj" --runtime win-x64 --self-contained -p:Configuration=Debug -p:Platform="Any CPU" -p:publishsinglefile=true -o publish\remote\x64\ 26 | 27 | @echo Publishing Set Network Permissions x86 28 | dotnet publish "SetNetworkPermissions\SetNetworkPermissions.csproj" --runtime win-x86 --self-contained -p:Configuration=Debug -p:Platform="x86" -p:publishsinglefile=true -o publish\permissions\x86\ 29 | 30 | rem @echo Publishing Set Network Permissions x64 31 | rem dotnet publish "SetNetworkPermissions\SetNetworkPermissions.csproj" --runtime win-x64 --self-contained -p:Configuration=Debug -p:Platform="Any CPU" -p:publishsinglefile=true -o publish\permissions\x64\ 32 | 33 | rem @echo Publishing ASCOM Remote x86 - Needs Support Library 34 | rem dotnet publish "remote server\remote server.csproj" --runtime win-x86 -p:Configuration=Debug --self-contained false -p:Platform="x86" -p:publishsinglefile=true -o publish\x86NeedsCoreInstall\ 35 | rem @echo Publishing ASCOM Remote x64 - Needs Support Library 36 | rem dotnet publish "remote server\remote server.csproj" --runtime win-x64 -p:Configuration=Debug --self-contained false -p:Platform="Any CPU" -p:publishsinglefile=true -o publish\x64NeedsCoreInstall\ 37 | 38 | @echo *** Creating Windows installer 39 | cd Setup 40 | "C:\Program Files (x86)\Inno Script Studio\isstudio.exe" -compile "ASCOM Remote Setup.iss" 41 | cd .. 42 | @echo *** Finsihed! 43 | pause -------------------------------------------------------------------------------- /Documentation/ASCOM Alpaca API Reference.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/ASCOM Alpaca API Reference.docx -------------------------------------------------------------------------------- /Documentation/ASCOM Alpaca API Reference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/ASCOM Alpaca API Reference.pdf -------------------------------------------------------------------------------- /Documentation/ASCOM Distributed Architecture.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/ASCOM Distributed Architecture.docx -------------------------------------------------------------------------------- /Documentation/ASCOM Distributed Architecture.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/ASCOM Distributed Architecture.pdf -------------------------------------------------------------------------------- /Documentation/ASCOM Remote Installation and Configuration.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/ASCOM Remote Installation and Configuration.docx -------------------------------------------------------------------------------- /Documentation/ASCOM Remote Installation and Configuration.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/ASCOM Remote Installation and Configuration.pdf -------------------------------------------------------------------------------- /Documentation/ASCOM Remote.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/ASCOM Remote.vsdx -------------------------------------------------------------------------------- /Documentation/Alpaca Introduction.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/Alpaca Introduction.docx -------------------------------------------------------------------------------- /Documentation/Alpaca Introduction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/Alpaca Introduction.pdf -------------------------------------------------------------------------------- /Documentation/AlpacaImageBytes.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/AlpacaImageBytes.docx -------------------------------------------------------------------------------- /Documentation/AlpacaImageBytes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/AlpacaImageBytes.pdf -------------------------------------------------------------------------------- /Documentation/ImageBytesDiagrams.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Documentation/ImageBytesDiagrams.vsdx -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASCOMRemote 2 | The ASCOM REST based Remote Driver Server and Remote Access Clients 3 | 4 | Contains the definition of the ASCOM REST device interfaces together with device drivers that can present remote network based drivers, 5 | connected through TCP/IP as local devices.access remote -------------------------------------------------------------------------------- /Remote Server Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Remote Server Key.snk -------------------------------------------------------------------------------- /Remote Server/ASCOM.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Remote Server/ASCOM.ico -------------------------------------------------------------------------------- /Remote Server/ASCOMAlpacaMidRes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Remote Server/ASCOMAlpacaMidRes.jpg -------------------------------------------------------------------------------- /Remote Server/AlpacaConfiguredDevice.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class AlpacaConfiguredDevice 4 | { 5 | public AlpacaConfiguredDevice() { } 6 | 7 | public AlpacaConfiguredDevice(string deviceName,string deviceType,int deviceNumber,string uniqueID) 8 | { 9 | DeviceName = deviceName; 10 | DeviceType = deviceType; 11 | DeviceNumber = deviceNumber; 12 | UniqueID = uniqueID; 13 | } 14 | 15 | public string DeviceName { get; set; } 16 | public string DeviceType { get; set; } 17 | public int DeviceNumber { get; set; } 18 | public string UniqueID { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Remote Server/AlpacaDeviceDescription.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class AlpacaDeviceDescription 4 | { 5 | public AlpacaDeviceDescription() { } 6 | 7 | public AlpacaDeviceDescription(string serverName, string manufacturer, string manufacturerVersion, string location) 8 | { 9 | ServerName = serverName; 10 | Manufacturer = manufacturer; 11 | ManufacturerVersion = manufacturerVersion; 12 | Location = location; 13 | } 14 | 15 | public string ServerName { get; set; } = ""; 16 | public string Manufacturer { get; set; } = ""; 17 | public string ManufacturerVersion { get; set; } = ""; 18 | public string Location { get; set; } = ""; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Remote Server/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Remote Server/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Microsoft.Win32; 4 | 5 | namespace ASCOM.Remote 6 | { 7 | class Configuration : IDisposable 8 | { 9 | private readonly bool LOG_CONFIGURATION_CALLS = false; // Stored as a variable rather than a const to avoid compiler warnings about unreachable code 10 | 11 | private readonly RegistryKey hiveKey, baseRegistryKey; 12 | 13 | public static void Reset() 14 | { 15 | try 16 | { 17 | RegistryKey hiveKey = RegistryKey.OpenBaseKey(SharedConstants.ASCOM_REMOTE_CONFIGURATION_HIVE, RegistryView.Default); 18 | hiveKey.DeleteSubKeyTree(SharedConstants.ASCOM_REMOTE_CONFIGURATION_KEY); 19 | } 20 | catch (Exception ex) 21 | { 22 | Console.WriteLine($"Exception reseting configuration:\r\n{ex}"); 23 | } 24 | } 25 | 26 | public Configuration() 27 | { 28 | try 29 | { 30 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "Configuration New", "About to create base key"); 31 | hiveKey = RegistryKey.OpenBaseKey(SharedConstants.ASCOM_REMOTE_CONFIGURATION_HIVE, RegistryView.Default); 32 | baseRegistryKey = hiveKey.CreateSubKey(SharedConstants.ASCOM_REMOTE_CONFIGURATION_KEY); 33 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "Configuration New", "Created base key: " + baseRegistryKey.Name); 34 | } 35 | catch (Exception ex) 36 | { 37 | ServerForm.LogException(0, 0, 0, "Configuration New", ex.ToString()); 38 | } 39 | } 40 | 41 | public T GetValue(string KeyName, string SubKey, T DefaultValue) 42 | { 43 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", $"Getting {typeof(T).Name} value '{KeyName}' in subkey '{SubKey}', default: '{DefaultValue}'"); 44 | 45 | if (typeof(T) == typeof(bool)) 46 | { 47 | string registryValue; 48 | if (SubKey == "") 49 | { 50 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey is empty so getting value directly"); 51 | registryValue = (string)baseRegistryKey.GetValue(KeyName); 52 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + registryValue); 53 | } 54 | else 55 | { 56 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey has a value so using it..."); 57 | registryValue = (string)baseRegistryKey.CreateSubKey(SubKey).GetValue(KeyName); 58 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + registryValue); 59 | } 60 | 61 | if (registryValue == null) 62 | { 63 | SetValueInvariant(KeyName, SubKey, DefaultValue); 64 | bool defaultValue = Convert.ToBoolean(DefaultValue); 65 | registryValue = defaultValue.ToString(CultureInfo.InvariantCulture); 66 | } 67 | 68 | bool RetVal = Convert.ToBoolean(registryValue, CultureInfo.InvariantCulture); 69 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", $"Retrieved {KeyName} = {RetVal}"); 70 | return (T)((object)RetVal); 71 | } 72 | 73 | if (typeof(T) == typeof(string)) 74 | { 75 | string RetVal; 76 | if (SubKey == "") 77 | { 78 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey is empty so getting value directly"); 79 | RetVal = (string)baseRegistryKey.GetValue(KeyName); 80 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + RetVal); 81 | } 82 | else 83 | { 84 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey has a value so using it..."); 85 | RetVal = (string)baseRegistryKey.CreateSubKey(SubKey).GetValue(KeyName); 86 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + RetVal); 87 | } 88 | 89 | if (RetVal == null) 90 | { 91 | SetValue(KeyName, SubKey, DefaultValue); 92 | RetVal = DefaultValue.ToString(); 93 | } 94 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", $"Retrieved {KeyName} = {RetVal}"); 95 | return (T)((object)RetVal); 96 | } 97 | 98 | if (typeof(T) == typeof(decimal)) 99 | { 100 | string registryValue; 101 | if (SubKey == "") 102 | { 103 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey is empty so getting value directly"); 104 | registryValue = (string)baseRegistryKey.GetValue(KeyName); 105 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + registryValue); 106 | } 107 | else 108 | { 109 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey has a value so using it..."); 110 | registryValue = (string)baseRegistryKey.CreateSubKey(SubKey).GetValue(KeyName); 111 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + registryValue); 112 | } 113 | 114 | if (registryValue == null) 115 | { 116 | SetValueInvariant(KeyName, SubKey, DefaultValue); 117 | decimal defaultValue = Convert.ToDecimal(DefaultValue); 118 | registryValue = defaultValue.ToString(CultureInfo.InvariantCulture); 119 | } 120 | 121 | decimal RetVal = Convert.ToDecimal(registryValue, CultureInfo.InvariantCulture); 122 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", $"Retrieved {KeyName} = {RetVal}"); 123 | return (T)((object)RetVal); 124 | } 125 | 126 | if (typeof(T) == typeof(DateTime)) 127 | { 128 | string registryValue; 129 | if (SubKey == "") 130 | { 131 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey is empty so getting value directly"); 132 | registryValue = (string)baseRegistryKey.GetValue(KeyName); 133 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + registryValue); 134 | } 135 | else 136 | { 137 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey has a value so using it..."); 138 | registryValue = (string)baseRegistryKey.CreateSubKey(SubKey).GetValue(KeyName); 139 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + registryValue); 140 | } 141 | 142 | if (registryValue == null) 143 | { 144 | SetValueInvariant(KeyName, SubKey, DefaultValue); 145 | return DefaultValue; 146 | } 147 | 148 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue DateTime", $"String value prior to Convert: {registryValue}"); 149 | 150 | if (DateTime.TryParse(registryValue, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out DateTime RetVal)) 151 | { 152 | // The string parsed OK so return the parsed value; 153 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue DateTime", $"Retrieved {KeyName} = {RetVal}"); 154 | return (T)((object)RetVal); 155 | } 156 | else // If the string fails to parse, overwrite with the default value and return this 157 | { 158 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue DateTime", $"Failed to parse registry value, persisting and returning the default value: {DefaultValue}"); 159 | SetValueInvariant(KeyName, SubKey, DefaultValue); 160 | return DefaultValue; 161 | } 162 | } 163 | 164 | if ((typeof(T) == typeof(Int32)) | (typeof(T) == typeof(int))) 165 | { 166 | string registryValue; 167 | if (SubKey == "") 168 | { 169 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey is empty so getting value directly"); 170 | registryValue = (string)baseRegistryKey.GetValue(KeyName); 171 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + registryValue); 172 | } 173 | else 174 | { 175 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "SubKey has a value so using it..."); 176 | registryValue = (string)baseRegistryKey.CreateSubKey(SubKey).GetValue(KeyName); 177 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", "Value retrieved OK: " + registryValue); 178 | } 179 | 180 | if (registryValue == null) 181 | { 182 | SetValueInvariant(KeyName, SubKey, DefaultValue); 183 | int defaultValue = Convert.ToInt32(DefaultValue); 184 | registryValue = defaultValue.ToString(CultureInfo.InvariantCulture); 185 | } 186 | 187 | int RetVal = Convert.ToInt32(registryValue, CultureInfo.InvariantCulture); 188 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "GetValue", $"Retrieved {KeyName} = {RetVal}"); 189 | return (T)((object)RetVal); 190 | } 191 | 192 | throw new DriverException("GetValue: Unknown type: " + typeof(T).Name); 193 | } 194 | 195 | public void SetValue(string KeyName, string SubKey, T Value) 196 | { 197 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "SetValue", $"Setting {typeof(T).Name} value '{KeyName}' in subkey '{SubKey}' to: '{Value}'"); 198 | 199 | if (SubKey == "") baseRegistryKey.SetValue(KeyName, Value.ToString()); 200 | else baseRegistryKey.CreateSubKey(SubKey).SetValue(KeyName, Value.ToString()); 201 | } 202 | 203 | public void SetValueInvariant(string KeyName, string SubKey, T Value) 204 | { 205 | if (LOG_CONFIGURATION_CALLS) ServerForm.LogMessage(0, 0, 0, "SetValue DateTime", $"Setting {typeof(T).Name} value '{KeyName}' in subkey '{SubKey}' to: '{Value}'"); 206 | 207 | if ((typeof(T) == typeof(Int32)) | (typeof(T) == typeof(int))) 208 | { 209 | int intValue = Convert.ToInt32(Value); 210 | if (SubKey == "") baseRegistryKey.SetValue(KeyName, intValue.ToString(CultureInfo.InvariantCulture)); 211 | else baseRegistryKey.CreateSubKey(SubKey).SetValue(KeyName, intValue.ToString(CultureInfo.InvariantCulture)); 212 | return; 213 | } 214 | 215 | if (typeof(T) == typeof(bool)) 216 | { 217 | bool boolValue = Convert.ToBoolean(Value); 218 | if (SubKey == "") baseRegistryKey.SetValue(KeyName, boolValue.ToString(CultureInfo.InvariantCulture)); 219 | else baseRegistryKey.CreateSubKey(SubKey).SetValue(KeyName, boolValue.ToString(CultureInfo.InvariantCulture)); 220 | return; 221 | } 222 | 223 | if (typeof(T) == typeof(decimal)) 224 | { 225 | decimal decimalValue = Convert.ToDecimal(Value); 226 | if (SubKey == "") baseRegistryKey.SetValue(KeyName, decimalValue.ToString(CultureInfo.InvariantCulture)); 227 | else baseRegistryKey.CreateSubKey(SubKey).SetValue(KeyName, decimalValue.ToString(CultureInfo.InvariantCulture)); 228 | return; 229 | } 230 | 231 | if (typeof(T) == typeof(DateTime)) 232 | { 233 | DateTime dateTimeValue = Convert.ToDateTime(Value); 234 | if (SubKey == "") baseRegistryKey.SetValue(KeyName, dateTimeValue.ToString("HH:mm:ss", CultureInfo.InvariantCulture)); 235 | else baseRegistryKey.CreateSubKey(SubKey).SetValue(KeyName, dateTimeValue.ToString("HH:mm:ss", CultureInfo.InvariantCulture)); 236 | return; 237 | } 238 | 239 | throw new DriverException("SetValueInvariant: Unknown type: " + typeof(T).Name); 240 | } 241 | 242 | #region IDisposable Support 243 | private bool disposedValue = false; // To detect redundant calls 244 | 245 | protected virtual void Dispose(bool disposing) 246 | { 247 | if (!disposedValue) 248 | { 249 | if (disposing) 250 | { 251 | baseRegistryKey?.Dispose(); 252 | hiveKey?.Dispose(); 253 | } 254 | 255 | disposedValue = true; 256 | } 257 | } 258 | 259 | // This code added to correctly implement the disposable pattern. 260 | public void Dispose() 261 | { 262 | // Do not change this code. Put clean up code in Dispose(bool disposing) above. 263 | Dispose(true); 264 | } 265 | #endregion 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /Remote Server/ConfigurationManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Text.Json.Serialization; 5 | using System.Text.Json; 6 | 7 | namespace ASCOM.Remote 8 | { 9 | [DefaultMember("Settings")] 10 | internal class ConfigurationManager : IDisposable 11 | { 12 | private const string FOLDER_NAME = "ASCOM Remote"; // Folder name underneath the local application data folder 13 | private const string SETTINGS_FILENAME = "remote.settings"; // Settings file name 14 | 15 | private TraceLoggerPlus TL; 16 | private Settings settings; 17 | private bool disposedValue; 18 | readonly int settingsFileVersion; 19 | private readonly JsonDocument appSettingsDocument = null; 20 | private readonly JsonSerializerOptions deserialiseOptions; 21 | private readonly JsonSerializerOptions serialiseOptions; 22 | 23 | #region Initialiser and Dispose 24 | 25 | /// 26 | /// Create a Configuration management instance and load the current settings 27 | /// 28 | /// Data logger instance. 29 | public ConfigurationManager(TraceLoggerPlus logger) 30 | { 31 | TL = logger; 32 | 33 | try 34 | { 35 | // Set JSON serialisation options 36 | serialiseOptions = new() 37 | { 38 | WriteIndented = true // Write the file in indented form for easier reading 39 | }; 40 | serialiseOptions.Converters.Add(new JsonStringEnumConverter()); // For increased resilience, accept both string member names and integer member values as valid for enum elements. 41 | 42 | // Set JSON de-serialisation options 43 | deserialiseOptions = new() 44 | { 45 | PropertyNameCaseInsensitive = true // Ignore incorrect element name casing 46 | }; 47 | deserialiseOptions.Converters.Add(new JsonStringEnumConverter()); // For increased resilience, accept both string member names and integer member values as valid for enum elements. 48 | 49 | // Create a new settings file with default values in case the supplied file cannot be used 50 | settings = new(); 51 | 52 | // Get the full settings file name including path 53 | string folderName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), FOLDER_NAME); 54 | SettingsFileName = Path.Combine(folderName, SETTINGS_FILENAME); 55 | TL?.LogMessage("ConfigurationManager", $"Settings folder: {folderName}, Settings file: {SettingsFileName}"); 56 | 57 | // Load the values in the settings file if it exists 58 | if (File.Exists(SettingsFileName)) // Settings file exists 59 | { 60 | // Read the file contents into a string 61 | TL?.LogMessage("ConfigurationManager", "File exists, about to read it..."); 62 | string serialisedSettings = File.ReadAllText(SettingsFileName); 63 | TL?.LogMessage("ConfigurationManager", $"Serialised settings: \r\n{serialisedSettings}"); 64 | 65 | // Make a basic check to see if this file is a beta / pre-release version that doesn't have a version number. If so replace with a new version 66 | if (!serialisedSettings.Contains("\"SettingsCompatibilityVersion\":")) // No compatibility version found so assume that this is a corrupt settings file 67 | { 68 | // Persist the default settings values 69 | try 70 | { 71 | // Rename the current settings file to preserve it 72 | string badVersionSettingsFileName = $"{SettingsFileName}.bad"; 73 | File.Delete(badVersionSettingsFileName); 74 | File.Move(SettingsFileName, $"{badVersionSettingsFileName}"); 75 | 76 | // Persist the default settings values 77 | settings = new(); 78 | PersistSettings(settings); 79 | 80 | Status = $"A corrupt settings file was found.\r\n\r\nApplication settings have been reset to defaults and the original settings file has been renamed to {badVersionSettingsFileName}."; 81 | } 82 | catch (Exception ex2) 83 | { 84 | TL?.LogMessage("ConfigurationManager", $"A corrupt settings file found but an error occurred when saving new Remote Server settings: {ex2}"); 85 | Status = $"$\"A corrupt settings file was found but an error occurred when saving new Remote Server settings: {ex2.Message}."; 86 | } 87 | } 88 | else // File does have a compatibility version so read in the settings from the file 89 | { 90 | TL?.LogMessage("ConfigurationManager", $"Found compatibility version element..."); 91 | // Try to read in the settings version number from the settings file 92 | try 93 | { 94 | TL?.LogMessage("ConfigurationManager", $"About to parse settings string"); 95 | appSettingsDocument = JsonDocument.Parse(serialisedSettings, new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip }); 96 | TL?.LogMessage("ConfigurationManager", $"About to get settings version"); 97 | settingsFileVersion = appSettingsDocument.RootElement.GetProperty("SettingsCompatibilityVersion").GetInt32(); 98 | TL?.LogMessage("ConfigurationManager", $"Found settings version: {settingsFileVersion}"); 99 | 100 | // Handle different file versions 101 | switch (settingsFileVersion) 102 | { 103 | // File version 1 - first production release 104 | case 1: 105 | 106 | try 107 | { 108 | 109 | // De-serialise the settings string into a Settings object 110 | settings = JsonSerializer.Deserialize(serialisedSettings, deserialiseOptions); 111 | 112 | // Test whether the retrieved settings match the requirements of this version of the Remote Server 113 | if (settings.SettingsCompatibilityVersion == Settings.SETTINGS_COMPATIBILTY_VERSION) // Version numbers match so all is well 114 | { 115 | Status = $"Settings read OK."; 116 | } 117 | else // Version numbers don't match so reset to defaults 118 | { 119 | int originalSettingsCompatibilityVersion = 0; 120 | try 121 | { 122 | originalSettingsCompatibilityVersion = settings.SettingsCompatibilityVersion; 123 | 124 | // Rename the current settings file to preserve it 125 | string badVersionSettingsFileName = $"{SettingsFileName}.badversion"; 126 | File.Delete(badVersionSettingsFileName); 127 | File.Move(SettingsFileName, $"{badVersionSettingsFileName}"); 128 | 129 | // Persist the default settings values 130 | settings = new(); 131 | PersistSettings(settings); 132 | 133 | Status = $"The current settings version: {originalSettingsCompatibilityVersion} does not match the required version: {Settings.SETTINGS_COMPATIBILTY_VERSION}. Application settings have been reset to default values and the original settings file renamed to {badVersionSettingsFileName}."; 134 | } 135 | catch (Exception ex2) 136 | { 137 | TL?.LogMessage("ConfigurationManager", $"Error persisting new Remote Server settings file: {ex2}"); 138 | Status = $"The current settings version:{originalSettingsCompatibilityVersion} does not match the required version: {Settings.SETTINGS_COMPATIBILTY_VERSION} but the new settings could not be saved: {ex2.Message}."; 139 | } 140 | } 141 | } 142 | catch (JsonException ex1) 143 | { 144 | // There was an exception when parsing the settings file so report it and set default values 145 | TL?.LogMessage("ConfigurationManager", $"Error de-serialising Remote Server settings file: {ex1}"); 146 | Status = $"There was an error de-serialising the settings file and application default settings are in effect.\r\n\r\nPlease correct the error in the file or use the \"Reset to Defaults\" button on the Settings page to save new values.\r\n\r\nJSON parser error message:\r\n{ex1.Message}"; 147 | } 148 | catch (Exception ex1) 149 | { 150 | TL?.LogMessage("ConfigurationManager", ex1.ToString()); 151 | Status = $"Exception reading the settings file, default values are in effect."; 152 | } 153 | break; 154 | 155 | // Handle unknown settings version numbers 156 | default: 157 | 158 | // Persist default settings values because the file version is unknown and the file may be corrupt 159 | try 160 | { 161 | // Rename the current settings file to preserve it 162 | string badVersionSettingsFileName = $"{SettingsFileName}.unknownversion"; 163 | File.Delete(badVersionSettingsFileName); 164 | File.Move(SettingsFileName, $"{badVersionSettingsFileName}"); 165 | 166 | // Persist the default settings values 167 | settings = new(); 168 | PersistSettings(settings); 169 | 170 | Status = $"An unsupported settings version was found: {settingsFileVersion}. Settings have been reset to defaults and the original settings file has been renamed to {badVersionSettingsFileName}."; 171 | } 172 | catch (Exception ex2) 173 | { 174 | TL?.LogMessage("ConfigurationManager", $"An unsupported settings version was found: {settingsFileVersion} but an error occurred when saving new Remote Server settings: {ex2}"); 175 | Status = $"$\"An unsupported settings version was found: {settingsFileVersion} but an error occurred when saving new Remote Server settings: {ex2.Message}."; 176 | } 177 | break; 178 | } 179 | } 180 | catch (JsonException ex) 181 | { 182 | // There was an exception when parsing the settings file so report it and use default values 183 | TL?.LogMessage("ConfigurationManager", $"Error getting settings file version from settings file: {ex}"); 184 | Status = $"An error occurred when reading the settings file version and application default settings are in effect.\r\n\r\nPlease correct the error in the file or use the \"Reset to Defaults\" button on the Settings page to create a new settings file.\r\n\r\nJSON parser error message:\r\n{ex.Message}"; 185 | } 186 | catch (Exception ex) 187 | { 188 | TL?.LogMessage("ConfigurationManager", $"Exception parsing the settings file: {ex}"); 189 | Status = $"Exception parsing the settings file: {ex.Message}"; 190 | } 191 | finally 192 | { 193 | appSettingsDocument?.Dispose(); 194 | } 195 | } 196 | } 197 | else // Settings file does not exist 198 | { 199 | TL.LogMessage("ConfigurationManager", $"Configuration file does not exist, initialising new file: {SettingsFileName}"); 200 | PersistSettings(settings); 201 | Status = $"First time use - configuration set to default values."; 202 | } 203 | } 204 | catch (Exception ex) 205 | { 206 | TL?.LogMessage("ConfigurationManager", ex.ToString()); 207 | Status = $"Unexpected exception reading the settings file, default values are in use."; 208 | } 209 | } 210 | 211 | protected virtual void Dispose(bool disposing) 212 | { 213 | if (!disposedValue) 214 | { 215 | if (disposing) 216 | { 217 | TL = null; 218 | settings = null; 219 | } 220 | 221 | disposedValue = true; 222 | } 223 | } 224 | 225 | public void Dispose() 226 | { 227 | // Do not change this code. Put clean-up code in 'Dispose(bool disposing)' method 228 | Dispose(disposing: true); 229 | GC.SuppressFinalize(this); 230 | } 231 | 232 | #endregion 233 | 234 | #region Public methods 235 | 236 | public void Reset() 237 | { 238 | try 239 | { 240 | settings = new(); 241 | PersistSettings(settings); 242 | Status = $"Settings reset at {DateTime.Now:HH:mm:ss}."; 243 | } 244 | catch (Exception ex) 245 | { 246 | TL?.LogMessage("Reset", $"Exception during Reset: {ex}"); 247 | throw; 248 | } 249 | } 250 | 251 | /// 252 | /// Persist current settings 253 | /// 254 | public void Save() 255 | { 256 | TL?.LogMessage("Save", "Saving settings to settings file"); 257 | PersistSettings(settings); 258 | Status = $"Settings saved at {DateTime.Now:HH:mm:ss}."; 259 | } 260 | 261 | public Settings Settings 262 | { 263 | get { return settings; } 264 | } 265 | 266 | public string SettingsFileName { get; private set; } 267 | 268 | /// 269 | /// Text message describing any issues found when validating the settings 270 | /// 271 | public string Status { get; private set; } 272 | 273 | #endregion 274 | 275 | #region Support code 276 | 277 | private void PersistSettings(Settings settingsToPersist) 278 | { 279 | try 280 | { 281 | ArgumentNullException.ThrowIfNull(settingsToPersist); 282 | 283 | // Set the version number of this settings file 284 | settingsToPersist.SettingsCompatibilityVersion = Settings.SETTINGS_COMPATIBILTY_VERSION; 285 | 286 | TL?.LogMessage("PersistSettings", $"Settings file: {SettingsFileName}"); 287 | 288 | string serialisedSettings = JsonSerializer.Serialize(settingsToPersist, serialiseOptions); 289 | 290 | Directory.CreateDirectory(Path.GetDirectoryName(SettingsFileName)); 291 | File.WriteAllText(SettingsFileName, serialisedSettings); 292 | } 293 | catch (Exception ex) 294 | { 295 | TL.LogMessage("PersistSettings", ex.ToString()); 296 | } 297 | } 298 | 299 | #endregion 300 | 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /Remote Server/ConfiguredDevice.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | /// 4 | /// Class to hold COM activation and access information for a served device 5 | /// 6 | public class ConfiguredDevice(string deviceType, string progID, string description, int deviceNumber, bool allowConnectedSetFalse, bool allowConnectedSetTrue, bool allowConcurrentAccess, string uniqueId) 7 | { 8 | public string DeviceType { get; set; } = deviceType; 9 | public string ProgID { get; set; } = progID; 10 | public string Description { get; set; } = description; 11 | public int DeviceNumber { get; set; } = deviceNumber; 12 | public bool AllowConnectedSetFalse { get; set; } = allowConnectedSetFalse; 13 | public bool AllowConnectedSetTrue { get; set; } = allowConnectedSetTrue; 14 | public bool AllowConcurrentAccess { get; set; } = allowConcurrentAccess; 15 | public string UniqueID { get; set; } = uniqueId; 16 | 17 | /// 18 | /// Return a unique key for this device based on its device type and device number 19 | /// 20 | public string DeviceKey 21 | { 22 | get 23 | { 24 | return $"{DeviceType.ToLowerInvariant()}/{DeviceNumber}"; // Create the device key 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Remote Server/DriverHostForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | partial class DriverHostForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.label1 = new System.Windows.Forms.Label(); 32 | this.SuspendLayout(); 33 | // 34 | // label1 35 | // 36 | this.label1.AutoSize = true; 37 | this.label1.Location = new System.Drawing.Point(12, 9); 38 | this.label1.Name = "label1"; 39 | this.label1.Size = new System.Drawing.Size(165, 13); 40 | this.label1.TabIndex = 1; 41 | this.label1.Text = "ASCOM Remote Server Driver host"; 42 | // 43 | // DriverHostForm 44 | // 45 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 46 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 47 | this.ClientSize = new System.Drawing.Size(193, 31); 48 | this.ControlBox = false; 49 | this.Controls.Add(this.label1); 50 | this.MaximizeBox = false; 51 | this.MinimizeBox = false; 52 | this.Name = "DriverHostForm"; 53 | this.ShowInTaskbar = false; 54 | this.Text = "Driver Host Form"; 55 | this.WindowState = System.Windows.Forms.FormWindowState.Minimized; 56 | this.ResumeLayout(false); 57 | this.PerformLayout(); 58 | 59 | } 60 | 61 | #endregion 62 | 63 | private System.Windows.Forms.Label label1; 64 | } 65 | } -------------------------------------------------------------------------------- /Remote Server/DriverHostForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | using System.Threading; 5 | 6 | namespace ASCOM.Remote 7 | { 8 | /// 9 | /// Form class to host a single driver and provide the required message loop to make sure all works properly 10 | /// The driver is created in the form loaded event and signalled by adding an entry in ActiveObjects in the form load event handler. 11 | /// 12 | public partial class DriverHostForm : Form 13 | { 14 | string deviceKey; 15 | readonly ServerForm restServer; 16 | readonly KeyValuePair configuredDevice; 17 | 18 | /// 19 | /// Main constructor for the form. Save state variables to use when creating, using and destroying the driver 20 | /// 21 | /// TraceLogger object 22 | /// ConfiguredDevice object with details of the driver to be created 23 | /// Handle to the main server form 24 | public DriverHostForm(KeyValuePair device, ServerForm server) 25 | { 26 | InitializeComponent(); 27 | configuredDevice = device; 28 | restServer = server; 29 | 30 | this.FormClosed += DriverHostForm_FormClosed; 31 | this.Load += DriverHostForm_Load; 32 | ServerForm.LogMessage(0, 0, 0, "DriverHostForm", "Form has been instantiated on thread: " + Environment.CurrentManagedThreadId); 33 | } 34 | 35 | /// 36 | /// Form load event - Create the driver 37 | /// 38 | /// 39 | /// 40 | private void DriverHostForm_Load(object sender, EventArgs e) 41 | { 42 | deviceKey = $"{configuredDevice.Value.DeviceType.ToLowerInvariant()}/{configuredDevice.Value.DeviceNumber}"; 43 | 44 | ServerForm.LogMessage(0, 0, 0, "DriverHostForm", $"Creating driver {deviceKey} ({configuredDevice.Key}) on thread {Environment.CurrentManagedThreadId} with apartment state {Thread.CurrentThread.GetApartmentState()}"); 45 | restServer.CreateInstance(configuredDevice); // Create the driver on this thread 46 | ServerForm.LogMessage(0, 0, 0, "DriverHostForm", $"Created driver {deviceKey} ({configuredDevice.Key}) on thread {Environment.CurrentManagedThreadId}"); 47 | 48 | ServerForm.ActiveObjects[deviceKey].DriverHostForm = this; // Save the driver host form reference so that calls can be made to the driver 49 | } 50 | 51 | /// 52 | /// When the form is closing stop the windows message loop on this thread so that the thread will end 53 | /// 54 | /// 55 | /// 56 | private void DriverHostForm_FormClosed(object sender, FormClosedEventArgs e) 57 | { 58 | Application.ExitThread(); 59 | } 60 | 61 | /// 62 | /// Send a command to the driver 63 | /// 64 | /// Details of the command to send 65 | /// Background thread on which the command is executing 66 | public Thread DriverCommand(RequestData requestData) 67 | { 68 | try // Process the command 69 | { 70 | Application.DoEvents(); 71 | 72 | // Process the command on a separate thread allowing other requests to be handled concurrently through this thread, which is running the Windows message loop 73 | Thread driverThread = new(new ParameterizedThreadStart(ProcessCommand)); // Create a new thread on which to make the call to the COM driver 74 | if (ServerForm.DebugTraceState) ServerForm.LogMessage1(requestData, requestData.Elements[SharedConstants.URL_ELEMENT_METHOD], $"DriverCommand has received a command for {deviceKey} on FORM thread {Environment.CurrentManagedThreadId} Apartment state: {Thread.CurrentThread.GetApartmentState()} Is background: {Thread.CurrentThread.IsBackground} Is thread pool thread: {Thread.CurrentThread.IsThreadPoolThread}"); 75 | 76 | driverThread.Start(requestData); // Start the thread supplying the request data to the method 77 | 78 | if (ServerForm.DebugTraceState) ServerForm.LogMessage1(requestData, requestData.Elements[SharedConstants.URL_ELEMENT_METHOD], $"DriverCommand has started the command for {deviceKey} on FORM thread {Environment.CurrentManagedThreadId}"); 79 | return driverThread; // Return the thread so that the calling method can wait for it to complete and so that this thread can start waiting for the next command 80 | } 81 | catch (Exception ex) // Something serious has gone wrong with the ASCOM Remote server itself so report this to the user 82 | { 83 | ServerForm.LogException1(requestData, "DriverCommand", ex.ToString()); 84 | ServerForm.Return500Error(requestData, $"Internal server error (DriverOnSeparateThread): {ex}"); 85 | } 86 | return null; 87 | } 88 | 89 | void ProcessCommand(object requestData) 90 | { 91 | restServer.ProcessDriverCommand((RequestData)requestData); 92 | } 93 | 94 | /// 95 | /// Destroy the driver 96 | /// 97 | public void DestroyDriver() 98 | { 99 | ServerForm.LogMessage(0, 0, 0, "DriverHostForm", "Destroy driver method has been called on thread: " + Environment.CurrentManagedThreadId); 100 | ServerForm.DestroyDriver(deviceKey); 101 | ServerForm.LogMessage(0, 0, 0, "DriverHostForm", "Destroy driver method completed on thread: " + Environment.CurrentManagedThreadId); 102 | Application.ExitThread(); // Close all forms on this thread, which will also terminate the thread itself 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Remote Server/DriverHostForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Remote Server/Entity Classes/ActiveObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace ASCOM.Remote 3 | { 4 | /// 5 | /// Class to hold information about an ASCOM Remote served device 6 | /// 7 | /// 8 | /// Parameterised initialiser for the main object properties that also initialises the lock object 9 | /// 10 | /// 11 | /// 12 | /// 13 | public class ActiveObject(bool AllowConnectedSetFalseParm, bool AllowSetConnectedTrueParm, bool AllowConcurrentAccessParm, string ConfiguredDeviceKeyParm) 14 | { 15 | private int? interfaceVersion; 16 | 17 | /// 18 | /// Key of the corresponding entry in ConfiguredDevices to allow tieback to the main configuration information 19 | /// 20 | public string ConfiguredDeviceKey { get; set; } = ConfiguredDeviceKeyParm; 21 | 22 | /// 23 | /// The device COM object 24 | /// 25 | public dynamic DeviceObject { get; set; } 26 | 27 | /// 28 | /// Flag indicating whether the user is allowed to set the device's Connected property to False 29 | /// 30 | public bool AllowConnectedSetFalse { get; set; } = AllowConnectedSetFalseParm; 31 | 32 | /// 33 | /// Flag indicating whether the user is allowed to set the device's Connected property to True 34 | /// 35 | public bool AllowConnectedSetTrue { get; set; } = AllowSetConnectedTrueParm; 36 | 37 | /// 38 | /// Flag indicating whether the driver can handle concurrent calls 39 | /// 40 | public bool AllowConcurrentAccess { get; set; } = AllowConcurrentAccessParm; 41 | 42 | /// 43 | /// Lock object to ensure that only one command at a time is sent to the device 44 | /// 45 | public object CommandLock { get; } = new object(); // Create a lock object 46 | 47 | /// 48 | /// Host form for the device when devices are set to run on independent threads 49 | /// 50 | public DriverHostForm DriverHostForm { get; set; } 51 | 52 | /// 53 | /// Flag indicating whether the device was created and the Connected property set True without error 54 | /// 55 | public bool InitialisedOk { get; set; } = false; // Initialise flag and error message 56 | 57 | /// 58 | /// Error message that will be returned if the user tries to call a method on a device that did not initialise correctly. 59 | /// 60 | public string InitialisationErrorMessage { get; set; } = "ASCOM Remote ActiveObject error message - something went wrong during device initialisation but the error message was not recorded for an unknown reason."; 61 | 62 | /// 63 | /// If the device is a Camera, points to the last image array value returned, otherwise null 64 | /// 65 | public object LastImageArray { get; set; } 66 | 67 | /// 68 | /// The device's interface version 69 | /// 70 | public int InterfaceVersion 71 | { 72 | get 73 | { 74 | // Check whether we have already cached this device's interface version 75 | if (interfaceVersion.HasValue) // Cached value available so return it. 76 | return interfaceVersion.Value; 77 | 78 | // No cached value so query it from the device 79 | try 80 | { 81 | interfaceVersion = DeviceObject.Interfaceversion; 82 | } 83 | catch // Something went wrong, so most likely this is an old simulator without an InterfceVersion property - Set the property to 1. 84 | { 85 | interfaceVersion = 1; 86 | } 87 | 88 | // Return the interface version. 89 | return interfaceVersion.Value; 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Remote Server/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ASCOM.Remote 5 | { 6 | public static class ExtensionMethods 7 | { 8 | /// 9 | /// Lookup dictionary to translate lower case method names to mixed case. 10 | /// The device type is included in order to disambiguate the same method name if used in different device types AND cased differently/> 11 | /// 12 | private static readonly Dictionary methodLookup = new() 13 | { 14 | { "connected","Connected" }, 15 | 16 | // Telescope 17 | { "tracking","Tracking" }, 18 | { "doesrefraction","DoesRefraction" }, 19 | { "slewsettletime","SlewSettleTime" }, 20 | { "declinationrate","DeclinationRate" }, 21 | { "rightascensionrate","RightAscensionRate" }, 22 | { "guideratedeclination","GuideRateDeclination" }, 23 | { "guideraterightascension","GuideRateRightAscension" }, 24 | { "sideofpier", "SideOfPier" }, 25 | { "siteelevation","SiteElevation" }, 26 | { "sitelatitude","SiteLatitude" }, 27 | { "sitelongitude","SiteLongitude" }, 28 | { "targetdeclination","TargetDeclination" }, 29 | { "targetrightascension","TargetRightAscension" }, 30 | { "utcdate","UTCDate" }, 31 | { "trackingrate","TrackingRate" }, 32 | { SharedConstants.AXIS_PARAMETER_NAME,SharedConstants.AXIS_PARAMETER_NAME }, 33 | { SharedConstants.RA_PARAMETER_NAME,SharedConstants.RA_PARAMETER_NAME }, 34 | { SharedConstants.DEC_PARAMETER_NAME,SharedConstants.DEC_PARAMETER_NAME }, 35 | { SharedConstants.RATE_PARAMETER_NAME,SharedConstants.RATE_PARAMETER_NAME }, 36 | { SharedConstants.DIRECTION_PARAMETER_NAME,SharedConstants.DIRECTION_PARAMETER_NAME }, 37 | { SharedConstants.DURATION_PARAMETER_NAME,SharedConstants.DURATION_PARAMETER_NAME }, 38 | { SharedConstants.ALT_PARAMETER_NAME,SharedConstants.ALT_PARAMETER_NAME }, 39 | { SharedConstants.AZ_PARAMETER_NAME,SharedConstants.AZ_PARAMETER_NAME }, 40 | 41 | // Rotator 42 | { SharedConstants.POSITION_PARAMETER_NAME,SharedConstants.POSITION_PARAMETER_NAME}, 43 | { "reverse","Reverse" }, 44 | 45 | // Camera 46 | { "binx","BinX" }, 47 | { "biny","BinY"}, 48 | { "cooleron","CoolerOn"}, 49 | { "fastreadout","FastReadout"}, 50 | { "gain","Gain"}, 51 | { "numx","NumX"}, 52 | { "numy","NumY"}, 53 | { "offset","Offset"}, 54 | { "readoutmode","ReadoutMode"}, 55 | { "setccdtemperature","SetCCDTemperature"}, 56 | { "startx","StartX"}, 57 | { "starty","StartY"}, 58 | { "subexposureduration","SubExposureDuration"}, 59 | { "slaved","Slaved" }, 60 | { "Brightness","Brightness" }, 61 | { "position","Position" }, 62 | { "tempcomp","TempComp" }, 63 | { SharedConstants.SENSORNAME_PARAMETER_NAME,SharedConstants.SENSORNAME_PARAMETER_NAME }, 64 | { "averageperiod","AveragePeriod" }, 65 | { "sensordescription","SensorDescription" }, 66 | { SharedConstants.ID_PARAMETER_NAME,SharedConstants.ID_PARAMETER_NAME }, 67 | { SharedConstants.NAME_PARAMETER_NAME,SharedConstants.NAME_PARAMETER_NAME }, 68 | { SharedConstants.VALUE_PARAMETER_NAME,SharedConstants.VALUE_PARAMETER_NAME }, 69 | { SharedConstants.STATE_PARAMETER_NAME,SharedConstants.STATE_PARAMETER_NAME }, 70 | { "id","Id" } 71 | }; 72 | 73 | /// 74 | /// Look up the cased version of a method name. 75 | /// 76 | /// The method name to look up 77 | /// Optional device type to disambiguate method names that are used in more than 1 device type AND cased differently 78 | /// When the required method name key has not yet been added to the lookup dictionary. 79 | /// The mixed case equivalent of the supplied method name 80 | public static string ToCorrectCase(this string methodName, string deviceType = null) 81 | { 82 | string lookupKey = "ValueNotSet"; 83 | // Look up the cased version of the method. If this fails a KeyNotFoiund exception will be thrown 84 | try 85 | { 86 | lookupKey = $"{methodName}{(deviceType is null ? "" : $".{deviceType}")}"; 87 | return methodLookup[lookupKey]; 88 | } 89 | catch (Exception ex) 90 | { 91 | return $"UnknownMethod:'{lookupKey}' - {ex}"; 92 | } 93 | } 94 | 95 | /// 96 | /// Append a byte array to an existing array 97 | /// 98 | /// First array 99 | /// Second array 100 | /// Concatenated array of byte 101 | public static byte[] Append(this byte[] first, byte[] second) 102 | { 103 | byte[] ret = new byte[first.Length + second.Length]; 104 | Buffer.BlockCopy(first, 0, ret, 0, first.Length); 105 | Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); 106 | return ret; 107 | } 108 | 109 | public static string ToConcatenatedString(this List list, string separator) 110 | { 111 | string concatenatedList = ""; 112 | foreach (string item in list) 113 | { 114 | concatenatedList += item + separator; 115 | } 116 | return concatenatedList.Trim(separator.ToCharArray()); 117 | } 118 | 119 | public static void FromConcatenatedString(this List list, string concatenatedString, string separator) 120 | { 121 | string[] items = concatenatedString.Split(separator.ToCharArray()); 122 | 123 | list.Clear(); 124 | 125 | foreach (string item in items) 126 | { 127 | list.Add(item); 128 | } 129 | } 130 | 131 | public static List ToListStringValue(this List fromList) 132 | { 133 | List toList = []; 134 | 135 | foreach (string item in fromList) 136 | { 137 | toList.Add(new StringValue(item)); 138 | } 139 | 140 | return toList; 141 | } 142 | 143 | public static List ToListString(this List fromList) 144 | { 145 | List toList = []; 146 | 147 | foreach (StringValue item in fromList) 148 | { 149 | toList.Add(item.Value); 150 | } 151 | 152 | return toList; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Remote Server/HideTabControlBorders.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Drawing.Drawing2D; 3 | using System.Windows.Forms; 4 | 5 | namespace ASCOM.Remote 6 | { 7 | /// 8 | /// Class that takes overrides normal tab control drawing in order to remove white boarders that are created by the default control 9 | /// 10 | /// This is taken from https://stackoverflow.com/questions/7768555/tabcontrol-and-borders-visual-glitch 11 | /// 12 | internal class HideTabControlBorders : NativeWindow 13 | { 14 | private const int WM_PAINT = 0xF; 15 | 16 | private readonly TabControl tabControl; 17 | 18 | public HideTabControlBorders(TabControl tc) 19 | { 20 | tabControl = tc; 21 | tabControl.Selected += new TabControlEventHandler(TabControl_Selected); 22 | AssignHandle(tc.Handle); 23 | } 24 | 25 | void TabControl_Selected(object sender, TabControlEventArgs e) 26 | { 27 | tabControl.Invalidate(); 28 | } 29 | 30 | protected override void WndProc(ref Message m) 31 | { 32 | base.WndProc(ref m); 33 | 34 | if (m.Msg == WM_PAINT) 35 | { 36 | using Graphics g = Graphics.FromHwnd(m.HWnd); 37 | 38 | //Replace the outside white borders: 39 | if (tabControl.Parent != null) 40 | { 41 | g.SetClip(new Rectangle(0, 0, tabControl.Width - 2, tabControl.Height - 1), CombineMode.Exclude); 42 | using SolidBrush sb = new(tabControl.Parent.BackColor); 43 | g.FillRectangle(sb, new Rectangle(0, 44 | tabControl.ItemSize.Height + 2, 45 | tabControl.Width, 46 | tabControl.Height - (tabControl.ItemSize.Height + 2))); 47 | } 48 | 49 | //Replace the inside white borders: 50 | if (tabControl.SelectedTab != null) 51 | { 52 | g.ResetClip(); 53 | Rectangle r = tabControl.SelectedTab.Bounds; 54 | g.SetClip(r, CombineMode.Exclude); 55 | using SolidBrush sb = new(tabControl.SelectedTab.BackColor); 56 | g.FillRectangle(sb, new Rectangle(r.Left - 3, 57 | r.Top - 1, 58 | r.Width + 4, 59 | r.Height + 3)); 60 | } 61 | } 62 | } 63 | 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Remote Server/HostPc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | using System.Net.NetworkInformation; 4 | using System.Net.Sockets; 5 | 6 | namespace ASCOM.Remote 7 | { 8 | /// 9 | /// Class that presents all usable IPv4 and IPv6 addresses on the host 10 | /// 11 | internal class HostPc 12 | { 13 | 14 | /// 15 | /// Class initialiser 16 | /// 17 | public HostPc() 18 | { 19 | } 20 | 21 | /// 22 | /// Returns the hosts IPv4 addresses 23 | /// 24 | /// 25 | public static List IpV4Addresses 26 | { 27 | get 28 | { 29 | return GetIpAddresses(AddressFamily.InterNetwork); 30 | } 31 | } 32 | /// 33 | /// Returns the hosts IPv6 addresses 34 | /// 35 | /// 36 | public static List IpV6Addresses 37 | { 38 | get 39 | { 40 | return GetIpAddresses(AddressFamily.InterNetworkV6); 41 | } 42 | } 43 | 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | private static List GetIpAddresses(AddressFamily addressFamily) 50 | { 51 | // Initialise the IPv4 and IPv6 address lists 52 | List ipAddresses = []; 53 | 54 | // Get an array of all network interfaces on this host 55 | NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); 56 | 57 | // Iterate over each network adapter looking for usable IP addresses 58 | foreach (NetworkInterface adapter in adapters) 59 | { 60 | // Only test operational adapters 61 | if (adapter.OperationalStatus == OperationalStatus.Up) 62 | { 63 | // Get the adapter's properties 64 | IPInterfaceProperties adapterProperties = adapter.GetIPProperties(); 65 | 66 | // If the adapter has properties get the collection of unicast addresses 67 | if (adapterProperties != null) 68 | { 69 | // Get the collection of unicast addresses 70 | UnicastIPAddressInformationCollection uniCast = adapterProperties.UnicastAddresses; 71 | 72 | // If there are some unicast IP addresses get these 73 | if (uniCast.Count > 0) 74 | { 75 | // Iterate over the unicast addresses 76 | foreach (UnicastIPAddressInformation uni in uniCast) 77 | { 78 | // Save IPv4 addresses to the IPv4 list 79 | if (uni.Address.AddressFamily == addressFamily) 80 | { 81 | ipAddresses.Add(uni.Address); 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | // Sort the addresses into ascending text order 90 | ipAddresses.Sort(CompareIPaddresses); 91 | 92 | return ipAddresses; 93 | } 94 | 95 | /// 96 | /// COmpare two IPAdress values based on their text representations 97 | /// 98 | /// 99 | /// 100 | /// 101 | private static int CompareIPaddresses(IPAddress x, IPAddress y) 102 | { 103 | if (x == null) 104 | { 105 | if (y == null) // If x is null and y is null, they're equal. 106 | { 107 | return 0; 108 | } 109 | else // If x is null and y is not null, y is greater. 110 | { 111 | return -1; 112 | } 113 | } 114 | else // If x is not null... 115 | { 116 | if (y == null) // ...and y is null, x is greater. 117 | { 118 | return 1; 119 | } 120 | else // ...and y is not null, compare the lengths of the two strings. 121 | { 122 | return x.ToString().CompareTo(y.ToString()); 123 | } 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Remote Server/InvalidParameterException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace ASCOM.Remote 5 | { 6 | /// 7 | /// This exception should be raised by the driver to reject a parameter from the client. 8 | /// 9 | [Serializable] 10 | public class InvalidParameterException : DriverException 11 | { 12 | const string csDefaultMessage = "The requested operation is not permitted at this time"; 13 | public static readonly int InvalidParameterErrorNumber = unchecked((int)0x80040FFE); 14 | 15 | /// 16 | /// Default public constructor for NotConnectedException takes no parameters. 17 | /// 18 | public InvalidParameterException() : base(csDefaultMessage, InvalidParameterErrorNumber) 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class 24 | /// from another exception. 25 | /// 26 | /// The inner exception. 27 | public InvalidParameterException(Exception innerException) : base(csDefaultMessage, InvalidParameterErrorNumber, innerException) 28 | { 29 | } 30 | 31 | /// 32 | /// Initializes a new instance of the class 33 | /// with a non-default error message. 34 | /// 35 | /// A descriptive human-readable message. 36 | public InvalidParameterException(string message) : base(message, InvalidParameterErrorNumber) 37 | { 38 | } 39 | 40 | /// 41 | /// Initializes a new instance of the class 42 | /// based on another exception. 43 | /// 44 | /// Descriptive text documenting the cause or source of the error. 45 | /// The inner exception the led to the throwing of this exception. 46 | public InvalidParameterException(string message, Exception innerException) : base(message, InvalidParameterErrorNumber, innerException) 47 | { 48 | } 49 | 50 | /// 51 | /// Added to keep Code Analysis happy 52 | /// 53 | /// 54 | /// 55 | protected InvalidParameterException(SerializationInfo info, StreamingContext context) : base(info, context) 56 | { 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Remote Server/ProfileDevice.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class ProfileDevice(string deviceType, string progID, string description) 4 | { 5 | public string DeviceType { get; set; } = deviceType; 6 | public string ProgID { get; set; } = progID; 7 | public string Description { get; set; } = description; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Remote Server/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Windows.Forms; 6 | using ASCOM.Tools; 7 | 8 | namespace ASCOM.Remote 9 | { 10 | static class Program 11 | { 12 | /// 13 | /// The main entry point for the application. 14 | /// 15 | [STAThread] 16 | static void Main(string[] arguments) 17 | { 18 | // Check whether any command line arguments were supplied 19 | if (arguments.Length > 0) // One or more arguments were supplied 20 | { 21 | switch (arguments[0].ToLowerInvariant()) 22 | { 23 | case "-reset": 24 | case "--reset": 25 | case "/reset": 26 | // Reset the configuration 27 | TraceLoggerPlus TLReset = new("RemoteReset", true); 28 | using (ConfigurationManager configurationManager = new(TLReset)) 29 | { 30 | configurationManager.Reset(); 31 | configurationManager.Save(); 32 | } 33 | 34 | Configuration.Reset(); 35 | 36 | MessageBox.Show($"Configuration reset to default values.", "Reset Configuration", MessageBoxButtons.OK); 37 | break; 38 | 39 | default: 40 | MessageBox.Show($"The parameter: {arguments[0]} is not valid.", "Unrecognised command line parameter", MessageBoxButtons.OK, MessageBoxIcon.Warning); 41 | return; 42 | } 43 | } 44 | 45 | TraceLoggerPlus TLInit = new("RemoteInit", true); 46 | using (ConfigurationManager configurationManager = new(TLInit)) 47 | { 48 | // Restart as the other bitness version if required 49 | 50 | // Check whether we are supposed to be running as a 32bit or a 64bit application 51 | if (configurationManager.Settings.RunAs64Bit) // We are supposed to be running as a 64bit application 52 | { 53 | // Check whether the OS is 32bit or 64bit 54 | if (Environment.Is64BitOperatingSystem) // OS is 64bit 55 | { 56 | // Check whether the application is running as 32bit or 64bit 57 | if (Environment.Is64BitProcess) // Application is 64bit 58 | { 59 | // No action required because we are already running as 64bit 60 | Debug.WriteLine($"Already running as 64bit"); 61 | } 62 | else // Application is 32bit 63 | { 64 | // Start the 64bit version of the application 65 | string folderName32 = Path.GetDirectoryName(Application.ExecutablePath); // Folder name of the 32bit version 66 | Debug.WriteLine($"Folder name = {folderName32}"); 67 | string folderName64 = Directory.GetParent(folderName32).FullName; 68 | 69 | Debug.WriteLine($"New folder name = {folderName64}"); 70 | Process.Start($"{folderName64}\\remoteserver.exe"); 71 | 72 | // End this program 73 | return; 74 | } 75 | } 76 | else // OS is 32bit 77 | { 78 | // Can't run as a 64bit application on a 32bit OS so no action because this must already be the 32bit version of the application 79 | } 80 | } 81 | else // We are supposed to be running as a 32bit application 82 | { 83 | // Check whether the OS is 32bit or 64bit 84 | if (Environment.Is64BitOperatingSystem) // OS is 64bit 85 | { 86 | // Check whether the application is running as 32bit or 64bit 87 | if (Environment.Is64BitProcess) // Application is 64bit 88 | { 89 | // Start the 32bit version of the application 90 | Debug.WriteLine($"Starting the 32bit version"); 91 | 92 | string folderName = Path.GetDirectoryName(Application.ExecutablePath); 93 | Debug.WriteLine($"Folder name = {folderName}"); 94 | folderName += @"\32bit"; 95 | Debug.WriteLine($"New folder name = {folderName}"); 96 | Process.Start($"{folderName}\\remoteserver.exe"); 97 | 98 | // End this program 99 | return; 100 | } 101 | else // Application is 32bit 102 | { 103 | // No action required because we are already running as 32bit on the 64bit OS 104 | Debug.WriteLine($"Already running as 32bit"); 105 | } 106 | } 107 | else // OS is 32bit 108 | { 109 | // No action required because the OS is 32bit so this application must also be 32bit. 110 | } 111 | } 112 | 113 | Debug.WriteLine($"Starting application"); 114 | } 115 | 116 | #if !DEBUG // Exclude the un-handled exception handlers from the Debug version so that the application can be debugged in Visual Studio 117 | // Add the event handler for handling UI thread exceptions to the event. 118 | Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException); 119 | 120 | // Set the un-handled exception mode to force all Windows Forms errors to go through our handler. 121 | Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 122 | 123 | // Add the event handler for handling non-UI thread exceptions to the event. 124 | AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); 125 | #endif 126 | Application.EnableVisualStyles(); 127 | Application.SetCompatibleTextRenderingDefault(false); 128 | ServerForm serverForm = new(); 129 | Application.Run(serverForm); 130 | 131 | if (serverForm.RestartApplication) Application.Restart(); // Restart the application if the network permissions have been changed 132 | 133 | #if DEBUG // When debugging, include a log to show the time of close down 134 | TraceLogger TL = new("ASCOMRemoteEnded", true) 135 | { 136 | Enabled = true 137 | }; 138 | TL.LogMessage("ASCOMRemoteEnded", "Application has exited"); 139 | TL.Enabled = false; 140 | TL.Dispose(); 141 | TL = null; 142 | #endif 143 | } 144 | 145 | static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) 146 | { 147 | TraceLogger TL = new("RemoteAccessServerException", true) 148 | { 149 | Enabled = true 150 | }; 151 | TL.LogMessage("Main", $"Thread exception: {e.Exception}"); 152 | Process.Start(TL.LogFileName); 153 | 154 | TL.Enabled = false; 155 | TL.Dispose(); 156 | 157 | //MessageBox.Show(e.Exception.Message, "Un-handled Thread Exception, see RemoteAccessServerException log for details."); 158 | Environment.Exit(0); 159 | } 160 | 161 | static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 162 | { 163 | Exception exception = (Exception)e.ExceptionObject; 164 | TraceLogger TL = new("RemoteAccessServerException", true) 165 | { 166 | Enabled = true 167 | }; 168 | TL.LogMessage("Main", $"Un-handled exception: {exception}"); 169 | Process.Start(TL.LogFileName); 170 | TL.Enabled = false; 171 | TL.Dispose(); 172 | Environment.Exit(0); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Remote Server/Remote Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | ASCOM.Remote 5 | RemoteServer 6 | net8.0-windows 7 | true 8 | true 9 | ASCOM Remote Server 10 | ASCOM Remote Server 11 | Copyright © 2024 Peter Simpson 12 | bin\$(Configuration)\ 13 | AnyCPU;x86;x64 14 | win-x86;win-x64 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | $(ProductMajor).$(ProductMinor).$(ProductPatch)$(ProductPreReleaseSeparator)$(ProductPreRelease)+$(BuildNumber).$(Head) 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | <_Parameter1>ShortGitId 41 | <_Parameter2>$(BuildNumber).$(Head) 42 | 43 | 44 | 45 | 46 | 47 | 48 | 7 49 | 0 50 | 1 51 | 52 | 53 | 54 | 55 | 60 | 61 | $([MSBuild]::Add($([MSBuild]::Multiply($([MSBuild]::Modulo($([MSBuild]::Add($([MSBuild]::Multiply($([MSBuild]::Subtract($([System.DateTime]::UtcNow.Year),2023)),366)),$([System.DateTime]::UtcNow.DayOfYear))),2048)),32)),$([System.DateTime]::UtcNow.TimeOfDay.Hours))) 62 | 63 | 64 | 65 | - 66 | 67 | 68 | 69 | $(ProductMajor).$(ProductMinor).$(ProductPatch).$(BuildNumber) 70 | $(ProductMajor).$(ProductMinor).$(ProductPatch).$(BuildNumber) 71 | 72 | 73 | $(ProductMajor).$(ProductMinor).$(ProductPatch)$(ProductPreReleaseSeparator)$(ProductPreRelease)+$(BuildNumber) 74 | 75 | 76 | embedded 77 | AnyCPU 78 | 79 | 80 | embedded 81 | AnyCPU 82 | 83 | 84 | x86 85 | embedded 86 | 87 | 88 | embedded 89 | AnyCPU 90 | true 91 | 92 | 93 | embedded 94 | AnyCPU 95 | true 96 | 97 | 98 | embedded 99 | x86 100 | true 101 | 102 | 103 | ASCOM.Remote.Program 104 | 105 | 106 | ..\Remote Server Key.snk 107 | 108 | 109 | ASCOM.ico 110 | 111 | 112 | True 113 | 114 | 115 | 116 | 117 | DriverHostForm.cs 118 | 119 | 120 | 121 | ServedDevice.cs 122 | 123 | 124 | 125 | SetupForm.cs 126 | 127 | 128 | 129 | ServerForm.cs 130 | 131 | 132 | DriverHostForm.cs 133 | 134 | 135 | ServedDevice.cs 136 | 137 | 138 | SetupForm.cs 139 | 140 | 141 | ServerForm.cs 142 | Designer 143 | 144 | 145 | 146 | 147 | Always 148 | 149 | 150 | Always 151 | 152 | 153 | Never 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /Remote Server/RequestData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | using System.Net; 3 | 4 | namespace ASCOM.Remote 5 | { 6 | public class RequestData 7 | { 8 | public RequestData() 9 | { 10 | } 11 | 12 | public RequestData(string clientIpAddress, uint clientID, uint clientTransactionID, uint serverTransactionID, NameValueCollection suppliedParameters, HttpListenerRequest request, HttpListenerResponse response, string[] elements, string deviceKey) 13 | { 14 | ClientIpAddress = clientIpAddress; 15 | ClientID = clientID; 16 | ClientTransactionID = clientTransactionID; 17 | ServerTransactionID = serverTransactionID; 18 | QueryParameters = suppliedParameters; 19 | Request = request; 20 | Response = response; 21 | Elements = elements; 22 | DeviceKey = deviceKey; 23 | } 24 | 25 | public uint ClientID { get; set; } 26 | public uint ClientTransactionID { get; set; } 27 | public uint ServerTransactionID { get; set; } 28 | public NameValueCollection FormParameters { get; set; } 29 | public NameValueCollection QueryParameters { get; set; } 30 | public HttpListenerRequest Request { get; set; } 31 | public HttpListenerResponse Response { get; set; } 32 | public string[] Elements { get; set; } 33 | public string DeviceKey { get; set; } 34 | public string ClientIpAddress { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/AlpacaConfiguredDevicesResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public class AlpacaConfiguredDevicesResponse : RestResponseBase 6 | { 7 | public AlpacaConfiguredDevicesResponse() 8 | { 9 | Value = []; 10 | } 11 | 12 | public AlpacaConfiguredDevicesResponse(uint clientTransactionID, uint transactionID, List value) 13 | { 14 | base.ServerTransactionID = transactionID; 15 | base.ClientTransactionID = clientTransactionID; 16 | Value = value; 17 | } 18 | 19 | public List Value { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/AlpacaDescriptionResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class AlpacaDescriptionResponse : RestResponseBase 4 | { 5 | public AlpacaDescriptionResponse() 6 | { 7 | Value = new AlpacaDeviceDescription(); 8 | } 9 | 10 | public AlpacaDescriptionResponse(uint clientTransactionID, uint transactionID, AlpacaDeviceDescription value) 11 | { 12 | base.ServerTransactionID = transactionID; 13 | base.ClientTransactionID = clientTransactionID; 14 | Value = value; 15 | } 16 | 17 | public AlpacaDeviceDescription Value { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/AlpacaDiscoveryResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class AlpacaDiscoveryResponse 4 | { 5 | public AlpacaDiscoveryResponse() { } 6 | 7 | public AlpacaDiscoveryResponse(int alpacaPort) 8 | { 9 | AlpacaPort = alpacaPort; 10 | } 11 | 12 | public int AlpacaPort { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/AxisRatesResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public class AxisRatesResponse : RestResponseBase 6 | { 7 | public AxisRatesResponse() { } 8 | 9 | public AxisRatesResponse(uint clientTransactionID, uint transactionID, List value) 10 | { 11 | base.ServerTransactionID = transactionID; 12 | base.ClientTransactionID = clientTransactionID; 13 | Value = value; 14 | } 15 | 16 | public List Value { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/Base64ArrayHandOffResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class Base64ArrayHandOffResponse : ImageArrayResponseBase 4 | { 5 | public int Dimension0Length { get; set; } = 0; 6 | 7 | public int Dimension1Length { get; set; } = 0; 8 | 9 | public int Dimension2Length { get; set; } = 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/Base64ArrayJsonResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class Base64ArrayJsonResponse : Base64ArrayHandOffResponse 4 | { 5 | public string Value { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/BoolResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class BoolResponse : RestResponseBase 4 | { 5 | public BoolResponse() { } 6 | public BoolResponse(uint clientTransactionID, uint transactionID, bool value) 7 | { 8 | base.ServerTransactionID = transactionID; 9 | base.ClientTransactionID = clientTransactionID; 10 | Value = value; 11 | } 12 | 13 | public bool Value { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/ConfigurationResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public class ConfigurationResponse : RestResponseBase 6 | { 7 | private Dictionary list; 8 | 9 | public ConfigurationResponse() { } 10 | 11 | public ConfigurationResponse(uint clientTransactionID, uint transactionID, Dictionary value) 12 | { 13 | base.ClientTransactionID = clientTransactionID; 14 | base.ServerTransactionID = transactionID; 15 | list = value; 16 | } 17 | 18 | public Dictionary Value 19 | { 20 | get { return list; } 21 | set { list = value; } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/DateAndTimeResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public class DateTimeResponse : RestResponseBase 6 | { 7 | public DateTimeResponse() { } 8 | 9 | public DateTimeResponse(uint clientTransactionID, uint transactionID, DateTime value) 10 | { 11 | base.ServerTransactionID = transactionID; 12 | base.ClientTransactionID = clientTransactionID; 13 | Value = value; 14 | } 15 | 16 | public DateTime Value { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/DoubleArray2DResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class DoubleArray2DResponse : ImageArrayResponseBase 4 | { 5 | private double[,] doubleArray2D; 6 | 7 | private const int RANK = 2; 8 | private const SharedConstants.ImageArrayElementTypes TYPE = SharedConstants.ImageArrayElementTypes.Double; 9 | 10 | public DoubleArray2DResponse(uint clientTransactionID, uint transactionID, double[,] value) 11 | { 12 | base.ServerTransactionID = transactionID; 13 | doubleArray2D = value; 14 | base.Type = (int)TYPE; 15 | base.Rank = RANK; 16 | base.ClientTransactionID = clientTransactionID; 17 | } 18 | 19 | public double[,] Value 20 | { 21 | get { return doubleArray2D; } 22 | set 23 | { 24 | doubleArray2D = value; 25 | base.Type = (int)TYPE; 26 | base.Rank = RANK; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/DoubleArray3DResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class DoubleArray3DResponse : ImageArrayResponseBase 4 | { 5 | private double[,,] doubleArray3D; 6 | 7 | private const int RANK = 3; 8 | private const SharedConstants.ImageArrayElementTypes TYPE = SharedConstants.ImageArrayElementTypes.Double; 9 | 10 | public DoubleArray3DResponse(uint clientTransactionID, uint transactionID, double[,,] value) 11 | { 12 | base.ServerTransactionID = transactionID; 13 | doubleArray3D = value; 14 | base.Type = (int)TYPE; 15 | base.Rank = RANK; 16 | base.ClientTransactionID = clientTransactionID; 17 | } 18 | 19 | public double[,,] Value 20 | { 21 | get { return doubleArray3D; } 22 | set 23 | { 24 | doubleArray3D = value; 25 | base.Type = (int)TYPE; 26 | base.Rank = RANK; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/DoubleResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class DoubleResponse : RestResponseBase 4 | { 5 | public DoubleResponse() { } 6 | 7 | public DoubleResponse(uint clientTransactionID, uint transactionID, double value) 8 | { 9 | base.ServerTransactionID = transactionID; 10 | base.ClientTransactionID = clientTransactionID; 11 | Value = value; 12 | } 13 | 14 | public double Value { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/ImageArrayResponseBase.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public class ImageArrayResponseBase : RestResponseBase 6 | { 7 | private int rank = 0; 8 | private SharedConstants.ImageArrayElementTypes type = SharedConstants.ImageArrayElementTypes.Unknown; 9 | 10 | [JsonProperty(Order = -3)] 11 | public int Type 12 | { 13 | get { return (int)type; } 14 | set { type = (SharedConstants.ImageArrayElementTypes)value; } 15 | } 16 | 17 | [JsonProperty(Order = -2)] 18 | public int Rank 19 | { 20 | get { return rank; } 21 | set { rank = value; } 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/IntArray1DResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class IntArray1DResponse : RestResponseBase 4 | { 5 | private int[] intArray1D; 6 | 7 | public IntArray1DResponse() { } 8 | 9 | public IntArray1DResponse(uint clientTransactionID, uint transactionID, int[] value) 10 | { 11 | base.ServerTransactionID = transactionID; 12 | base.ClientTransactionID = clientTransactionID; 13 | intArray1D = value; 14 | } 15 | 16 | public int[] Value 17 | { 18 | get { return intArray1D; } 19 | set { intArray1D = value; } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/IntArray2DResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class IntArray2DResponse : ImageArrayResponseBase 4 | { 5 | private int[,] intArray2D; 6 | 7 | private const int RANK = 2; 8 | private const SharedConstants.ImageArrayElementTypes TYPE = SharedConstants.ImageArrayElementTypes.Int32; 9 | 10 | public IntArray2DResponse(uint clientTransactionID, uint transactionID) 11 | { 12 | base.ServerTransactionID = transactionID; 13 | base.ClientTransactionID = clientTransactionID; 14 | } 15 | 16 | public int[,] Value 17 | { 18 | get { return intArray2D; } 19 | set 20 | { 21 | intArray2D = value; 22 | base.Type = (int)TYPE; 23 | base.Rank = RANK; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/IntArray3DResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class IntArray3DResponse : ImageArrayResponseBase 4 | { 5 | private int[,,] intArray3D; 6 | 7 | private const int RANK = 3; 8 | private const SharedConstants.ImageArrayElementTypes TYPE = SharedConstants.ImageArrayElementTypes.Int32; 9 | 10 | public IntArray3DResponse(uint clientTransactionID, uint transactionID) 11 | { 12 | base.ServerTransactionID = transactionID; 13 | base.ClientTransactionID = clientTransactionID; 14 | } 15 | 16 | public int[,,] Value 17 | { 18 | get { return intArray3D; } 19 | set 20 | { 21 | intArray3D = value; 22 | base.Type = (int)TYPE; 23 | base.Rank = RANK; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/IntResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class IntResponse : RestResponseBase 4 | { 5 | public IntResponse() { } 6 | 7 | public IntResponse(uint clientTransactionID, uint transactionID, int value) 8 | { 9 | base.ServerTransactionID = transactionID; 10 | base.ClientTransactionID = clientTransactionID; 11 | Value = value; 12 | } 13 | 14 | public int Value { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/MethodResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public enum MethodTypes 4 | { 5 | PropertyGet, 6 | PropertySet, 7 | Method, 8 | Function 9 | } 10 | 11 | public class MethodResponse : RestResponseBase 12 | { 13 | public MethodResponse() { } 14 | public MethodResponse(uint clientTransactionID, uint transactionID) 15 | { 16 | base.ServerTransactionID = transactionID; 17 | base.ClientTransactionID = clientTransactionID; 18 | } 19 | // No additional fields for this class 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/ProfileResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public class ProfileResponse : RestResponseBase 6 | { 7 | private List list; 8 | 9 | public ProfileResponse() { } 10 | 11 | public ProfileResponse(uint clientTransactionID, uint transactionID, List value) 12 | { 13 | base.ServerTransactionID = transactionID; 14 | list = value; 15 | base.ClientTransactionID = clientTransactionID; 16 | } 17 | 18 | public List Value 19 | { 20 | get { return list; } 21 | set { list = value; } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/RateResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class RateResponse(double minimum, double maximum) 4 | { 5 | private double maximum = maximum; 6 | private double minimum = minimum; 7 | 8 | public double Maximum 9 | { 10 | get { return this.maximum; } 11 | set { this.maximum = value; } 12 | } 13 | 14 | public double Minimum 15 | { 16 | get { return this.minimum; } 17 | set { this.minimum = value; } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/RestResponseBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public abstract class RestResponseBase 6 | { 7 | private Exception exception; 8 | 9 | public uint ClientTransactionID { get; set; } 10 | public uint ServerTransactionID { get; set; } 11 | public int ErrorNumber { get; set; } = 0; 12 | public string ErrorMessage { get; set; } = ""; 13 | public Exception DriverException 14 | { 15 | get 16 | { 17 | return exception; 18 | } 19 | set 20 | { 21 | exception = value; 22 | if (exception != null) 23 | { 24 | // Set the error number and message fields from the exception 25 | ErrorNumber = exception.HResult; 26 | ErrorMessage = exception.Message; 27 | 28 | // Convert ASCOM exception error numbers (0x80040400 - 0x80040FFF) to equivalent Alpaca error numbers (0x400 to 0xFFF) so that they will be interpreted correctly by native Alpaca clients 29 | if ((ErrorNumber >= SharedConstants.ASCOM_ERROR_NUMBER_BASE) && (ErrorNumber <= SharedConstants.ASCOM_ERROR_NUMBER_MAX)) 30 | { 31 | ErrorNumber = ErrorNumber - SharedConstants.ASCOM_ERROR_NUMBER_OFFSET; 32 | } 33 | } 34 | } 35 | } 36 | 37 | /// 38 | /// Method used by NewtonSoft JSON to determine whether the DriverException field should be included in the serialise JSON response. 39 | /// 40 | /// 41 | public bool ShouldSerializeDriverException() 42 | { 43 | return SerializeDriverException; 44 | } 45 | 46 | /// 47 | /// Control variable that determines whether the DriverException field will be included in serialised JSON responses 48 | /// 49 | internal bool SerializeDriverException { get; set; } = true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/ShortArray2DResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class ShortArray2DResponse : ImageArrayResponseBase 4 | { 5 | private short[,] shortArray2D; 6 | 7 | private const int RANK = 2; 8 | private const SharedConstants.ImageArrayElementTypes TYPE = SharedConstants.ImageArrayElementTypes.Int16; 9 | 10 | public ShortArray2DResponse(uint clientTransactionID, uint transactionID, short[,] value) 11 | { 12 | base.ServerTransactionID = transactionID; 13 | shortArray2D = value; 14 | base.Type = (int)TYPE; 15 | base.Rank = RANK; 16 | base.ClientTransactionID = clientTransactionID; 17 | } 18 | 19 | public short[,] Value 20 | { 21 | get { return shortArray2D; } 22 | set 23 | { 24 | shortArray2D = value; 25 | base.Type = (int)TYPE; 26 | base.Rank = RANK; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/ShortArray3DResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class ShortArray3DResponse : ImageArrayResponseBase 4 | { 5 | private short[,,] shortArray3D; 6 | 7 | private const int RANK = 3; 8 | private const SharedConstants.ImageArrayElementTypes TYPE = SharedConstants.ImageArrayElementTypes.Int16; 9 | 10 | public ShortArray3DResponse(uint clientTransactionID, uint transactionID, short[,,] value) 11 | { 12 | base.ServerTransactionID = transactionID; 13 | shortArray3D = value; 14 | base.Type = (int)TYPE; 15 | base.Rank = RANK; 16 | base.ClientTransactionID = clientTransactionID; 17 | } 18 | 19 | public short[,,] Value 20 | { 21 | get { return shortArray3D; } 22 | set 23 | { 24 | shortArray3D = value; 25 | base.Type = (int)TYPE; 26 | base.Rank = RANK; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/ShortResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class ShortResponse : RestResponseBase 4 | { 5 | public ShortResponse() { } 6 | 7 | public ShortResponse(uint clientTransactionID, uint transactionID, short value) 8 | { 9 | base.ServerTransactionID = transactionID; 10 | base.ClientTransactionID = clientTransactionID; 11 | Value = value; 12 | } 13 | 14 | public short Value { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/StringArrayResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class StringArrayResponse : RestResponseBase 4 | { 5 | private string[] stringArray; 6 | 7 | public StringArrayResponse() { } 8 | 9 | public StringArrayResponse(uint clientTransactionID, uint transactionID, string[] value) 10 | { 11 | base.ServerTransactionID = transactionID; 12 | stringArray = value; 13 | base.ClientTransactionID = clientTransactionID; 14 | } 15 | 16 | public string[] Value 17 | { 18 | get { return stringArray; } 19 | set { stringArray = value; } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/StringListResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public class StringListResponse:RestResponseBase 6 | { 7 | private List list; 8 | 9 | public StringListResponse() { } 10 | 11 | public StringListResponse(uint clientTransactionID, uint transactionID, List value) 12 | { 13 | base.ServerTransactionID = transactionID; 14 | list = value; 15 | base.ClientTransactionID = clientTransactionID; 16 | } 17 | 18 | public List Value 19 | { 20 | get { return list; } 21 | set { list = value; } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/StringResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | public class StringResponse : RestResponseBase 4 | { 5 | public StringResponse() { } 6 | 7 | public StringResponse(uint clientTransactionID, uint transactionID, string value) 8 | { 9 | base.ServerTransactionID = transactionID; 10 | base.ClientTransactionID = clientTransactionID; 11 | Value = value; 12 | } 13 | 14 | public string Value { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Remote Server/ResponseClasses/TrackingRatesResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ASCOM.Common.DeviceInterfaces; 3 | 4 | namespace ASCOM.Remote 5 | { 6 | public class TrackingRatesResponse : RestResponseBase 7 | { 8 | private List rates; 9 | 10 | public TrackingRatesResponse() { } 11 | 12 | public TrackingRatesResponse(uint clientTransactionID, uint transactionID) 13 | { 14 | base.ServerTransactionID = transactionID; 15 | base.ClientTransactionID = clientTransactionID; 16 | } 17 | 18 | public List Value 19 | { 20 | get { return rates; } 21 | set { rates = value; } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Remote Server/ServedDevice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | using System.Runtime.InteropServices; 6 | using ASCOM.Com; 7 | using ASCOM.Common; 8 | 9 | namespace ASCOM.Remote 10 | { 11 | public partial class ServedDevice : UserControl 12 | { 13 | 14 | #region Variables 15 | 16 | int deviceNumber = 0; 17 | string description = ""; 18 | string progID = ""; 19 | bool devicesAreConnected = false; 20 | 21 | Profile profile; 22 | readonly List deviceTypes; 23 | readonly Dictionary deviceDictionary; 24 | SetupForm setupForm; 25 | bool recalculate = false; 26 | 27 | #endregion 28 | 29 | #region Initialisers 30 | public ServedDevice() 31 | { 32 | InitializeComponent(); 33 | 34 | // Create generic lists 35 | deviceTypes = []; 36 | deviceDictionary = []; 37 | 38 | cmbDeviceType.MouseUp += CmbDeviceType_MouseUp; // To force a device number recalculation if the device type is changed 39 | 40 | // The combo boxes have to be self painted because the DropDownStyle is DropDownList, to make the list read only, and this changes the background colour to grey! 41 | cmbDevice.DrawMode = DrawMode.OwnerDrawFixed; 42 | cmbDevice.DrawItem += new DrawItemEventHandler(ComboBox_DrawItem); // Attach an event handler to draw the combo box on demand 43 | cmbDeviceType.DrawMode = DrawMode.OwnerDrawFixed; 44 | cmbDeviceType.DrawItem += new DrawItemEventHandler(ComboBox_DrawItem); // Attach an event handler to draw the combo box on demand 45 | } 46 | 47 | public void InitUI(SetupForm parent) 48 | { 49 | setupForm = parent; 50 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.InitUI", "Start"); 51 | profile = new Profile(); 52 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.InitUI", "Created Profile"); 53 | 54 | cmbDeviceType.Items.Add(SharedConstants.DEVICE_NOT_CONFIGURED); 55 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.InitUI", "Added Device not configured"); 56 | 57 | 58 | foreach (string deviceType in Devices.DeviceTypeNames()) 59 | { 60 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.InitUI", "Adding item: " + deviceType); 61 | cmbDeviceType.Items.Add(deviceType); 62 | deviceTypes.Add(deviceType); // Remember the device types on this system 63 | } 64 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.InitUI", "Setting selected index to 0"); 65 | 66 | cmbDeviceType.SelectedIndex = 0; 67 | 68 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.InitUI", "Finished"); 69 | 70 | } 71 | #endregion 72 | 73 | #region Data accessor properties 74 | 75 | public int DeviceNumber 76 | { 77 | get 78 | { 79 | return deviceNumber; 80 | } 81 | set 82 | { 83 | deviceNumber = value; 84 | txtDeviceNumber.Text = value.ToString(); 85 | } 86 | } 87 | 88 | public string DeviceType 89 | { 90 | get 91 | { 92 | try 93 | { 94 | return cmbDeviceType.SelectedItem.ToString(); 95 | } 96 | catch 97 | { 98 | return "None"; 99 | } 100 | } 101 | set 102 | { 103 | try 104 | { 105 | cmbDeviceType.SelectedItem = value; 106 | } 107 | catch 108 | { 109 | cmbDeviceType.SelectedIndex = -1; ; 110 | } 111 | } 112 | } 113 | 114 | public string Description 115 | { 116 | get 117 | { 118 | try 119 | { 120 | return description; 121 | } 122 | catch 123 | { 124 | return ""; 125 | } 126 | } 127 | set 128 | { 129 | try 130 | { 131 | description = value; 132 | cmbDevice.SelectedItem = value.ToString(); 133 | } 134 | catch { } 135 | } 136 | } 137 | 138 | public string ProgID 139 | { 140 | get 141 | { 142 | return progID; 143 | } 144 | set 145 | { 146 | progID = value; 147 | if (!DesignMode) 148 | { 149 | try 150 | { 151 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.ProgID", "Set ProgID to: " + progID); 152 | switch (progID) 153 | { 154 | case "": 155 | cmbDevice.SelectedIndex = -1; 156 | break; 157 | case SharedConstants.DEVICE_NOT_CONFIGURED: 158 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.ProgID", "Description: " + SharedConstants.DEVICE_NOT_CONFIGURED); 159 | cmbDevice.SelectedItem = 0; 160 | break; 161 | default: 162 | //ServerForm.LogMessage(0, 0, 0, "ServedDevice.ProgID", "Description: " + deviceDictionary[progID]); 163 | cmbDevice.SelectedItem = deviceDictionary[progID]; 164 | break; 165 | } 166 | } 167 | catch (Exception ex) 168 | { 169 | ServerForm.LogException(0, 0, 0, "ServedDevice.ProgID", ex.ToString()); 170 | cmbDevice.SelectedIndex = -1; 171 | } 172 | } 173 | } 174 | } 175 | 176 | public bool AllowConnectedSetFalse 177 | { 178 | get 179 | { 180 | return chkAllowSetConnectedFalse.Checked; 181 | } 182 | set 183 | { 184 | chkAllowSetConnectedFalse.Checked = value; 185 | } 186 | } 187 | 188 | public bool AllowConnectedSetTrue 189 | { 190 | get 191 | { 192 | return chkAllowSetConnectedTrue.Checked; 193 | } 194 | set 195 | { 196 | chkAllowSetConnectedTrue.Checked = value; 197 | } 198 | } 199 | 200 | public bool DevicesAreConnected 201 | { 202 | get 203 | { 204 | return devicesAreConnected; 205 | } 206 | set 207 | { 208 | devicesAreConnected = value; 209 | btnSetup.Enabled = !devicesAreConnected; 210 | } 211 | } 212 | 213 | public bool AllowConcurrentAccess 214 | { 215 | get 216 | { 217 | return ChkAllowConcurrentAccess.Checked; 218 | } 219 | set 220 | { 221 | ChkAllowConcurrentAccess.Checked = value; 222 | } 223 | } 224 | #endregion 225 | 226 | #region Event handlers 227 | private void CmbDeviceType_SelectedIndexChanged(object sender, EventArgs e) 228 | { 229 | try 230 | { 231 | 232 | //ServerForm.LogMessage(0, 0, 0, this.Name, "cmbDeviceType_Changed - Started"); 233 | cmbDevice.Items.Clear(); 234 | 235 | //ServerForm.LogMessage(0, 0, 0, this.Name, "cmbDeviceType_Changed - Setting selected index to -1"); 236 | cmbDevice.SelectedIndex = -1; 237 | 238 | //ServerForm.LogMessage(0, 0, 0, this.Name, "cmbDeviceType_Changed - Resetting instance number"); 239 | DeviceNumber = 0; 240 | 241 | if (cmbDeviceType.SelectedItem.ToString() == SharedConstants.DEVICE_NOT_CONFIGURED) 242 | { 243 | //ServerForm.LogMessage(0, 0, 0, this.Name, "cmbDeviceType_Changed - \"None\" device type selected"); 244 | cmbDevice.Items.Clear(); 245 | cmbDevice.SelectedIndex = -1; 246 | cmbDevice.ResetText(); 247 | cmbDevice.Enabled = false; 248 | description = ""; 249 | progID = SharedConstants.DEVICE_NOT_CONFIGURED; 250 | 251 | } 252 | else 253 | { 254 | cmbDevice.Enabled = true; 255 | //ServerForm.LogMessage(0, 0, 0, this.Name, "cmbDeviceType_Changed - Real device type has been selected"); 256 | } 257 | 258 | 259 | if (recalculate) 260 | { 261 | setupForm.RecalculateDeviceNumbers(); 262 | recalculate = false; 263 | } 264 | 265 | // Set up device list so we can translate ProgID to description 266 | if (cmbDeviceType.SelectedItem.ToString() != SharedConstants.DEVICE_NOT_CONFIGURED) 267 | { 268 | List installedDevices = Profile.GetDrivers(Devices.StringToDeviceType(cmbDeviceType.SelectedItem.ToString())); 269 | //ServerForm.LogMessage(0, 0, 0, this.Name, "cmbDeviceType_Changed - Created registered device array list"); 270 | 271 | deviceDictionary.Clear(); 272 | foreach (ASCOMRegistration kvp in installedDevices) 273 | { 274 | if (!deviceDictionary.ContainsKey(kvp.ProgID)) deviceDictionary.Add(kvp.ProgID, kvp.Name); 275 | cmbDevice.Items.Add(kvp.Name); 276 | } 277 | if (cmbDevice.Items.Count > 0) cmbDevice.SelectedIndex = 0; 278 | //ServerForm.LogMessage(0, 0, 0, this.Name, "cmbDeviceType_Changed - Finished"); 279 | } 280 | 281 | } 282 | catch (Exception ex) 283 | { 284 | MessageBox.Show(ex.ToString()); 285 | } 286 | 287 | } 288 | 289 | private void CmbDevice_SelectedIndexChanged(object sender, EventArgs e) 290 | { 291 | description = cmbDevice.SelectedItem.ToString(); 292 | foreach (KeyValuePair kvp in deviceDictionary) 293 | { 294 | if (kvp.Value == description) 295 | { 296 | progID = kvp.Key; 297 | } 298 | } 299 | } 300 | 301 | private void CmbDeviceType_MouseUp(object sender, MouseEventArgs e) 302 | { 303 | recalculate = true; 304 | } 305 | 306 | /// 307 | /// Event handler to paint the device list combo box in the "DropDown" rather than "DropDownList" style 308 | /// 309 | /// Device to be painted 310 | /// Draw event arguments object 311 | public void ComboBox_DrawItem(object sender, DrawItemEventArgs e) 312 | { 313 | if (e.Index < 0) return; 314 | 315 | ComboBox combo = sender as ComboBox; 316 | if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) // Draw the selected item in menu highlight colour 317 | { 318 | e.Graphics.FillRectangle(new SolidBrush(SystemColors.MenuHighlight), e.Bounds); 319 | e.Graphics.DrawString(combo.Items[e.Index].ToString(), e.Font, new SolidBrush(SystemColors.HighlightText), new Point(e.Bounds.X, e.Bounds.Y)); 320 | } 321 | else 322 | { 323 | e.Graphics.FillRectangle(new SolidBrush(SystemColors.Window), e.Bounds); 324 | e.Graphics.DrawString(combo.Items[e.Index].ToString(), e.Font, new SolidBrush(combo.ForeColor), new Point(e.Bounds.X, e.Bounds.Y)); 325 | } 326 | 327 | e.DrawFocusRectangle(); 328 | } 329 | 330 | private void BtnSetup_Click(object sender, EventArgs e) 331 | { 332 | // This device's ProgID is held in the variable progID so try and run its SetupDialog method 333 | ServerForm.LogMessage(0, 0, 0, "Setup", $"Setup button pressed for device: {cmbDevice.Text}, ProgID: {progID}"); 334 | 335 | try 336 | { 337 | // Get an instance of the driver from its ProgID and store this in a dynamic variable so that we can call its method directly 338 | Type ProgIdType = Type.GetTypeFromProgID(progID); 339 | 340 | dynamic oDrv = Activator.CreateInstance(ProgIdType); 341 | 342 | try 343 | { 344 | if (GetConnectdState(oDrv)) // Driver is connected and the Setup dialogue must be run with the device disconnected so ask whether we can disconnect it 345 | { 346 | DialogResult dialogResult = MessageBox.Show("Device is connected, OK to disconnect and run Setup?", "Disconnect Device?", MessageBoxButtons.OKCancel); 347 | if (dialogResult == DialogResult.OK) // OK to disconnect and run setup dialogue 348 | { 349 | try { oDrv.Connected = false; } catch { }; // Set Connected to false ignoring errors 350 | try { oDrv.Link = false; } catch { }; // Set Link to false (for IFocuserV1 devices) ignoring errors 351 | 352 | int RemainingObjectCount = Marshal.FinalReleaseComObject(oDrv); 353 | 354 | oDrv = null; 355 | oDrv = Activator.CreateInstance(ProgIdType); 356 | 357 | oDrv.SetupDialog(); 358 | 359 | try 360 | { 361 | oDrv.Connected = true; // Try setting Connected to true 362 | } 363 | catch (Exception ex2) when (DeviceType.Equals("focuser", StringComparison.InvariantCultureIgnoreCase)) 364 | { 365 | // Connected failed so try Link in case this is an IFocuserV1 device 366 | ServerForm.LogException(0, 0, 0, "Setup", $"Error setting Connected to true for focuser device {ProgID}, now trying Link for IFocuserV1 devices: \r\n{ex2}"); 367 | oDrv.Link = true; 368 | } 369 | } 370 | else // Not OK to disconnect so just do nothing and exit 371 | { 372 | ServerForm.LogMessage(0, 0, 0, "Setup", "User did not give permission to disconnect device - no action taken"); 373 | } 374 | } 375 | else // Driver is not connected 376 | { 377 | oDrv.SetupDialog(); 378 | 379 | try { oDrv.Dispose(); } catch { }; // Dispose the driver if possible 380 | 381 | // Release the COM object properly 382 | try 383 | { 384 | //ServerForm.LogMessage(0, 0, 0, "Setup", " Releasing COM object"); 385 | int LoopCount = 0; 386 | int RemainingObjectCount = 0; 387 | 388 | do 389 | { 390 | LoopCount += 1; // Increment the loop counter so that we don't go on for ever! 391 | RemainingObjectCount = Marshal.ReleaseComObject(oDrv); 392 | } while ((RemainingObjectCount > 0) & (LoopCount < 20)); 393 | } 394 | catch (Exception ex2) 395 | { 396 | ServerForm.LogMessage(0, 0, 0, "Setup", $" ReleaseComObject Exception: {ex2.Message}"); 397 | } 398 | 399 | oDrv = null; 400 | } 401 | } 402 | catch (Exception ex1) 403 | { 404 | string errMsg = $"Exception calling SetupDialog method: {ex1.Message}"; 405 | MessageBox.Show(errMsg); 406 | ServerForm.LogMessage(0, 0, 0, "Setup", errMsg); 407 | ServerForm.LogException(0, 0, 0, "Setup", ex1.ToString()); 408 | } 409 | 410 | } 411 | catch (Exception ex) 412 | { 413 | string errMsg = $"Exception creating driver {progID} - {ex.Message}"; 414 | MessageBox.Show(errMsg); 415 | ServerForm.LogMessage(0, 0, 0, "Setup", errMsg); 416 | ServerForm.LogException(0, 0, 0, "Setup", ex.ToString()); 417 | } 418 | } 419 | 420 | #endregion 421 | 422 | #region Support code 423 | private static bool GetConnectdState(dynamic driverObject) 424 | { 425 | bool connectedState; 426 | 427 | try 428 | { 429 | connectedState = driverObject.Connected; 430 | } 431 | catch 432 | { 433 | connectedState = driverObject.Link; 434 | } 435 | 436 | return connectedState; 437 | } 438 | #endregion 439 | } 440 | } 441 | -------------------------------------------------------------------------------- /Remote Server/Settings.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | internal class Settings 4 | { 5 | // Current version number for this settings class. Only needs to be incremented when there are breaking changes! 6 | // For example this can be left at its current level when adding new settings that have usable default values. 7 | 8 | // This value is set when values are actually persisted in ConformConfiguration.PersistSettings in order not to overwrite the value that is retrieved from the current settings file when it is read. 9 | internal const int SETTINGS_COMPATIBILTY_VERSION = 1; 10 | 11 | public int SettingsCompatibilityVersion { get; set; } = SETTINGS_COMPATIBILTY_VERSION; // Default is zero so that versions prior to introduction of the settings compatibility version number can be detected. 12 | 13 | public bool RunAs64Bit { get; set; } = false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Remote Server/Shared Constants.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public static class SharedConstants 6 | { 7 | // Alpaca and ASCOM error number constants 8 | public const int ASCOM_ERROR_NUMBER_OFFSET = unchecked((int)0x80040000); // Offset value that relates the ASCOM Alpaca reserved error number range to the ASCOM COM HResult error number range 9 | public const int ASCOM_ERROR_NUMBER_BASE = unchecked((int)0x80040400); // Lowest ASCOM error number 10 | public const int ASCOM_ERROR_NUMBER_MAX = unchecked((int)0x80040FFF); // Highest ASCOM error number 11 | public const int ALPACA_ERROR_CODE_BASE = 0x400; // Start of the Alpaca error code range 0x400 to 0xFFF 12 | public const int ALPACA_ERROR_CODE_MAX = 0xFFF; // End of Alpaca error code range 0x400 to 0xFFF 13 | 14 | // Device API URL element positions /api/v1/telescope/0/method 15 | public const int URL_ELEMENT_API = 0; // For /api/ URIs 16 | public const int URL_ELEMENT_API_VERSION = 1; 17 | public const int URL_ELEMENT_DEVICE_TYPE = 2; 18 | public const int URL_ELEMENT_DEVICE_NUMBER = 3; 19 | public const int URL_ELEMENT_METHOD = 4; 20 | public const int URL_ELEMENT_SERVER_COMMAND = 2; // For /server/ type URIs 21 | 22 | // Regular expressions to validate IP addresses and host names 23 | public const string ValidIpAddressRegex = @"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"; 24 | public const string ValidHostnameRegex = @"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"; 25 | 26 | // HTTP Parameter names shared by driver and remote server 27 | public const string RA_PARAMETER_NAME = "RightAscension"; 28 | public const string DEC_PARAMETER_NAME = "Declination"; 29 | public const string ALT_PARAMETER_NAME = "Altitude"; 30 | public const string AZ_PARAMETER_NAME = "Azimuth"; 31 | public const string AXIS_PARAMETER_NAME = "Axis"; 32 | public const string RATE_PARAMETER_NAME = "Rate"; 33 | public const string DIRECTION_PARAMETER_NAME = "Direction"; 34 | public const string DURATION_PARAMETER_NAME = "Duration"; 35 | public const string CLIENT_ID_PARAMETER_NAME = "ClientID"; 36 | public const string CLIENT_TRANSACTION_ID_PARAMETER_NAME = "ClientTransactionID"; 37 | public const string COMMAND_PARAMETER_NAME = "Command"; 38 | public const string RAW_PARAMETER_NAME = "Raw"; 39 | public const string LIGHT_PARAMETER_NAME = "Light"; 40 | public const string ACTION_COMMAND_PARAMETER_NAME = "Action"; 41 | public const string ACTION_PARAMETERS_PARAMETER_NAME = "Parameters"; 42 | public const string ID_PARAMETER_NAME = "Id"; 43 | public const string STATE_PARAMETER_NAME = "State"; 44 | public const string NAME_PARAMETER_NAME = "Name"; 45 | public const string VALUE_PARAMETER_NAME = "Value"; 46 | public const string POSITION_PARAMETER_NAME = "Position"; 47 | public const string SIDEOFPIER_PARAMETER_NAME = "SideOfPier"; 48 | public const string UTCDATE_PARAMETER_NAME = "UTCDate"; 49 | public const string SENSORNAME_PARAMETER_NAME = "SensorName"; 50 | public const string BRIGHTNESS_PARAMETER_NAME = "Brightness"; 51 | 52 | public const string ISO8601_DATE_FORMAT_STRING = "yyyy-MM-ddTHH:mm:ss.fffffff"; 53 | 54 | // Remote client configuration constants 55 | public const int SOCKET_ERROR_MAXIMUM_RETRIES = 2; // The number of retries that the client will make when it receives a socket actively refused error from the remote server 56 | public const int SOCKET_ERROR_RETRY_DELAY_TIME = 1000; // The delay time (milliseconds) between socket actively refused retries 57 | 58 | // Remote server setup form constants 59 | public const string LOCALHOST_NAME_IPV4 = "localhost"; 60 | public const string LOCALHOST_ADDRESS_IPV4 = "127.0.0.1"; // Get the localhost loop back address 61 | public const string LOCALHOST_ADDRESS_IPV6 = "[::1]"; // Get the localhost loop back address 62 | public const string BIND_TO_ALL_INTERFACES_DESCRIPTION = "All IP Addresses"; // Text describing requirement to bind to all interfaces 63 | public const string BIND_TO_ALL_INTERFACES_IP_ADDRESS_STRONG = "+"; // IP address value that causes binding to all available IP addresses 64 | public const string BIND_TO_ALL_INTERFACES_IP_ADDRESS_WEAK = "*"; // IP address value that causes binding to all available IP addresses 65 | 66 | // Constants shared by Remote Client Drivers and the ASCOM Remote Server 67 | public const string API_URL_BASE = "/api/"; // This constant must always be lower case to make the logic tests work properly 68 | public const string API_VERSION_V1 = "v1"; // This constant must always be lower case to make the logic tests work properly 69 | public static readonly int[] MANAGEMENT_SUPPORTED_INTERFACE_VERSIONS = [1]; // Array of supported interface versions that is returned through the management API 70 | public const string REMOTE_SERVER_MANAGEMENT_URL_BASE = "/server/"; // Management commands unique to the remote server. This constant must always be lower case to make the logic tests work properly 71 | public const string ALPACA_DEVICE_MANAGEMENT_URL_BASE = "/management/"; // management commands common to all Alpaca devices. This constant must always be lower case to make the logic tests work properly 72 | public const string ALPACA_DEVICE_SETUP_URL_BASE = "/setup/"; // management commands common to all Alpaca devices. This constant must always be lower case to make the logic tests work properly 73 | 74 | // Remote server management API interface constants 75 | public const string REMOTE_SERVER_MANGEMENT_GET_PROFILE = "profile"; 76 | public const string REMOTE_SERVER_MANGEMENT_GET_CONFIGURATION = "configuration"; 77 | public const string REMOTE_SERVER_MANGEMENT_GET_CONCURRENT_CALLS = "concurrency"; 78 | public const string REMOTE_SERVER_MANGEMENT_RESTART_SERVER = "restart"; 79 | public const string REMOTE_SERVER_MANGEMENT_REBOOT_SERVER = "reboot"; 80 | public const string REMOTE_SERVER_MANGEMENT_SHUTDOWN_SERVER = "shutdown"; 81 | public const string ALPACA_DEVICE_MANAGEMENT_APIVERSIONS = "apiversions"; 82 | public const string ALPACA_DEVICE_MANAGEMENT_DESCRIPTION = "description"; 83 | public const string ALPACA_DEVICE_MANAGEMENT_CONFIGURED_DEVICES = "configureddevices"; 84 | public const string ALPACA_DEVICE_MANAGEMENT_MANUFACTURER = "Peter Simpson"; 85 | public const string ALPACA_DEVICE_MANAGEMENT_SERVERNAME = "ASCOM Remote Server"; 86 | 87 | // Remote server browser interface URL constants 88 | public const string BROWSER_URL_SETUP = "setup"; 89 | 90 | // Constants used to set network permissions 91 | public const string SET_NETWORK_PERMISSIONS_EXE_PATH = @"\ASCOM\RemoteServer\SetNetworkPermissions\ASCOM.SetNetworkPermissions.exe"; //Relative path of the SetNetworkPermissions exe from C:\Program Files (or x86 on 64bit OS). Must match the location where the installer puts the exe! 92 | public const string ENABLE_REMOTE_SERVER_MANAGEMENT_URI_COMMAND_NAME = "setremoteservermanagementuriacl"; 93 | public const string ENABLE_ALPACA_DEVICE_MANAGEMENT_URI_COMMAND_NAME = "setalpacamanagementurl"; 94 | public const string ENABLE_ALPACA_SETUP_URI_COMMAND_NAME = "setalpacasetupurl"; 95 | public const string ENABLE_API_URI_COMMAND_NAME = "setapiuriacl"; 96 | public const string ENABLE_HTTP_DOT_SYS_PORT_COMMAND_NAME = "enablehttpdotsysport"; 97 | public const string SET_LOCAL_SERVER_PATH_COMMAND_NAME = "localserverpath"; 98 | public const string SET_REMOTE_SERVER_PATH_COMMAND_NAME = "remoteserverpath"; 99 | public const string USER_NAME_COMMAND_NAME = "username"; 100 | 101 | // Client driver profile persistence constants 102 | public const string TRACE_LEVEL_PROFILENAME = "Trace Level"; public const bool CLIENT_TRACE_LEVEL_DEFAULT = true; 103 | public const string DEBUG_TRACE_PROFILENAME = "Include Debug Trace"; public const bool DEBUG_TRACE_DEFAULT = false; 104 | public const string IPADDRESS_PROFILENAME = "IP Address"; public const string IPADDRESS_DEFAULT = SharedConstants.LOCALHOST_ADDRESS_IPV4; 105 | public const string PORTNUMBER_PROFILENAME = "Port Number"; public const decimal PORTNUMBER_DEFAULT = 11111; 106 | public const string REMOTE_DEVICE_NUMBER_PROFILENAME = "Remote Device Number"; public const decimal REMOTE_DEVICE_NUMBER_DEFAULT = 0; 107 | public const string SERVICE_TYPE_PROFILENAME = "Service Type"; public const string SERVICE_TYPE_DEFAULT = "http"; 108 | public const string ESTABLISH_CONNECTION_TIMEOUT_PROFILENAME = "Establish Connection Timeout"; public const int ESTABLISH_CONNECTION_TIMEOUT_DEFAULT = 10; 109 | public const string STANDARD_SERVER_RESPONSE_TIMEOUT_PROFILENAME = "Standard Server Response Timeout"; public const int STANDARD_SERVER_RESPONSE_TIMEOUT_DEFAULT = 10; 110 | public const string LONG_SERVER_RESPONSE_TIMEOUT_PROFILENAME = "Long Server Response Timeout"; public const int LONG_SERVER_RESPONSE_TIMEOUT_DEFAULT = 120; 111 | public const string USERNAME_PROFILENAME = "User Name"; public const string USERNAME_DEFAULT = ""; 112 | public const string PASSWORD_PROFILENAME = "Password"; public const string PASSWORD_DEFAULT = ""; 113 | public const string MANAGE_CONNECT_LOCALLY_PROFILENAME = "Manage Connect Locally"; public const bool MANAGE_CONNECT_LOCALLY_DEFAULT = false; 114 | public const string IMAGE_ARRAY_TRANSFER_TYPE_PROFILENAME = "Image Array Transfer Type"; public const ImageArrayTransferType IMAGE_ARRAY_TRANSFER_TYPE_DEFAULT = DEFAULT_IMAGE_ARRAY_TRANSFER_TYPE; 115 | public const string IMAGE_ARRAY_COMPRESSION_PROFILENAME = "Image Array Compression"; public const ImageArrayCompression IMAGE_ARRAY_COMPRESSION_DEFAULT = DEFAULT_IMAGE_ARRAY_COMPRESSION; 116 | 117 | // Driver naming constants 118 | public const string DRIVER_DISPLAY_NAME = "ASCOM Remote Client"; 119 | public const string DRIVER_PROGID_BASE = "ASCOM.Remote"; 120 | public const string NOT_CONNECTED_MESSAGE = "is not connected."; // This is appended to the driver display name + driver number and displayed when the driver is not connected 121 | public const string TRACELOGGER_NAME_FORMAT_STRING = "Remote{0}.{1}"; 122 | 123 | // Enum to describe Camera.ImageArray and ImageArrayVCariant array types 124 | public enum ImageArrayElementTypes 125 | { 126 | Unknown = 0, 127 | Int16 = 1, 128 | Int32 = 2, 129 | Double = 3, 130 | Single = 4, 131 | Byte = 5, 132 | Int64 = 6 133 | } 134 | 135 | // Enum used by the remote client to indicate what type of Alpaca image array transfer should be used 136 | public enum ImageArrayTransferType 137 | { 138 | JSON = 0, 139 | Base64HandOff = 1, 140 | } 141 | 142 | // Enum used by the remote client to indicate what type of compression should be used in Alpaca responses 143 | public enum ImageArrayCompression 144 | { 145 | None = 0, 146 | Deflate = 1, 147 | GZip = 2, 148 | GZipOrDeflate = 3 149 | } 150 | 151 | // Default image array transfer constants 152 | public const ImageArrayCompression DEFAULT_IMAGE_ARRAY_COMPRESSION = ImageArrayCompression.None; 153 | public const ImageArrayTransferType DEFAULT_IMAGE_ARRAY_TRANSFER_TYPE = ImageArrayTransferType.JSON; 154 | 155 | // Image array base64 hand-off support constants 156 | public const string BASE64_HANDOFF_HEADER = "base64handoff"; // Name of HTTP header used to affirm binary serialisation support for image array data 157 | public const string BASE64_HANDOFF_SUPPORTED = "true"; // Value of HTTP header to indicate support for binary serialised image array data 158 | public const string BASE64_HANDOFF_FILE_DOWNLOAD_URI_EXTENSION = "base64"; // Addition to the ImageArray and ImageArrayVariant method names from which base64 serialised image files can be downloaded 159 | 160 | // Image bytes constants 161 | public const string ACCEPT_HEADER_NAME = "Accept"; // Name of HTTP header used to affirm ImageBytes support for image array data 162 | public const string IMAGE_BYTES_MIME_TYPE = "application/imagebytes"; 163 | 164 | // Registry key where the Web Server configuration will be stored 165 | public const RegistryHive ASCOM_REMOTE_CONFIGURATION_HIVE = RegistryHive.CurrentUser; 166 | public const string ASCOM_REMOTE_CONFIGURATION_KEY = @"Software\ASCOM Remote"; 167 | 168 | public const string REQUEST_RECEIVED_STRING = "RequestReceived"; 169 | 170 | public const string DEVICE_NOT_CONFIGURED = "None"; // ProgID / UniqueID / device type value indicating no device configured 171 | 172 | public const string CORS_DEFAULT_PERMISSION = "*"; // Default permission for CORS origins (The * value means "permit all origins") 173 | public const string CORS_SERIALISATION_SEPARATOR = "|"; 174 | 175 | // Alpaca discovery constants 176 | public const string ALPACA_DISCOVERY_BROADCAST_ID = "alpacadiscovery"; 177 | public const int ALPACA_DISCOVERY_PORT = 32227; 178 | public const string ALPACA_DISCOVERY_RESPONSE_STRING = "alpacaport"; 179 | public const string ALPACA_DISCOVERY_MULTICAST_GROUP = "ff12::a1:9aca"; 180 | 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /Remote Server/SharedResources.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | public static class Library 6 | { 7 | /// 8 | /// Convert 3D 32bit Integer array to double array 9 | /// 10 | /// 32bit integer array to be converted 11 | /// 3D double array containing the integer values 12 | public static double[,] Array2DToDouble(Array inputArray) 13 | { 14 | double[,] outputArray = new double[inputArray.GetLength(0), inputArray.GetLength(1)]; 15 | 16 | for (int j = 0; j <= inputArray.GetUpperBound(1); j++) 17 | { 18 | for (int i = 0; i <= inputArray.GetUpperBound(0); i++) 19 | { 20 | outputArray[i, j] = Convert.ToDouble(inputArray.GetValue(i, j)); 21 | } 22 | } 23 | return outputArray; 24 | } 25 | 26 | /// 27 | /// Convert 3D array to double array 28 | /// 29 | /// 32bit integer array to be converted 30 | /// 3D double array containing the integer values 31 | public static double[,,] Array3DToDouble(Array inputArray) 32 | { 33 | double[,,] outputArray = new double[inputArray.GetLength(0), inputArray.GetLength(1), inputArray.GetLength(2)]; 34 | 35 | for (int k = 0; k <= inputArray.GetUpperBound(2); k++) 36 | { 37 | for (int j = 0; j <= inputArray.GetUpperBound(1); j++) 38 | { 39 | for (int i = 0; i <= inputArray.GetUpperBound(0); i++) 40 | { 41 | outputArray[i, j, k] = Convert.ToDouble(inputArray.GetValue(i, j, k)); 42 | } 43 | } 44 | } 45 | return outputArray; 46 | } 47 | 48 | /// 49 | /// Convert 3D 32bit Integer array to double array 50 | /// 51 | /// 32bit integer array to be converted 52 | /// 3D double array containing the integer values 53 | public static int[,] Array2DToInt(Array inputArray) 54 | { 55 | int[,] outputArray = new int[inputArray.GetLength(0), inputArray.GetLength(1)]; 56 | 57 | for (int j = 0; j <= inputArray.GetUpperBound(1); j++) 58 | { 59 | for (int i = 0; i <= inputArray.GetUpperBound(0); i++) 60 | { 61 | outputArray[i, j] = Convert.ToInt32(inputArray.GetValue(i, j)); 62 | } 63 | } 64 | return outputArray; 65 | } 66 | 67 | /// 68 | /// Convert 3D array to double array 69 | /// 70 | /// 32bit integer array to be converted 71 | /// 3D double array containing the integer values 72 | public static int[,,] Array3DToInt(Array inputArray) 73 | { 74 | int[,,] outputArray = new int[inputArray.GetLength(0), inputArray.GetLength(1), inputArray.GetLength(2)]; 75 | 76 | for (int k = 0; k <= inputArray.GetUpperBound(2); k++) 77 | { 78 | for (int j = 0; j <= inputArray.GetUpperBound(1); j++) 79 | { 80 | for (int i = 0; i <= inputArray.GetUpperBound(0); i++) 81 | { 82 | outputArray[i, j, k] = Convert.ToInt32(inputArray.GetValue(i, j, k)); 83 | } 84 | } 85 | } 86 | return outputArray; 87 | } 88 | 89 | /// 90 | /// Convert 3D 32bit Integer array to double array 91 | /// 92 | /// 32bit integer array to be converted 93 | /// 3D double array containing the integer values 94 | public static short[,] Array2DToShort(Array inputArray) 95 | { 96 | short[,] outputArray = new short[inputArray.GetLength(0), inputArray.GetLength(1)]; 97 | 98 | for (int j = 0; j <= inputArray.GetUpperBound(1); j++) 99 | { 100 | for (int i = 0; i <= inputArray.GetUpperBound(0); i++) 101 | { 102 | outputArray[i, j] = Convert.ToInt16(inputArray.GetValue(i, j)); 103 | } 104 | } 105 | return outputArray; 106 | } 107 | 108 | /// 109 | /// Convert 3D array to double array 110 | /// 111 | /// 32bit integer array to be converted 112 | /// 3D double array containing the integer values 113 | public static short[,,] Array3DToShort(Array inputArray) 114 | { 115 | short[,,] outputArray = new short[inputArray.GetLength(0), inputArray.GetLength(1), inputArray.GetLength(2)]; 116 | 117 | for (int k = 0; k <= inputArray.GetUpperBound(2); k++) 118 | { 119 | for (int j = 0; j <= inputArray.GetUpperBound(1); j++) 120 | { 121 | for (int i = 0; i <= inputArray.GetUpperBound(0); i++) 122 | { 123 | outputArray[i, j, k] = Convert.ToInt16(inputArray.GetValue(i, j, k)); 124 | } 125 | } 126 | } 127 | return outputArray; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Remote Server/StringValue.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | /// 6 | /// Class to hold a string value for use with the data grid view control. 7 | /// This is required in order for the data grid view control to be able to bind to a List variable 8 | /// String values are trimmed to ensure that searches for the "*" wild card character succeed even if the user pre or postpends spaces when entering the value. 9 | /// 10 | public class StringValue(string s) 11 | { 12 | string stringValue = s.Trim(); 13 | 14 | [DisplayName("Permitted CORS Origins")] 15 | public string Value 16 | { 17 | get // Return the string value 18 | { 19 | return stringValue; 20 | } 21 | set // Set the string value, trimming it first 22 | { 23 | stringValue = value.Trim(); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Remote Server/Updates/GitHubReleases.cs: -------------------------------------------------------------------------------- 1 | using Octokit; 2 | using Semver; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace ASCOM.Remote 9 | { 10 | public static class GitHubReleases 11 | { 12 | public static Task> GetReleases(string owner, string name) 13 | { 14 | if (string.IsNullOrEmpty(owner)) 15 | { 16 | throw new ArgumentNullException(nameof(owner)); 17 | } 18 | 19 | if (string.IsNullOrEmpty(name)) 20 | { 21 | throw new ArgumentNullException(nameof(name)); 22 | } 23 | 24 | var Github = new GitHubClient(new ProductHeaderValue(name + @"-UpdateCheck")); 25 | 26 | return Github.Repository.Release.GetAll(owner, name); 27 | } 28 | 29 | public static Release LatestRelease(this IEnumerable releases) 30 | { 31 | ArgumentNullException.ThrowIfNull(releases); 32 | return releases.Where(rp => !rp.Prerelease).Latest(); 33 | } 34 | 35 | public static Release LatestPrerelease(this IEnumerable releases) 36 | { 37 | ArgumentNullException.ThrowIfNull(releases); 38 | return releases.Where(rp => rp.Prerelease).Latest(); 39 | 40 | } 41 | 42 | public static Release Latest(this IEnumerable releases) 43 | { 44 | ArgumentNullException.ThrowIfNull(releases); 45 | if (releases.Any()) 46 | { 47 | return releases.OrderBy(rp => rp.ReleaseSemVersionFromTag()).LastOrDefault(); 48 | } 49 | return null; 50 | } 51 | 52 | public static SemVersion ReleaseSemVersionFromTag(this Release release) 53 | { 54 | ArgumentNullException.ThrowIfNull(release); 55 | if (!string.IsNullOrEmpty(release.TagName) && SemVersion.TryParse(release.TagName, SemVersionStyles.AllowV, out SemVersion _latest_release_version)) 56 | { 57 | return _latest_release_version; 58 | } 59 | return SemVersion.ParsedFrom(0, 0, 0, release.TagName ?? "No Tag"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Remote Server/Updates/Updates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Semver; 5 | using System.Reflection; 6 | using System.Threading.Tasks; 7 | using Octokit; 8 | 9 | namespace ASCOM.Remote 10 | { 11 | internal class Updates 12 | { 13 | #region Internal properties 14 | 15 | /// 16 | /// True if a newer release verison is available 17 | /// 18 | internal static bool HasNewerRelease { get; set; } 19 | 20 | /// 21 | /// Latest release name 22 | /// 23 | internal static string LatestReleaseName { get; set; } = ""; 24 | /// 25 | /// Latest release version 26 | /// 27 | internal static string LatestReleaseVersion { get; set; } = ""; 28 | 29 | /// 30 | /// Download URL for the latest release version 31 | /// 32 | internal static string ReleaseUrl { get; set; } 33 | 34 | /// 35 | /// True if a new preview version is available 36 | /// 37 | internal static bool HasNewerPreview { get; set; } 38 | 39 | /// 40 | /// Latest preview version 41 | /// 42 | internal static string LatestPreviewName { get; set; } = ""; 43 | 44 | /// 45 | /// Latest preview version 46 | /// 47 | internal static string LatestPreviewVersion { get; set; } = ""; 48 | 49 | /// 50 | /// Download URL for the latest preview version 51 | /// 52 | internal static string PreviewURL { get; set; } = ""; 53 | 54 | /// 55 | /// True if the client is running the latest release version 56 | /// 57 | internal static bool UpToDate { get; set; } 58 | 59 | /// 60 | /// True if the client has a version that is ahead of the latest preview release 61 | /// 62 | internal static bool AheadOfPreview { get; set; } = false; 63 | 64 | /// 65 | /// True if the client has a version that is ahead of the latest main release 66 | /// 67 | internal static bool AheadOfRelease { get; set; } = false; 68 | 69 | /// 70 | /// True if some releases have been retrieved from GitHub 71 | /// 72 | internal static bool HasReleases { get => Releases.Count > 0; } 73 | 74 | /// 75 | /// List of releases 76 | /// 77 | internal static IReadOnlyList Releases { get; set; } = new List(); //null; 78 | 79 | internal static string AscomRemoteVersion => $"{Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion}"; 80 | 81 | internal static string AscomRemoteVersionDisplayString 82 | { 83 | get 84 | { 85 | string informationalVersion = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; 86 | string shortGitId = $"{Assembly.GetEntryAssembly().GetCustomAttribute().Value}"; 87 | 88 | SemVersion.TryParse(informationalVersion, SemVersionStyles.AllowV, out SemVersion semver); 89 | if (semver is not null) 90 | return $"{semver.Major}.{semver.Minor}.{semver.Patch}{(semver.Prerelease == "" ? "" : "-")}{semver.Prerelease} (Build {shortGitId})"; 91 | else 92 | return $" Bad informational version string: ##{informationalVersion}##"; 93 | } 94 | } 95 | #endregion 96 | 97 | #region Internal methods 98 | 99 | internal static async Task> GetReleases() 100 | { 101 | try 102 | { 103 | LogDebug("GetReleases", "Getting release details"); 104 | Releases = await GitHubReleases.GetReleases("ASCOMInitiative", "ASCOMRemote"); 105 | SetProperties(); 106 | LogDebug("GetReleases", $"Found {Releases.Count} releases"); 107 | 108 | foreach (Octokit.Release release in Releases) 109 | { 110 | LogDebug("CheckForUpdatesSync", $"Found release: {release.Name}, ReleaseSemVersionFromTag: {release.ReleaseSemVersionFromTag()}, Published on: {release.PublishedAt.GetValueOrDefault()}, Major: {release.ReleaseSemVersionFromTag().Major}, Minor: {release.ReleaseSemVersionFromTag().Minor}, Patch: {release.ReleaseSemVersionFromTag().Patch}, Pre-release: {release.Prerelease}"); 111 | } 112 | 113 | return Releases; 114 | } 115 | catch (Exception ex) 116 | { 117 | LogDebug("GetReleases", $"Exception: {ex}"); 118 | throw; 119 | } 120 | } 121 | 122 | internal async static Task CheckForUpdates() 123 | { 124 | try 125 | { 126 | LogDebug("CheckForUpdates", "Getting release details"); 127 | Releases = await Task.Run(() => { return GitHubReleases.GetReleases("ASCOMInitiative", "ASCOMRemote"); }); 128 | SetProperties(); 129 | LogDebug("CheckForUpdates", $"Found {Releases.Count} releases"); 130 | 131 | foreach (Octokit.Release release in Releases) 132 | { 133 | LogDebug("CheckForUpdates", $"Found release: {release.Name}, ReleaseSemVersionFromTag: {release.ReleaseSemVersionFromTag()}, Published on: {release.PublishedAt.GetValueOrDefault()}, Major: {release.ReleaseSemVersionFromTag().Major}, Minor: {release.ReleaseSemVersionFromTag().Minor}, Patch: {release.ReleaseSemVersionFromTag().Patch}, Pre-release: {release.Prerelease}"); 134 | } 135 | } 136 | catch (Exception ex) 137 | { 138 | LogDebug("CheckForUpdates", $"Exception: {ex}"); 139 | throw; 140 | } 141 | } 142 | 143 | internal static bool UpdateAvailable() 144 | { 145 | try 146 | { 147 | if (Releases != null) 148 | { 149 | if (Releases.Count > 0) 150 | { 151 | if (SemVersion.TryParse(Updates.AscomRemoteVersion, SemVersionStyles.AllowV, out SemVersion currentversion)) 152 | { 153 | LogDebug("UpdateAvailable", $"Application semver - Major: {currentversion.Major}, Minor: {currentversion.Minor}, Patch: {currentversion.Patch}, Pre-release: {currentversion.Prerelease}, Metadata: {currentversion.Metadata}"); 154 | Octokit.Release Release = Releases?.Latest(); 155 | 156 | if (Release != null) 157 | { 158 | if (SemVersion.TryParse(Release.TagName, SemVersionStyles.AllowV, out SemVersion latestrelease)) 159 | { 160 | LogDebug("UpdateAvailable", $"Found release semver - Major: {latestrelease.Major}, Minor: {latestrelease.Minor}, Patch: {latestrelease.Patch}, Pre-release: {latestrelease.Prerelease}, Metadata: {latestrelease.Metadata}"); 161 | return SemVersion.ComparePrecedence(currentversion, latestrelease) == -1; 162 | } 163 | } 164 | } 165 | else 166 | { 167 | throw new InvalidValueException($"The informational product version set in the project file is not a valid SEMVER string: {Updates.AscomRemoteVersion}"); 168 | } 169 | } 170 | } 171 | } 172 | catch (Exception ex) 173 | { 174 | LogDebug("UpdateAvailable", $"Exception: {ex}"); 175 | } 176 | return false; 177 | } 178 | 179 | #endregion 180 | 181 | #region Support code 182 | /// 183 | /// Set properties according to the releases returned 184 | /// 185 | /// ConfgormLogger instance to record operational messages 186 | private static void SetProperties() 187 | { 188 | try 189 | { 190 | LogDebug("SetProperties", $"Running..."); 191 | if (SemVersion.TryParse(Updates.AscomRemoteVersion, SemVersionStyles.AllowV, out SemVersion installedVersion)) 192 | { 193 | LogDebug("SetProperties", $"Installed version: {installedVersion}"); 194 | 195 | Release latestRelease = Updates.Releases?.LatestRelease(); 196 | Release latestPreRelease = Updates.Releases?.LatestPrerelease(); 197 | if ((latestRelease is not null) & (latestPreRelease is not null)) 198 | { 199 | 200 | bool latesOk = SemVersion.TryParse(latestRelease.TagName, SemVersionStyles.AllowV, out SemVersion latestVersion); 201 | 202 | bool latestPreOk = SemVersion.TryParse(latestPreRelease.TagName, SemVersionStyles.AllowV, out SemVersion latestPreReleaseVersion); 203 | 204 | LogDebug("SetProperties", $"latestrelease: {latestVersion}, latestprerelease: {latestPreReleaseVersion}"); 205 | 206 | if ((SemVersion.ComparePrecedence(installedVersion, latestVersion) == 0) || (SemVersion.ComparePrecedence(installedVersion, latestPreReleaseVersion) == 0)) 207 | { 208 | UpToDate = true; 209 | } 210 | 211 | if (latestVersion != null) 212 | { 213 | if (SemVersion.ComparePrecedence(installedVersion, latestVersion) == -1) //(installedRelease < latestrelease) 214 | { 215 | HasNewerRelease = true; 216 | LatestReleaseVersion = latestRelease.TagName; 217 | LatestReleaseName = latestRelease.Name; 218 | ReleaseUrl = latestRelease.HtmlUrl; 219 | } 220 | 221 | if (SemVersion.ComparePrecedence(installedVersion, latestVersion) == 1) //(installedRelease > latestrelease) 222 | { 223 | LogDebug("SetProperties", $"Setting AheadOfRelease True"); 224 | AheadOfRelease = true; 225 | } 226 | } 227 | else 228 | { 229 | latestVersion = new SemVersion(0); 230 | } 231 | 232 | if (latestPreReleaseVersion != null) 233 | { 234 | LogDebug("SetProperties", $"(SemVersion.ComparePrecedence(installedVersion, latestPreReleaseVersion) == -1) && (SemVersion.ComparePrecedence(latestVersion, latestPreReleaseVersion) == -1): {(SemVersion.ComparePrecedence(installedVersion, latestPreReleaseVersion) == -1) && (SemVersion.ComparePrecedence(latestVersion, latestPreReleaseVersion) == -1)}"); 235 | if ((SemVersion.ComparePrecedence(installedVersion, latestPreReleaseVersion) == -1) && (SemVersion.ComparePrecedence(latestVersion, latestPreReleaseVersion) == -1)) //installedVersion < latestPreReleaseVersion && latestVersion < latestPreReleaseVersion 236 | { 237 | HasNewerPreview = true; 238 | LatestPreviewVersion = latestPreRelease.TagName; 239 | LatestPreviewName = latestPreRelease.Name; 240 | PreviewURL = latestPreRelease.HtmlUrl; 241 | } 242 | 243 | LogDebug("SetProperties", $"(SemVersion.ComparePrecedence(installedVersion, latestPreReleaseVersion) == -1) && (SemVersion.ComparePrecedence(latestVersion, latestPreReleaseVersion) == 1): {(SemVersion.ComparePrecedence(installedVersion, latestPreReleaseVersion) == 1) && (SemVersion.ComparePrecedence(latestVersion, latestPreReleaseVersion) == -1)}"); 244 | if ((SemVersion.ComparePrecedence(installedVersion, latestPreReleaseVersion) == 1) && (SemVersion.ComparePrecedence(latestVersion, latestPreReleaseVersion) == -1)) //(installedVersion > latestPreReleaseVersion && latestVersion < latestPreReleaseVersion) 245 | { 246 | AheadOfPreview = true; 247 | } 248 | } 249 | LogDebug("SetProperties", $"UpToDate: {UpToDate}, HasNewerRelease: {HasNewerRelease}, HasNewerPreview: {HasNewerPreview}, AheadOfPreview: {AheadOfPreview}, LatestVersion: {LatestReleaseVersion}, URL: {ReleaseUrl}, LatestPreviewVersion: {LatestPreviewVersion}, PreviewURL: {PreviewURL}"); 250 | } 251 | } 252 | else 253 | { 254 | LogDebug("SetProperties", $"Failed to parse {Updates.AscomRemoteVersion}"); 255 | } 256 | } 257 | catch (Exception ex) 258 | { 259 | LogDebug("SetProperties", $"Exception: {ex}"); 260 | } 261 | } 262 | 263 | static void LogDebug(string prefix, string message) 264 | { 265 | if (ServerForm.DebugTraceState) 266 | ServerForm.LogMessage(0, 0, 0, $"* {prefix}", message); 267 | } 268 | 269 | #endregion 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /Remote Server/ascomicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Remote Server/ascomicon.ico -------------------------------------------------------------------------------- /Remote Server/servedDevice.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ASCOM.Remote 2 | { 3 | partial class ServedDevice 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | cmbDeviceType = new System.Windows.Forms.ComboBox(); 32 | txtDeviceNumber = new System.Windows.Forms.TextBox(); 33 | cmbDevice = new System.Windows.Forms.ComboBox(); 34 | chkAllowSetConnectedTrue = new System.Windows.Forms.CheckBox(); 35 | chkAllowSetConnectedFalse = new System.Windows.Forms.CheckBox(); 36 | btnSetup = new System.Windows.Forms.Button(); 37 | ChkAllowConcurrentAccess = new System.Windows.Forms.CheckBox(); 38 | SuspendLayout(); 39 | // 40 | // cmbDeviceType 41 | // 42 | cmbDeviceType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 43 | cmbDeviceType.FormattingEnabled = true; 44 | cmbDeviceType.Location = new System.Drawing.Point(0, 1); 45 | cmbDeviceType.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 46 | cmbDeviceType.Name = "cmbDeviceType"; 47 | cmbDeviceType.Size = new System.Drawing.Size(192, 23); 48 | cmbDeviceType.TabIndex = 0; 49 | cmbDeviceType.SelectedIndexChanged += CmbDeviceType_SelectedIndexChanged; 50 | // 51 | // txtDeviceNumber 52 | // 53 | txtDeviceNumber.BackColor = System.Drawing.SystemColors.Window; 54 | txtDeviceNumber.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 55 | txtDeviceNumber.Location = new System.Drawing.Point(233, 1); 56 | txtDeviceNumber.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 57 | txtDeviceNumber.Name = "txtDeviceNumber"; 58 | txtDeviceNumber.ReadOnly = true; 59 | txtDeviceNumber.Size = new System.Drawing.Size(28, 23); 60 | txtDeviceNumber.TabIndex = 1; 61 | txtDeviceNumber.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; 62 | // 63 | // cmbDevice 64 | // 65 | cmbDevice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 66 | cmbDevice.FormattingEnabled = true; 67 | cmbDevice.Location = new System.Drawing.Point(338, 1); 68 | cmbDevice.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 69 | cmbDevice.Name = "cmbDevice"; 70 | cmbDevice.Size = new System.Drawing.Size(345, 23); 71 | cmbDevice.TabIndex = 3; 72 | cmbDevice.SelectedIndexChanged += CmbDevice_SelectedIndexChanged; 73 | // 74 | // chkAllowSetConnectedTrue 75 | // 76 | chkAllowSetConnectedTrue.AutoSize = true; 77 | chkAllowSetConnectedTrue.Location = new System.Drawing.Point(985, 5); 78 | chkAllowSetConnectedTrue.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 79 | chkAllowSetConnectedTrue.Name = "chkAllowSetConnectedTrue"; 80 | chkAllowSetConnectedTrue.Size = new System.Drawing.Size(15, 14); 81 | chkAllowSetConnectedTrue.TabIndex = 5; 82 | chkAllowSetConnectedTrue.UseVisualStyleBackColor = true; 83 | // 84 | // chkAllowSetConnectedFalse 85 | // 86 | chkAllowSetConnectedFalse.AutoSize = true; 87 | chkAllowSetConnectedFalse.Location = new System.Drawing.Point(915, 5); 88 | chkAllowSetConnectedFalse.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 89 | chkAllowSetConnectedFalse.Name = "chkAllowSetConnectedFalse"; 90 | chkAllowSetConnectedFalse.Size = new System.Drawing.Size(15, 14); 91 | chkAllowSetConnectedFalse.TabIndex = 4; 92 | chkAllowSetConnectedFalse.UseVisualStyleBackColor = true; 93 | // 94 | // btnSetup 95 | // 96 | btnSetup.Location = new System.Drawing.Point(751, 1); 97 | btnSetup.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 98 | btnSetup.Name = "btnSetup"; 99 | btnSetup.Size = new System.Drawing.Size(88, 22); 100 | btnSetup.TabIndex = 6; 101 | btnSetup.Text = "Setup"; 102 | btnSetup.UseVisualStyleBackColor = true; 103 | btnSetup.Click += BtnSetup_Click; 104 | // 105 | // ChkAllowConcurrentAccess 106 | // 107 | ChkAllowConcurrentAccess.AutoSize = true; 108 | ChkAllowConcurrentAccess.Location = new System.Drawing.Point(1083, 5); 109 | ChkAllowConcurrentAccess.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 110 | ChkAllowConcurrentAccess.Name = "ChkAllowConcurrentAccess"; 111 | ChkAllowConcurrentAccess.Size = new System.Drawing.Size(15, 14); 112 | ChkAllowConcurrentAccess.TabIndex = 7; 113 | ChkAllowConcurrentAccess.UseVisualStyleBackColor = true; 114 | // 115 | // ServedDevice 116 | // 117 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 118 | AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 119 | Controls.Add(ChkAllowConcurrentAccess); 120 | Controls.Add(btnSetup); 121 | Controls.Add(chkAllowSetConnectedTrue); 122 | Controls.Add(chkAllowSetConnectedFalse); 123 | Controls.Add(cmbDevice); 124 | Controls.Add(txtDeviceNumber); 125 | Controls.Add(cmbDeviceType); 126 | Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 127 | Name = "ServedDevice"; 128 | Size = new System.Drawing.Size(1142, 25); 129 | ResumeLayout(false); 130 | PerformLayout(); 131 | } 132 | 133 | #endregion 134 | 135 | private System.Windows.Forms.ComboBox cmbDeviceType; 136 | private System.Windows.Forms.TextBox txtDeviceNumber; 137 | private System.Windows.Forms.ComboBox cmbDevice; 138 | private System.Windows.Forms.CheckBox chkAllowSetConnectedTrue; 139 | private System.Windows.Forms.CheckBox chkAllowSetConnectedFalse; 140 | private System.Windows.Forms.Button btnSetup; 141 | private System.Windows.Forms.CheckBox ChkAllowConcurrentAccess; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Remote Server/servedDevice.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | text/microsoft-resx 50 | 51 | 52 | 2.0 53 | 54 | 55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 56 | 57 | 58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 59 | 60 | -------------------------------------------------------------------------------- /SetNetworkPermissions/ASCOM.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/SetNetworkPermissions/ASCOM.ico -------------------------------------------------------------------------------- /SetNetworkPermissions/Options.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | 3 | namespace ASCOM.Remote 4 | { 5 | /// 6 | /// Specify command line options that can be supplied to this executable through the CommandLine reference 7 | /// 8 | class Options 9 | { 10 | [Option('l', Program.SET_LOCAL_SERVER_PATH_COMMAND_NAME, Required = false, Default = Program.NOT_PRESENT_FLAG, HelpText = "Path to the local server executable")] 11 | public string LocalServerPath { get; set; } 12 | 13 | [Option('a', Program.ENABLE_API_URI_COMMAND_NAME, Required = false, Default = Program.NOT_PRESENT_FLAG, HelpText = "Set the API URI ACL")] 14 | public string SetApiUriAcl { get; set; } 15 | 16 | [Option('m', Program.ENABLE_REMOTE_SERVER_MANAGEMENT_URI_COMMAND_NAME, Required = false, Default = Program.NOT_PRESENT_FLAG, HelpText = "Set the Remote Server management URI ACL")] 17 | public string SetRemoteServerManagementUriAcl { get; set; } 18 | 19 | [Option('p', Program.ENABLE_ALPACA_DEVICE_MANAGEMENT_URI_COMMAND_NAME, Required = false, Default = Program.NOT_PRESENT_FLAG, HelpText = "Set the Alpaca device management URI ACL")] 20 | public string SetAlpacaManagementUriAcl { get; set; } 21 | 22 | [Option('s', Program.ENABLE_ALPACA_SETUP_URI_COMMAND_NAME, Required = false, Default = Program.NOT_PRESENT_FLAG, HelpText = "Set the Alpaca HTML setup URI ACL")] 23 | public string SetAlpacaSetupUriAcl { get; set; } 24 | 25 | [Option('h', Program.ENABLE_HTTP_DOT_SYS_PORT_COMMAND_NAME, Required = false, Default = Program.NOT_PRESENT_FLAG, HelpText = "Enable the specified firewall port on which HTTP.SYS will listen")] 26 | public string HttpDotSysPort { get; set; } 27 | 28 | [Option('h', Program.USER_NAME_COMMAND_NAME, Required = false, Default = Program.NOT_PRESENT_FLAG, HelpText = "Set the user name for which the HTTP.SYS permission will be enabled")] 29 | public string UserName { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /SetNetworkPermissions/SetNetworkPermissions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | ASCOM.Remote 5 | ASCOM.SetNetworkPermissions 6 | net8.0-windows 7 | true 8 | Set Network Permissions 9 | SetNetworkPemissions 10 | Copyright © 2024 ASCOM Initiative 11 | bin\$(Configuration)\ 12 | AnyCPU;x86;x64 13 | 14 | 15 | embedded 16 | 17 | 18 | embedded 19 | 20 | 21 | embedded 22 | 23 | 24 | embedded 25 | false 26 | 27 | 28 | embedded 29 | false 30 | 31 | 32 | embedded 33 | false 34 | 35 | 36 | app.manifest 37 | 38 | 39 | ASCOM.ico 40 | True 41 | ..\Remote Server Key.snk 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /SetNetworkPermissions/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SetNetworkPermissions/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 59 | 60 | 61 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Setup/ASCOM Remote Setup.iss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Setup/ASCOM Remote Setup.iss -------------------------------------------------------------------------------- /Setup/ASCOM.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Setup/ASCOM.ico -------------------------------------------------------------------------------- /Setup/ASCOMLogo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Setup/ASCOMLogo.bmp -------------------------------------------------------------------------------- /Setup/NewWizardImage.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Setup/NewWizardImage.bmp -------------------------------------------------------------------------------- /SignASCOMRemote.cmd: -------------------------------------------------------------------------------- 1 | @echo on 2 | call "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" -startdir=none -arch=x64 -host_arch=x64 3 | 4 | "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool" sign /a /fd SHA256 /n "Peter Simpson" /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 %1 5 | 6 | 7 | 8 | 9 | rem @echo on 10 | rem @echo Setting up variables 11 | rem call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 12 | rem @echo " " 13 | rem @echo Parameter: %1 14 | 15 | rem echo "%1" | findstr /C:"uninst" 1>nul 16 | 17 | rem We only need to sign these executables once, so do this when the uninstaller is created. 18 | rem if errorlevel 1 ( 19 | rem echo Signing main installer - Not signing other executables 20 | rem ) ELSE ( 21 | rem echo Signing uninstaller - Signing other exectutables too. 22 | 23 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\Remote Server\bin\Debug\ASCOM.RemoteServer.exe" 24 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\Remote Server\bin\Debug\ASCOM.Common.dll" 25 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Debug\ASCOM.SetNetworkPermissions.exe" 26 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Debug\WindowsFirewallHelper.dll" 27 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Debug\CommandLine.dll" 28 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Debug\NetStandard.dll" 29 | 30 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\Remote Server\bin\Debug\ASCOM.RemoteServer.exe" 31 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\Remote Server\bin\Debug\ASCOM.Common.dll" 32 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Debug\ASCOM.SetNetworkPermissions.exe" 33 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Debug\WindowsFirewallHelper.dll" 34 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Debug\CommandLine.dll" 35 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Debug\NetStandard.dll" 36 | 37 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\Remote Server\bin\Release\ASCOM.RemoteServer.exe" 38 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\Remote Server\bin\Release\ASCOM.Common.dll" 39 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Release\ASCOM.SetNetworkPermissions.exe" 40 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Release\WindowsFirewallHelper.dll" 41 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Release\CommandLine.dll" 42 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Release\NetStandard.dll" 43 | 44 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\Remote Server\bin\Release\ASCOM.RemoteServer.exe" 45 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\Remote Server\bin\Release\ASCOM.Common.dll" 46 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Release\ASCOM.SetNetworkPermissions.exe" 47 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Release\WindowsFirewallHelper.dll" 48 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Release\CommandLine.dll" 49 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 "J:\ASCOMRemote\SetNetworkPermissions\bin\Release\NetStandard.dll" 50 | 51 | ) 52 | 53 | rem Wait for 1 second to allow Inno to release a file handle, which prevents signtool from working correctly. 54 | rem timeout 1 55 | 56 | rem Sign the installer or uninstaller whose filename was supplied as the first parameter 57 | rem echo signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 %1 58 | rem signtool sign /a /fd SHA256 /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 %1 59 | 60 | rem pause 61 | -------------------------------------------------------------------------------- /Swagger/bugt300square.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Swagger/bugt300square.jpg -------------------------------------------------------------------------------- /Swagger/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Swagger/favicon-16x16.png -------------------------------------------------------------------------------- /Swagger/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASCOMInitiative/ASCOMRemote/e4d60019d54692a0dafd788895d1dde6a778a28e/Swagger/favicon-32x32.png -------------------------------------------------------------------------------- /Swagger/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ASCOM Alpaca API 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 84 | 85 | 86 | 87 |
88 | 89 | 90 | 91 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /Swagger/oauth2-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 68 | -------------------------------------------------------------------------------- /Swagger/redoclyconfig.yaml: -------------------------------------------------------------------------------- 1 | extends: 2 | - recommended 3 | 4 | rules: 5 | operation-operationId: off 6 | no-empty-servers: off 7 | -------------------------------------------------------------------------------- /redocly.yaml: -------------------------------------------------------------------------------- 1 | extends: 2 | - recommended 3 | rules: 4 | security-defined: off 5 | operation-operationId: off 6 | info-license: off 7 | --------------------------------------------------------------------------------