diff --git a/.gitignore b/.gitignore index fd3093b..6431763 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ *.user *.userosscache *.sln.docstates +*.ncrunchsolution +gitversion.json + # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -244,4 +247,3 @@ ModelManifest.xml # FAKE - F# Make .fake/ -*.ncrunchsolution diff --git a/ASCOM.K8056.Switch.Specifications/ASCOM.K8056.Switch.Specifications.DotSettings b/ASCOM.K8056.Switch.Specifications/ASCOM.K8056.Switch.Specifications.DotSettings deleted file mode 100644 index e7d45a2..0000000 --- a/ASCOM.K8056.Switch.Specifications/ASCOM.K8056.Switch.Specifications.DotSettings +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md index 72f2101..d078f4c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,106 +1,96 @@ -Arduino Power Controller with ASCOM Switch Driver -================================================= +# Arduino Power Controller with ASCOM Switch Driver ![Arduino Power Controller][image] -This driver and its source code and the accompanying Arduino Sketch are licensed under the [Tigra MIT license][license]. Essentially, anyone can do anything at all with this software, including commercially, without restriction. Anything you do is your responsibility and not mine. +This driver and its source code and the accompanying Arduino Sketch are licensed under the [Tigra MIT license][license]. Essentially, anyone can do anything at all with this software, including commercially, without restriction. Anything you do is your responsibility and not the author's. -For further information, please visit the [Project Home Page][project] or the [BitBucket Source Repository][source]. +If you find my work useful, please consider [sponsoring me][sponsor]. -Project Description -------------------- +For further information, please visit the [Project Home Page][project] or the [Git Source Repository][source]. -This project is designed to enable easy control of power to astronomical instruments and ancillary -equipment in a robotic observatory. The module can be controlled via the ASCOM Switch interface -from any planetarium or observatory automation software that supports it. Alternatively, the driver -can be used directly from a PowerShell script or any Windows scripting engine that can load a COM object. +## Project Description -We have tested the unit while switching 12 volt power supplies. The limitations on voltage and -current will depend on the relay module used. Commonly available modules have relays rated for -up to 10 Amps and up to 250 volts AC. However, switching mains voltages and/or high current loads -can be hazardous. A simple mistake can result in destruction of equipment, fire, electrocution -and death. ***Therefore we cannot endorse the use of this technology for switching high voltage or high -current loads***. We suggest using the module only to switch low voltage DC power where the consequences -of a mistake are more manageable. +This project serves a dual purpose. It is a fully functional turnkey solution designed to enable easy control of power to astronomical instruments and ancillary equipment in an observatory. At the same time, it can form a learning platform and a jumping-off point for other new drivers. The Switch code can be gradually replaced with new code and if done with care, it is possible to keep a working build at all times. In the past, driver development began with a mostly empty template project which didn't offer much guidance on how to get things done or demonstrate good architectural choices. The author discovered, after developing over a dozen ASCOM drivers, that it was actually easier to start from an already fully functional project and add new code from there. -The project uses cheap, commonly available modules to create a computer controlled power switch. -We used an Arduino UNO R3, available for as little as £5, and an 8-port relay module obtained -from eBay, also costing around £5. The only other item required is a 10-way Dupont style Male-to-Female -jumper cable to connect up the relay module. A 5 volt Arduino UNO should be capable of driving -the relays directly from the USB power. +To begin work on your own driver, we recommend forking this project on GitHub, building it on your own system and then striking out towards your new driver. -Note that the Arduino is not capable of powering your equipment directly and it should never be -directly connected to the power supply being switched. The output side of the relays must remain completely isolated from the Arduino and the USB connection. The observatory equipment itself should be powered using a separate power supply designed for the purpose. +The project consists of a an ASCOM Switch driver, plus an Arduino sketch. The two parts of the project communicate via the USB interface, which acts as a virtual COM port. The module can be controlled via the ASCOM Switch interface from any planetarium or observatory automation software that supports the ASCOM `ISwitch` interface. Alternatively, the driver can be used directly from a PowerShell script or any Windows scripting engine that can load a COM object. -Software Architecture ---------------------- +This serves as a base implementation that can be extended to build other drivers and to understand the operation of out-of-process (COM LocalServer) drivers. -For developers, this project demonstrates some advanced techniques for developing ASCOM drivers. At Tigra Astronomy, we use techniques that differ significantly from the methods described in the ASCOM documentation. We have our own reasons for this and this is not to say that our techniques are better or more correct, but they do demonstrate some alternative practices. +### Cautions and Limitations + +We have tested the unit while switching 12 volt power supplies. The limitations on voltage and current will depend on the relay module used. Commonly available modules have relays rated for up to 10 Amps and up to 250 volts AC. However, switching mains voltages and/or high current loads can be hazardous. A simple mistake can result in destruction of equipment, fire, electrocution and death. **_Therefore we cannot endorse the use of this technology for switching high voltage or high current loads_**. We suggest using the module only to switch low voltage (less than 25 volts) and low current (less than 1 Amp) DC power where the consequences of a mistake are more manageable. If higher voltages and currents need to be switched, then the use of contactors and/or solid state relays (SSRs) should be investigated. The author has used this technique quite successfully to control 12v accessories for over 2 years at the time of writing. + +The project uses cheap, commonly available modules to create a computer controlled power switch. We used an Arduino UNO R3, available for as little as £5, and an 8-port relay module obtained from eBay, also costing around £5. The only other item required is a 10-way Dupont style Male-to-Female jumper cable to connect up the relay module. A 5 volt Arduino UNO should be capable of driving the relays directly from the USB power, but a 3.3 volt board would probably need additional level shifting buffers. + +Note that the Arduino is not capable of powering your equipment directly and it should never be directly connected to the power supply being switched. The output side of the relays must remain completely isolated from the Arduino and the USB connection. The observatory equipment itself should be powered using a separate power supply designed for the purpose. + +You must accept personal responsibility if you choose to use this project. This is a non-negotiable condition of the license agreement and the author(s) cannot be held liable for anything that happens as a result of you using the project. + +## Software Architecture + +For developers, this project demonstrates some practices for developing ASCOM out-of-process (COM LocalServer) drivers and can be used as a template project to create new drivers. The driver has been developed using modern object-oriented techniques. While this may take a little effort to understand initially, the techniques used can be very powerful and save you a lot of time in the long run, so it is worth persevering. You can always post questions to the [ASCOM-Talk Developers Group][ascom-dev] if you are stuck on any of the implementation details. People there are only too willing to help. In particular: -- **Reactive ASCOM** - the project demonstrates our Reactive Communications library in use in a real driver. -- **WiX Installer** - demonstrates how to perform a declarative no-code install of an ASCOM LocalServer driver, without running any of the code being installed during the installation. Normally, registration must be performed by running the LocalServer application with the /register option. Our install does this declaratively by directly creating the required registry keys. Our installers use WiX - Windows Installer XML, which is free and open source (it was actually Microsoft's very first open source product). The pros and cons of our technique vs. the ASCOM standard technique are complex and this is not the place to have that discussion, but we've used our techniques successfully in at least 6 production drivers. -- **Aspect Oriented Programming** We use a product called PostSharp, which is an Aspect Oriented Programming framework and tool chain. PostSharp is not free but it will work in unlicensed mode for a project with a small number of properties and methods, and most ASCOM drivers fit that description nicely. PostSharp allows us to factor out a lot of 'boilerplate' code and replace it with attributes. Some of the attributes you'll notice in this project are: - - `[MustBeConnected]` used in the ASCOM Switch driver on methods that require that the comms channel is active. This is done in such a way that the check is only performed once in the case of nested calls. - - `[NLogTraceWithArguments]` emits diagnostics output on entry and exit from driver methods, including information about passed parameter values and return values, all without writing any code. - - `[NotifyPropertyChanged]` used in the device layer to implement the `INotifyPropertyChanged` interface automatically. - - `[ReaderWriterSynchronized]` used in the `ClientConnectionManager` class within the LocalServer project to create a thread safe Reader/Writer lock around the list of connected clients. - - -- **Modified LocalServer** Our LocalServer implementation has a few interesting customizations. - - **Reduced Attack Surface Area** that uses the Reflection-only load context when searching for assemblies with ASCOM drivers. The original LocalServer loads every assembly it finds into the execution context whilst running with elevated permissions. That comes with a high risk since malicious code could very easily be dropped into the folder. We minimize this attack vector by examining all assemblies in the Reflection-only context, so that no malicious code could execute while the server has elevated permissions.At other times we only load the assemblies containing ASCOM drivers. Note: there is no known incident of a LocalServer being used as an attack vector, but once we had identified the possibility we felt that we had to address it. - - **Status Display GUI** that shows the status of each client connection and also updates annunciators to show the state of the hardware. This also provides easy access to the Setup Dialog. +- **Reactive ASCOM** - the project demonstrates a transactional thread-safe approach to handling device communications, using the [Reactive Communications for ASCOM][rx-ascom] library. Command/Response sequencing and thread safety can be a real challenge to get right and there are many subtle pitfalls to be avoided. Using a transactional approach helps to ensure correct sequencing of commands and responses, while the Reactive Extensions for .NET ensure thread safety and provide an event-driven programming model. ASCOM drivers are essentially real-time systems and this model is a good fit for that. For information and learning resources on the [Reactive Communications for ASCOM][rx-ascom] library, please visit the [project home page][rx-ascom]. +- **WiX Installer** - demonstrates how to perform a declarative no-code install of an ASCOM LocalServer driver, without running any of the code being installed during the installation. Normally, registration must be performed by running the LocalServer application with the `/register` option. Our install does this declaratively by directly creating the required registry keys. Our installers use WiX - Windows Installer XML, which is free and open source (it was actually Microsoft's very first open source project, written by people on the Windows Installer team). +- **Aspect Oriented Programming** We use a product called PostSharp, which is an Aspect Oriented Programming framework and tool chain. PostSharp Essentials is free for a project with a relatively small number of classes, and most ASCOM drivers fit that description nicely. PostSharp allows us to factor out a lot of 'boilerplate' code and replace it with attributes. Some of the attributes you'll notice in this project are: + - `[MustBeConnected]` used in the ASCOM Switch driver on methods that require that the comms channel is active. This is done in such a way that the check is only performed once in the case of nested calls. + - `[NLogTraceWithArguments]` emits diagnostics output on entry and exit from driver methods, including information about passed parameter values and return values, all without writing any code. + - `[NotifyPropertyChanged]` used in the device layer to implement the `INotifyPropertyChanged` interface automatically. + - `[ReaderWriterSynchronized]` used in the `ClientConnectionManager` class within the LocalServer project to create a thread safe Reader/Writer lock around the list of connected clients. -Installation ------------- + The `Aspects` project contains a ReadMe file with more details. -Download » +- **LocalServer with Integral Driver Classes** A recent development with the LocalServer pattern was to incorporate driver classes directly into the LocalServer project. Previously, drivers were implemented as separate projects and dynamically loaded by the LocalServer process. However, it was realized that this had a number of disadvantages and promoted poor design. Driver classes are now incorporated directly into the LocalServer project and _should contain the bare minimum code necessary to compile and load_. -The installer can be downloaded from [BitBucket][download]. The installer has a minimal user interface but is fully functional. Upgrades can be installed 'on top of' existing versions and we expect that settings will be preserved in that situation. +- **Status Display User Interface** that shows the status of each client connection and also updates annunciators to show the state of the hardware. This also provides easy access to the Setup Dialog. Thread safety is again ensured using the Reactive Extensions for .NET. -The installer has two versions, one for 32-bit (x86) computers and another for 64-bit (x64) computers. The installer checks that it is running on the right type of system and will not allow installation to proceed if it finds a mismatch. This allows our ASCOM drivers to function correctly in all situations and they are compatible with operating systems and applications of all varieties. +## Installation -End users should install the `Release` version of the driver. `Debug` versions are for diagnostic use and will have much worse performance than the release build. + -Driver Configuration --------------------- +The installer can be downloaded from [GitHub][download]. The installer has a minimal user interface but is fully functional. Upgrades can be installed 'on top of' existing versions and we expect that settings will be preserved in that situation. -The driver currently allows configuration only of the Comm Port Name (typically "COM1") via the Setup Dialog. Other parameters may be configurable but there is no user interface. They can be edited using the *ASCOM Profile Explorer* utility. +The installer has two versions, one for 32-bit (x86) computers and another for 64-bit (x64) computers. The installer checks that it is running on the right type of system and will not allow installation to proceed if it finds a mismatch. This allows our ASCOM drivers to function correctly in all situations and they are compatible with operating systems and applications of all varieties. +## Driver Configuration -Diagnostics ------------ +The driver currently allows configuration only of the Comm Port Name (typically "COM1") via the Setup Dialog. Other parameters may be configurable but there is no user interface. They can be edited using the _ASCOM Profile Explorer_ utility. -The driver emits diagnostic information using NLog. It is configured by default to emit informational messages to the Trace channel and errors & warnings to the log file. Live output can be viewed with a utility such as SysInternals' DebugView, or Binary Fortress' Log Fusion. +## Diagnostics -Alternatively, the NLog configuration file `NLog.dll.nlog` and `NLog.config` can be edited to emit logging information to any NLog target, including a file or database. See the [NLog wiki][nlog] for information on configuring NLog. +The driver emits diagnostic information using NLog. It is configured by default to emit informational messages to the Trace channel and errors & warnings to the log file. Live output can be viewed with a utility such as SysInternals' DebugView, or Binary Fortress' Log Fusion. -Feeback and Bugs ----------------- +Alternatively, the NLog configuration file `NLog.config` can be edited to emit logging information to any NLog target, including a file or database. See the [NLog wiki][nlog] for information on configuring NLog. Note that NLog supports _Semantic Logging_, to services such as Seq and SeriLog, and this capability is well worth exploring. +## Feeback and Bugs -There is a bug tracker at the [BitBucket source repository][source]. Please submit any bug reports and feature requests there. +There is a bug tracker at the [GitHub source repository][source]. Please submit any bug reports and feature requests there. It would be most helpful if you could quote the full version number of the driver when reporting any issues. -Get Involved ------------- +## Get Involved -There are probably many ways in which this driver could be improved. If you would like to contribute, then we would be delighted to accept your pull request at our [BitBucket source repository][source]. Any source code that you contribute will be covered by the original [Tigra MIT License][license] and once merged, contributions are irrevocable. +There are probably many ways in which this driver could be improved. If you would like to contribute, then we would be delighted to accept your pull request at our [GitHub repository][source]. Any source code that you contribute will be covered by the original [Tigra MIT License][license] and once merged, contributions are irrevocable. If you want to get involved but are not sure what to do, please check the [bug tracker][source] to see if there are any outstanding issues or feature requests that you could work on. -Buy Me a Cup of Coffee ----------------------- +## Sponsor My Work -Software development is powered by coffee! If you've found this driver useful, or you're just feeling benevolent, then you might consider [buying me a cup of coffee][coffee] (or several cups) using the link at the bottom of my web site. Thank you! +If you find my work useful, please consider [sponsoring me on GitHub][sponsor]. -August 2016, Tim Long +March 2022, Tim Long -[license]: https://tigra.mit-license.org/ "Tigra Astronomy Open Source License" -[project]: http://tigra-astronomy.com/oss/arduino-power-controller "Project Home Page at Tigra Astronomy" -[source]: https://bitbucket.org/account/user/tigra-astronomy/projects/OPC "BitBucket Git Source Control" -[download]: https://bitbucket.org/tigra-astronomy/ta.arduinopowercontroller-ascom-switch-driver/downloads/ "Download the installer" -[tigra]: http://tigra-astronomy.com "Tigra Astronomy Web Site" -[nlog]: https://github.com/nlog/nlog/wiki/Configuration-file#targets "NLog Targets" -[coffee]: http://tigra-astronomy.com/#coffee "Buy me a cup of coffee" -[image]: http://tigra-astronomy.com/Media/TigraAstronomy/site-images/arduino-power-controller/hardware.png "Arduino Power Controller" \ No newline at end of file +[coffee]: # "Buy me a cup of coffee" +[download]: https://github.com/Tigra-Astronomy/TA.ArduinoPowerController.AscomServer/releases/latest "Download the installer" +[image]: images/hardware.png "Arduino Power Controller" +[license]: https://tigra.mit-license.org/ "Tigra Astronomy Open Source License" +[nlog]: https://github.com/nlog/nlog/wiki/Configuration-file#targets "NLog Targets" +[project]: https://github.com/Tigra-Astronomy/TA.ArduinoPowerController.AscomServer "Project Home Page" +[source]: https://github.com/Tigra-Astronomy/TA.ArduinoPowerController.AscomServer "Git Source Repository" +[tigra]: # "Tigra Astronomy Web Site" +[ascom-dev]: https://ascomtalk.groups.io/g/Developer "ASCOM-Talk/Developer discussion group" +[rx-ascom]: https://github.com/Tigra-Astronomy/TA.ReactiveCommunications "Project home page: Reactive Communications for ASCOM" +[sponsor]: https://github.com/sponsors/NameOfTheDragon "sponsor me on GitHub" \ No newline at end of file diff --git a/TA.ArduinoPowerController.DeviceInterface/ArduinoSwitchTransaction.cs b/TA.ArduinoPowerController.DeviceInterface/ArduinoSwitchTransaction.cs deleted file mode 100644 index e8d724d..0000000 --- a/TA.ArduinoPowerController.DeviceInterface/ArduinoSwitchTransaction.cs +++ /dev/null @@ -1,21 +0,0 @@ -// This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: ArduinoSwitchTransaction.cs Last modified: 2017-03-16@23:33 by Tim Long - -using System; -using PostSharp.Patterns.Contracts; -using TA.Ascom.ReactiveCommunications.Transactions; - -namespace TA.ArduinoPowerController.DeviceInterface - { - internal abstract class ArduinoSwitchTransaction : TerminatedStringTransaction - { - protected ArduinoSwitchTransaction([Required] string command) : base(command) - { - Timeout = TimeSpan.FromSeconds(2); - } - } - } \ No newline at end of file diff --git a/TA.ArduinoPowerController.DeviceInterface/CommunicationsStackBuilder.cs b/TA.ArduinoPowerController.DeviceInterface/CommunicationsStackBuilder.cs deleted file mode 100644 index 9e95b9d..0000000 --- a/TA.ArduinoPowerController.DeviceInterface/CommunicationsStackBuilder.cs +++ /dev/null @@ -1,40 +0,0 @@ -// This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: CommunicationsStackBuilder.cs Last modified: 2017-03-16@23:33 by Tim Long - -using System; -using TA.Ascom.ReactiveCommunications; - -namespace TA.ArduinoPowerController.DeviceInterface - { - /// - /// Factory methods for creating various parts of the communications stack. - /// - public static class CommunicationsStackBuilder - { - public static ICommunicationChannel BuildChannel(DeviceEndpoint endpoint) - { - if (endpoint is SerialDeviceEndpoint) - return new SerialCommunicationChannel(endpoint); - throw new NotSupportedException($"There is no supported channel type for the endpoint: {endpoint}") - { - Data = {["endpoint"] = endpoint} - }; - } - - public static TransactionObserver BuildTransactionObserver(ICommunicationChannel channel) - { - return new TransactionObserver(channel); - } - - public static ITransactionProcessor BuildTransactionProcessor(TransactionObserver observer) - { - var processor = new ReactiveTransactionProcessor(); - processor.SubscribeTransactionObserver(observer); - return processor; - } - } - } \ No newline at end of file diff --git a/TA.ArduinoPowerController.DeviceInterface/DeviceController.cs b/TA.ArduinoPowerController.DeviceInterface/DeviceController.cs index a5edfe5..aeff3f3 100644 --- a/TA.ArduinoPowerController.DeviceInterface/DeviceController.cs +++ b/TA.ArduinoPowerController.DeviceInterface/DeviceController.cs @@ -1,63 +1,64 @@ // This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: DeviceController.cs Last modified: 2017-03-16@23:33 by Tim Long +// +// Copyright © 2016-2019 Tigra Astronomy, all rights reserved. +// Licensed under the Tigra MIT license, see http://tigra.mit-license.org/ +// +// File: DeviceController.cs Last modified: 2019-09-08@08:36 by Tim Long using System; -using NLog; using TA.Ascom.ReactiveCommunications; +using TA.Utils.Core.Diagnostics; namespace TA.ArduinoPowerController.DeviceInterface - { +{ public class DeviceController : IDisposable - { + { private readonly ITransactionProcessorFactory factory; - private readonly Logger log = LogManager.GetCurrentClassLogger(); + private readonly ILog log; private bool disposed; private ITransactionProcessor transactionProcessor; - public DeviceController(ITransactionProcessorFactory factory) - { + public DeviceController(ITransactionProcessorFactory factory, ILog logService) + { this.factory = factory; - } + log = logService; + } public bool IsOnline => transactionProcessor != null && (factory?.Channel?.IsOpen ?? false); public void Dispose() - { + { Dispose(true); GC.SuppressFinalize(this); - } + } public void ClearRelay(ushort id) - { + { var transaction = new WriteRelayTransaction(id, false); transactionProcessor.CommitTransaction(transaction); transaction.WaitForCompletionOrTimeout(); RaiseRelayStateChanged(id, false); - } + } /// /// Close the connection to the AWR system. This should never fail. /// public void Close() - { + { log.Warn("Close requested"); if (!IsOnline) - { + { log.Warn("Ignoring Close request because already closed"); return; - } + } log.Info($"Closing device endpoint: {factory.Endpoint}"); factory.DestroyTransactionProcessor(); log.Info("====== Channel closed: the device is now disconnected ======"); - } + } protected virtual void Dispose(bool fromUserCode) - { + { if (!disposed) if (fromUserCode) Close(); @@ -65,51 +66,50 @@ protected virtual void Dispose(bool fromUserCode) // ToDo: Call the base class's Dispose(Boolean) method, if available. // base.Dispose(fromUserCode); - } + } // The IDisposable pattern, as described at // http://www.codeproject.com/Articles/15360/Implementing-IDisposable-and-the-Dispose-Pattern-P - /// /// Finalizes this instance (called prior to garbage collection by the CLR) /// ~DeviceController() - { + { Dispose(false); - } + } /// /// Opens the transaction pipeline for sending and receiving and performs initial state synchronization with the drive /// system. /// public void Open() - { + { log.Info($"Opening device endpoint: {factory.Endpoint}"); transactionProcessor = factory.CreateTransactionProcessor(); log.Info("====== Initialization completed successfully : Device is now ready to accept commands ======"); - } - + } public void PerformOnConnectTasks() - { + { //ToDo: perform any tasks that must occur as soon as the communication channel is connected. - } + } protected void RaiseRelayStateChanged(int relay, bool newState) - { + { var args = new RelayStateChangedEventArgs(relay, newState); RelayStateChanged?.Invoke(this, args); - } + } public event EventHandler RelayStateChanged; public void SetRelay(ushort id) - { + { var transaction = new WriteRelayTransaction(id, true); transactionProcessor.CommitTransaction(transaction); transaction.WaitForCompletionOrTimeout(); - RaiseRelayStateChanged(id, true); - } + if (transaction.Successful) + RaiseRelayStateChanged(id, true); } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/TA.ArduinoPowerController.DeviceInterface/Properties/AssemblyInfo.cs b/TA.ArduinoPowerController.DeviceInterface/Properties/AssemblyInfo.cs index 6b98fb8..909187b 100644 --- a/TA.ArduinoPowerController.DeviceInterface/Properties/AssemblyInfo.cs +++ b/TA.ArduinoPowerController.DeviceInterface/Properties/AssemblyInfo.cs @@ -8,4 +8,4 @@ using System.Reflection; [assembly: AssemblyTitle("TA.ArduinoPowerController.DeviceInterface")] -[assembly: AssemblyDescription("Low level device communications layer")] \ No newline at end of file +[assembly: AssemblyDescription("Low level device communications layer for Arduino Power Controller")] \ No newline at end of file diff --git a/TA.ArduinoPowerController.DeviceInterface/ReactiveTransactionProcessorFactory.cs b/TA.ArduinoPowerController.DeviceInterface/ReactiveTransactionProcessorFactory.cs index 670f325..b1a2a06 100644 --- a/TA.ArduinoPowerController.DeviceInterface/ReactiveTransactionProcessorFactory.cs +++ b/TA.ArduinoPowerController.DeviceInterface/ReactiveTransactionProcessorFactory.cs @@ -1,29 +1,33 @@ // This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: ReactiveTransactionProcessorFactory.cs Last modified: 2017-03-16@23:33 by Tim Long +// +// Copyright © 2016-2019 Tigra Astronomy, all rights reserved. +// Licensed under the Tigra MIT license, see http://tigra.mit-license.org/ +// +// File: ReactiveTransactionProcessorFactory.cs Last modified: 2019-09-08@06:37 by Tim Long using System; -using System.Threading; +using System.Threading.Tasks; using TA.Ascom.ReactiveCommunications; namespace TA.ArduinoPowerController.DeviceInterface - { +{ + /// + /// Class ReactiveTransactionProcessorFactory. Used to set up and tear down the communications stack + /// as the device is connected and disconnected. + /// Implements + /// + /// public class ReactiveTransactionProcessorFactory : ITransactionProcessorFactory - { - public string ConnectionString { get; } - + { private TransactionObserver observer; private ReactiveTransactionProcessor processor; public ReactiveTransactionProcessorFactory(string connectionString) - { - this.ConnectionString = connectionString; - // Endpoint will be InvalidEndpoint if the connection string is invalid. - Endpoint = DeviceEndpoint.FromConnectionString(connectionString); - } + { + ConnectionString = connectionString; + } + + public string ConnectionString { get; } public ICommunicationChannel Channel { get; private set; } @@ -33,17 +37,16 @@ public ReactiveTransactionProcessorFactory(string connectionString) /// /// ITransactionProcessor. public ITransactionProcessor CreateTransactionProcessor() - { - Endpoint = DeviceEndpoint.FromConnectionString(ConnectionString); - Channel = CommunicationsStackBuilder.BuildChannel(Endpoint); + { + Channel = new ChannelFactory().FromConnectionString(ConnectionString); observer = new TransactionObserver(Channel); processor = new ReactiveTransactionProcessor(); processor.SubscribeTransactionObserver(observer, TimeSpan.FromMilliseconds(100)); Channel.Open(); - //Task.Delay(TimeSpan.FromSeconds(2)).Wait(); // Arduino needs 2 seconds to initialize - Thread.Sleep(TimeSpan.FromSeconds(3)); + // Arduino may need a few seconds to initialize after starting the connection + Task.Delay(TimeSpan.FromSeconds(5)).Wait(); return processor; - } + } /// /// Destroys the transaction processor and its dependencies. Ensures that the @@ -53,7 +56,7 @@ public ITransactionProcessor CreateTransactionProcessor() /// again. /// public void DestroyTransactionProcessor() - { + { processor?.Dispose(); processor = null; // [Sentinel] observer = null; @@ -62,8 +65,8 @@ public void DestroyTransactionProcessor() Channel?.Dispose(); Channel = null; // [Sentinel] GC.Collect(3, GCCollectionMode.Forced, blocking: true); - } - - public DeviceEndpoint Endpoint { get; set; } } - } \ No newline at end of file + + public DeviceEndpoint Endpoint => Channel?.Endpoint ?? new InvalidEndpoint(); + } +} \ No newline at end of file diff --git a/TA.ArduinoPowerController.DeviceInterface/ReadMe.md b/TA.ArduinoPowerController.DeviceInterface/ReadMe.md new file mode 100644 index 0000000..e9e390c --- /dev/null +++ b/TA.ArduinoPowerController.DeviceInterface/ReadMe.md @@ -0,0 +1,5 @@ +# Device Interface project + +It is best practice to place the device communications and interfacing code in its own separate project. One reason for doing so is so that this "business logic" code can be unit tested in isolation. + +This will become particularly important in projects that contain more than one driver class. The driver classes can then share the common device interface code. \ No newline at end of file diff --git a/TA.ArduinoPowerController.DeviceInterface/RelayCommand.cs b/TA.ArduinoPowerController.DeviceInterface/RelayCommand.cs new file mode 100644 index 0000000..dcd4066 --- /dev/null +++ b/TA.ArduinoPowerController.DeviceInterface/RelayCommand.cs @@ -0,0 +1,26 @@ +// This file is part of the TA.ArduinoPowerController project +// +// Copyright © 2016-2019 Tigra Astronomy, all rights reserved. +// Licensed under the Tigra MIT license, see http://tigra.mit-license.org/ +// +// File: RelayCommand.cs Last modified: 2019-09-08@12:35 by Tim Long +namespace TA.ArduinoPowerController.DeviceInterface +{ + /// + /// A simple data transfer object (DTO) for storing relay operations + /// + class RelayCommand + { + public string Operation { get; set; } + + public ushort Relay { get; set; } + + public bool State { get; set; } + + /// Debugging is always easier if there is a ToString method. + public override string ToString() + { + return $"{nameof(Operation)}: {Operation}, {nameof(Relay)}: {Relay}, {nameof(State)}: {State}"; + } + } +} \ No newline at end of file diff --git a/TA.ArduinoPowerController.DeviceInterface/TA.ArduinoPowerController.DeviceInterface.csproj b/TA.ArduinoPowerController.DeviceInterface/TA.ArduinoPowerController.DeviceInterface.csproj index a844599..c6a8ef1 100644 --- a/TA.ArduinoPowerController.DeviceInterface/TA.ArduinoPowerController.DeviceInterface.csproj +++ b/TA.ArduinoPowerController.DeviceInterface/TA.ArduinoPowerController.DeviceInterface.csproj @@ -9,17 +9,17 @@ Properties TA.ArduinoPowerController.DeviceInterface TA.ArduinoPowerController.DeviceInterface - v4.6.2 + v4.8 512 - True + true full false - ..\BuildOutput\ + ..\BuildOutput\Debug\ DEBUG;TRACE prompt 4 @@ -28,75 +28,74 @@ pdbonly true - ..\BuildOutput\ + ..\BuildOutput\Release\ TRACE prompt 4 - - ..\packages\JetBrains.Annotations.10.4.0\lib\net\JetBrains.Annotations.dll - - - ..\packages\NLog.4.4.5\lib\net45\NLog.dll - - - ..\packages\PostSharp.4.3.31\lib\net35-client\PostSharp.dll - True - - - ..\packages\PostSharp.Patterns.Common.4.3.31\lib\net45\PostSharp.Patterns.Common.dll - - - ..\packages\PostSharp.Patterns.Model.4.3.31\lib\net40\PostSharp.Patterns.Model.dll - + - - ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll - - - ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll - - - ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll - - - ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll - + + + + + + - - ..\packages\TA.Ascom.ReactiveCommunications.0.4.2\lib\net45\TA.Ascom.ReactiveCommunications.dll - + - - Properties\GlobalAssemblyInfo.cs - - - + - + + Dependency Validation.layerdiagram + False + - + + + + 5.9.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + 2021.3.0 + + + 6.10.8 + + + 6.10.8 + + + 1.4.0 + + + 1.3.0 + + + - - - - + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Server/NLog.Release.config b/TA.ArduinoPowerController.Server/NLog.Release.config deleted file mode 100644 index 20fcb34..0000000 --- a/TA.ArduinoPowerController.Server/NLog.Release.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Server/NLog.config b/TA.ArduinoPowerController.Server/NLog.config index 8e5b941..67e9f85 100644 --- a/TA.ArduinoPowerController.Server/NLog.config +++ b/TA.ArduinoPowerController.Server/NLog.config @@ -3,19 +3,21 @@ - - + - + - - + + \ No newline at end of file diff --git a/TA.ArduinoPowerController.Server/NLog.xsd b/TA.ArduinoPowerController.Server/NLog.xsd deleted file mode 100644 index 4aa20b2..0000000 --- a/TA.ArduinoPowerController.Server/NLog.xsd +++ /dev/null @@ -1,2966 +0,0 @@ - - - - - - - - - - - - - - - Watch config file for changes and reload automatically. - - - - - Print internal NLog messages to the console. Default value is: false - - - - - Print internal NLog messages to the console error output. Default value is: false - - - - - Write internal NLog messages to the specified file. - - - - - Log level threshold for internal log messages. Default value is: Info. - - - - - Global log level threshold for application log messages. Messages below this level won't be logged.. - - - - - Throw an exception when there is an internal error. Default value is: false. - - - - - Throw an exception when there is a configuration error. If not set, determined by throwExceptions. - - - - - Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false. - - - - - Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false. - - - - - Write timestamps for internal NLog messages. Default value is: true. - - - - - Use InvariantCulture as default culture instead of CurrentCulture. Default value is: false. - - - - - - - - - - - - - - Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). - - - - - - - - - - - - - - - - - Prefix for targets/layout renderers/filters/conditions loaded from this assembly. - - - - - Load NLog extensions from the specified file (*.dll) - - - - - Load NLog extensions from the specified assembly. Assembly name should be fully qualified. - - - - - - - - - - Name of the logger. May include '*' character which acts like a wildcard. Allowed forms are: *, Name, *Name, Name* and *Name* - - - - - Comma separated list of levels that this rule matches. - - - - - Minimum level that this rule matches. - - - - - Maximum level that this rule matches. - - - - - Level that this rule matches. - - - - - Comma separated list of target names. - - - - - Ignore further rules if this one matches. - - - - - Enable or disable logging rule. Disabled rules are ignored. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file. - - - - - Ignore any errors in the include file. - - - - - - - Variable name. - - - - - Variable value. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Number of log events that should be processed in a batch by the lazy writer thread. - - - - - Limit of full s to write before yielding into Performance is better when writing many small batches, than writing a single large batch - - - - - Action to be taken when the lazy writer thread request queue count exceeds the set limit. - - - - - Limit on the number of requests in the lazy writer thread request queue. - - - - - Time in milliseconds to sleep between batches. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Condition expression. Log events who meet this condition will cause a flush on the wrapped target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Number of log events to be buffered. - - - - - Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. - - - - - Indicates whether to use sliding timeout. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Encoding to be used. - - - - - Instance of that is used to format log messages. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Maximum current connections. 0 = no maximum. - - - - - Indicates whether to keep connection open whenever possible. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - Network address. - - - - - Maximum queue size. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include source info (file name and line number) in the information sent over the network. - - - - - Indicates whether to include NLog-specific extensions to log4j schema. - - - - - Indicates whether to include stack contents. - - - - - Indicates whether to include call site (class and method name) in the information sent over the network. - - - - - AppInfo field. By default it's the friendly name of the current AppDomain. - - - - - NDC item separator. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - Layout that should be use to calcuate the value for the parameter. - - - - - Viewer parameter name. - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether to use default row highlighting rules. - - - - - Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) - - - - - The encoding for writing messages to the . - - - - - Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Condition that must be met in order to set the specified foreground and background color. - - - - - Background color. - - - - - Foreground color. - - - - - - - - - - - - - - - - Indicates whether to ignore case when comparing texts. - - - - - Regular expression to be matched. You must specify either text or regex. - - - - - Text to be matched. You must specify either text or regex. - - - - - Indicates whether to match whole words only. - - - - - Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. - - - - - Background color. - - - - - Foreground color. - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether to send the log messages to the standard error instead of the standard output. - - - - - Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) - - - - - The encoding for writing messages to the . - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. - - - - - Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. - - - - - Name of the database provider. - - - - - Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. - - - - - Indicates whether to keep the database connection open between the log events. - - - - - Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. - - - - - Name of the connection string (as specified in <connectionStrings> configuration section. - - - - - Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. - - - - - Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. - - - - - Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Text of the SQL command to be run on each log level. - - - - - Type of the SQL command to be run on each log level. - - - - - - - - - - - - - - - - - - - - - - - Type of the command. - - - - - Connection string to run the command against. If not provided, connection string from the target is used. - - - - - Indicates whether to ignore failures. - - - - - Command text. - - - - - - - - - - - - - - Layout that should be use to calcuate the value for the parameter. - - - - - Database parameter name. - - - - - Database parameter precision. - - - - - Database parameter scale. - - - - - Database parameter size. - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Layout that renders event Category. - - - - - Layout that renders event ID. - - - - - Name of the Event Log to write to. This can be System, Application or any user-defined name. - - - - - Name of the machine on which Event Log service is running. - - - - - Value to be used as the event Source. - - - - - Action to take if the message is larger than the option. - - - - - Optional entrytype. When not set, or when not convertable to then determined by - - - - - Message length limit to write to the Event Log. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Indicates whether to return to the first target after any successful write. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - File encoding. - - - - - Line ending mode. - - - - - Way file archives are numbered. - - - - - Name of the file to be used for an archive. - - - - - Indicates whether to automatically archive log files every time the specified time passes. - - - - - Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: - - - - - Indicates whether to compress archive files into the zip archive format. - - - - - Maximum number of archive files that should be kept. - - - - - Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. - - - - - Is the an absolute or relative path? - - - - - Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. - - - - - Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write - - - - - Is the an absolute or relative path? - - - - - Value indicationg whether file creation calls should be synchronized by a system global mutex. - - - - - Indicates whether the footer should be written only when the file is archived. - - - - - Name of the file to write to. - - - - - Value specifying the date format to use when archiving files. - - - - - Indicates whether to archive old log file on startup. - - - - - Indicates whether to create directories if they do not exist. - - - - - Indicates whether to enable log file(s) to be deleted. - - - - - File attributes (Windows only). - - - - - Indicates whether to delete old log file on startup. - - - - - Indicates whether to replace file contents on each write instead of appending log message at the end. - - - - - Indicates whether concurrent writes to the log file by multiple processes on the same host. - - - - - Indicates whether to keep log file open instead of opening and closing it on each logging event. - - - - - Maximum number of log filenames that should be stored as existing. - - - - - Indicates whether concurrent writes to the log file by multiple processes on different network hosts. - - - - - Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). - - - - - Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Log file buffer size in bytes. - - - - - Indicates whether to automatically flush the file buffers after each log message. - - - - - Delay in milliseconds to wait before attempting to write to the file again. - - - - - Number of times the write is appended on the file before NLog discards the log message. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Condition expression. Log events who meet this condition will be forwarded to the wrapped target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Windows domain name to change context to. - - - - - Required impersonation level. - - - - - Type of the logon provider. - - - - - Logon Type. - - - - - User account password. - - - - - Indicates whether to revert to the credentials of the process instead of impersonating another user. - - - - - Username to change context to. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Interval in which messages will be written up to the number of messages. - - - - - Maximum allowed number of messages written per . - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Endpoint address. - - - - - Name of the endpoint configuration in WCF configuration file. - - - - - Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) - - - - - Client ID. - - - - - Indicates whether to include per-event properties in the payload sent to the server. - - - - - Indicates whether to use binary message encoding. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - Layout that should be use to calculate the value for the parameter. - - - - - Name of the parameter. - - - - - Type of the parameter. - - - - - Type of the parameter. Obsolete alias for - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether to send message as HTML instead of plain text. - - - - - Encoding to be used for sending e-mail. - - - - - Indicates whether to add new lines between log entries. - - - - - CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - Mail message body (repeated for each log message send in one mail). - - - - - Mail subject. - - - - - Sender's email address (e.g. joe@domain.com). - - - - - Indicates the SMTP client timeout. - - - - - Priority used for sending mails. - - - - - Indicates whether NewLine characters in the body should be replaced with tags. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - SMTP Server to be used for sending. - - - - - SMTP Authentication mode. - - - - - Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). - - - - - Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). - - - - - Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. - - - - - Port number that SMTP Server is listening on. - - - - - Indicates whether the default Settings from System.Net.MailSettings should be used. - - - - - Folder where applications save mail messages to be processed by the local SMTP server. - - - - - Specifies how outgoing email messages will be handled. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Encoding to be used when writing text to the queue. - - - - - Indicates whether to use the XML format when serializing message. This will also disable creating queues. - - - - - Indicates whether to check if a queue exists before writing to it. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Indicates whether to create the queue if it doesn't exists. - - - - - Label to associate with each message. - - - - - Name of the queue to write to. - - - - - Indicates whether to use recoverable messages (with guaranteed delivery). - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Class name. - - - - - Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Encoding to be used. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Network address. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - Indicates whether to keep connection open whenever possible. - - - - - Maximum current connections. 0 = no maximum. - - - - - Maximum queue size. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Encoding to be used. - - - - - Instance of that is used to format log messages. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Maximum current connections. 0 = no maximum. - - - - - Indicates whether to keep connection open whenever possible. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - Network address. - - - - - Maximum queue size. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include source info (file name and line number) in the information sent over the network. - - - - - Indicates whether to include NLog-specific extensions to log4j schema. - - - - - Indicates whether to include stack contents. - - - - - Indicates whether to include call site (class and method name) in the information sent over the network. - - - - - AppInfo field. By default it's the friendly name of the current AppDomain. - - - - - NDC item separator. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Indicates whether to perform layout calculation. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Indicates whether performance counter should be automatically created. - - - - - Name of the performance counter category. - - - - - Counter help text. - - - - - Name of the performance counter. - - - - - Performance counter type. - - - - - The value by which to increment the counter. - - - - - Performance counter instance name. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Default filter to be applied when no specific rule matches. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - Condition to be tested. - - - - - Resulting filter to be applied when the condition matches. - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Number of times to repeat each log message. - - - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Number of retries that should be attempted on the wrapped target in case of a failure. - - - - - Time to wait between retries in milliseconds. - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Encoding. - - - - - Value whether escaping be done according to the old NLog style (Very non-standard) - - - - - Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) - - - - - Web service method name. Only used with Soap. - - - - - Web service namespace. Only used with Soap. - - - - - Protocol to be used when calling web service. - - - - - Web service URL. - - - - - Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see and ). - - - - - (optional) root namespace of the XML document, if POST of XML document chosen. (see and ). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Footer layout. - - - - - Header layout. - - - - - Body layout (can be repeated multiple times). - - - - - Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). - - - - - Column delimiter. - - - - - Quote Character. - - - - - Quoting mode. - - - - - Indicates whether CVS should include header. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Layout of the column. - - - - - Name of the column. - - - - - - - - - - - - - - - - List of property names to exclude when is true - - - - - Option to include all properties from the log events - - - - - Option to render the empty object value {} - - - - - Option to suppress the extra spaces in the output json - - - - - - - - - - - - - - Determines wether or not this attribute will be Json encoded. - - - - - Layout that will be rendered as the attribute's value. - - - - - Name of the attribute. - - - - - - - - - - - - - - Footer layout. - - - - - Header layout. - - - - - Body layout (can be repeated multiple times). - - - - - - - - - - - - - - - - - - - - - Layout text. - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Condition expression. - - - - - - - - - - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - Substring to be matched. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - String to compare the layout to. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - Substring to be matched. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - String to compare the layout to. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Server/Properties/AssemblyInfo.cs b/TA.ArduinoPowerController.Server/Properties/AssemblyInfo.cs index e2651f7..93e2aa6 100644 --- a/TA.ArduinoPowerController.Server/Properties/AssemblyInfo.cs +++ b/TA.ArduinoPowerController.Server/Properties/AssemblyInfo.cs @@ -1,11 +1,11 @@ // This file is part of the TA.ArduinoPowerController project -// +// // Copyright © 2016-2017 Tigra Astronomy, all rights reserved. // Licensed under the MIT license, see http://tigra.mit-license.org/ -// +// // File: AssemblyInfo.cs Last modified: 2017-03-16@23:33 by Tim Long using System.Reflection; [assembly: AssemblyTitle("TA.ArduinoPowerController.Server")] -[assembly: AssemblyDescription("ASCOM multi-interface server for TA.ArduinoPowerController.Server")] \ No newline at end of file +[assembly: AssemblyDescription("ASCOM multi-instance server for Arduino Power Controller")] \ No newline at end of file diff --git a/TA.ArduinoPowerController.Server/Properties/Resources.Designer.cs b/TA.ArduinoPowerController.Server/Properties/Resources.Designer.cs index 9e8fa8f..599f0ef 100644 --- a/TA.ArduinoPowerController.Server/Properties/Resources.Designer.cs +++ b/TA.ArduinoPowerController.Server/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace TA.ArduinoPowerController.Server.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/TA.ArduinoPowerController.Server/Properties/Settings.Designer.cs b/TA.ArduinoPowerController.Server/Properties/Settings.Designer.cs index 10c223e..4bcbc03 100644 --- a/TA.ArduinoPowerController.Server/Properties/Settings.Designer.cs +++ b/TA.ArduinoPowerController.Server/Properties/Settings.Designer.cs @@ -12,8 +12,8 @@ namespace TA.ArduinoPowerController.Server.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.0.1.0")] - public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.1.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -81,5 +81,17 @@ public string ConnectionString { this["SwitchNames"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("8")] + public int NumberOfSwitches { + get { + return ((int)(this["NumberOfSwitches"])); + } + set { + this["NumberOfSwitches"] = value; + } + } } } diff --git a/TA.ArduinoPowerController.Server/Properties/Settings.settings b/TA.ArduinoPowerController.Server/Properties/Settings.settings index 84e8632..ae5a1e4 100644 --- a/TA.ArduinoPowerController.Server/Properties/Settings.settings +++ b/TA.ArduinoPowerController.Server/Properties/Settings.settings @@ -17,5 +17,8 @@ + + 8 + \ No newline at end of file diff --git a/TA.ArduinoPowerController.Server/ReadMe.md b/TA.ArduinoPowerController.Server/ReadMe.md deleted file mode 100644 index 0ce311f..0000000 --- a/TA.ArduinoPowerController.Server/ReadMe.md +++ /dev/null @@ -1,21 +0,0 @@ -ASCOM Platform Components via NuGet -=================================== - -This repository contains a copy of the compiled binaries taken from the ASCOM Developer Components installer -together with a `.nuspec` file for creating a NuGet package. - -This package can be easily installed when developing ASCOM drivers or applications and allows development -to proceed without having to install the ASCOM Platform. - -This is probably of most use when using a build server, where it is burdensome to have to install ASCOM -on each build agent. - -No originality is claimed, this is simply an alternative mechanism for distributing the binaries from the ASCOM Platform. - -This is a derivative work and the binaries are redistributed with permission, under the Creative Commons license of the ASCOM Platform. - -All other content is Copyright © 2016 Tigra Astronomy, all rights reserved. You may use this content under the terms of [Tigra MIT License](http://tigra.mit-license.org "MIT License for Tigra Astronomy"). - -Tim Long, -Tigra Astronomy [http://tigra-astronomy.com](http://tigra-astronomy.com "Tigra Astronomy Web Site") -June 2016 \ No newline at end of file diff --git a/TA.ArduinoPowerController.Server/ServedComClassLocator.cs b/TA.ArduinoPowerController.Server/ServedComClassLocator.cs deleted file mode 100644 index f741dc0..0000000 --- a/TA.ArduinoPowerController.Server/ServedComClassLocator.cs +++ /dev/null @@ -1,144 +0,0 @@ -// This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: ServedComClassLocator.cs Last modified: 2017-03-16@23:34 by Tim Long - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using ASCOM; -using NLog; - -namespace TA.ArduinoPowerController.Server - { - internal class ServedComClassLocator : MarshalByRefObject - { - private readonly ILogger Log = LogManager.GetCurrentClassLogger(); - - public ServedComClassLocator() - { - DiscoveredAssemblyNames = new List(); - DiscoveredTypes = new List(); - } - - /// - /// Gets the list of the names of assemblies that contain ASCOM drivers to be served by - /// the LocalServer. - /// - /// The discovered assembly full names. - public List DiscoveredAssemblyNames { get; } - - /// - /// Gets the discovered types that were found to be decorated with the - /// attribute. - /// - /// A list of the discovered types. - public List DiscoveredTypes { get; } - - /// - /// Discovers types (and their declaring assemblies) that are decorated with the - /// , which identifies an ASCOM driver that should be served - /// by the LocalServer. - /// - /// - /// Assemblies are loaded using to prevent any code execution. - /// This would otherwise be a potential malware attack vector, since untrusted assemblies would be loaded and - /// potentially into - /// a privileged user context. If any of the loaded assemblies must later execute, then this operation should be - /// performed - /// in an isolated application domain so that the domain and all the loaded assemblies can later be unloaded. Unloaing - /// an entire - /// AppDomain is the only way to remove assemblies from memory. - /// The class inherits from specifically so that it can be proxied across an - /// App Domain boundary. - /// - /// - public void DiscoverServedClasses() - { - Log.Info("Loading served COM classes"); - - // put everything into one folder, the same as the server. - var assyPath = Assembly.GetExecutingAssembly().Location; - assyPath = Path.GetDirectoryName(assyPath); - Log.Debug($"Assembly load path is {assyPath}"); - - var d = new DirectoryInfo(assyPath); - var fileInfos = d.GetFiles("*.dll"); - Log.Debug($"Discovered {fileInfos.Length} candidate assemblies"); - try - { - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += HandleReflectionOnlyAssemblyResolve; - foreach (var fi in fileInfos) - { - Log.Trace($"Examining types in {fi.Name}"); - var aPath = fi.FullName; - try - { - Log.Trace($"Attempting reflection only load for {aPath}"); - var so = Assembly.ReflectionOnlyLoad(Path.GetFileNameWithoutExtension(fi.Name)); - var soShortName = so.GetName().Name; - var types = so.GetTypes(); - Log.Trace($"Reflection found {types.Length} types in assembly {so.FullName}"); - var servedClasses = from type in types.AsParallel() - let memberInfo = (MemberInfo) type - let safeAttributes = CustomAttributeData.GetCustomAttributes(memberInfo) - where safeAttributes.Any( - p => p.AttributeType.Name == nameof(ServedClassNameAttribute)) - select type; - var discoveredTypes = servedClasses.ToList(); - if (discoveredTypes.Any()) - { - DiscoveredTypes.AddRange(discoveredTypes); - DiscoveredAssemblyNames.Add(so.FullName); - Log.Warn($"Discovered {discoveredTypes.Count} served clases in assembly {soShortName}"); - } - } - catch (BadImageFormatException ex) - { - Log.Warn(ex, $"BadImageFormat: {fi.Name}.{fi.Extension} continuing"); - // Probably an attempt to load a Win32 DLL (i.e. not a .net assembly) - // Just swallow the exception and continue to the next item. - } - catch (Exception ex) - { - Log.Error(ex, $"Unexpected error processing {fi.Name}: {ex.Message}"); - //return false; - } - } - } - finally - { - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= HandleReflectionOnlyAssemblyResolve; - } - } - - - /// - /// Handles the reflection only assembly resolve. - /// - /// The sender. - /// The instance containing the event data. - /// Assembly. - private Assembly HandleReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args) - { - try - { - Log.Info( - $"Event: ReflectionOnlyResolveAssembly for {args.Name} requested by {args.RequestingAssembly.GetName().Name}", - args.Name); - var resolved = Assembly.ReflectionOnlyLoad(args.Name); - Log.Info($"Successfully resolved assembly with {resolved.FullName}"); - return resolved; - } - catch (FileLoadException ex) - { - Log.Error(ex, $"Failed to resolve assembly: {args.Name}"); - return null; // Let the app raise its own error. - } - } - } - } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Server/ServerStatusDisplay.Designer.cs b/TA.ArduinoPowerController.Server/ServerStatusDisplay.Designer.cs index c8d239d..d52089b 100644 --- a/TA.ArduinoPowerController.Server/ServerStatusDisplay.Designer.cs +++ b/TA.ArduinoPowerController.Server/ServerStatusDisplay.Designer.cs @@ -1,3 +1,5 @@ +using TA.WinFormsControls; + namespace TA.ArduinoPowerController.Server { partial class ServerStatusDisplay @@ -32,23 +34,22 @@ private void InitializeComponent() this.registeredClientCount = new System.Windows.Forms.Label(); this.OnlineClients = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); - this.ClientStatus = new System.Windows.Forms.ListBox(); - this.annunciatorPanel1 = new ASCOM.Controls.AnnunciatorPanel(); - this.Relay0Annunciator = new ASCOM.Controls.Annunciator(); - this.Relay1Annunciator = new ASCOM.Controls.Annunciator(); - this.Relay2Annunciator = new ASCOM.Controls.Annunciator(); - this.Relay3Annunciator = new ASCOM.Controls.Annunciator(); - this.Relay4Annunciator = new ASCOM.Controls.Annunciator(); - this.Relay5Annunciator = new ASCOM.Controls.Annunciator(); - this.Relay6Annunciator = new ASCOM.Controls.Annunciator(); - this.Relay7Annunciator = new ASCOM.Controls.Annunciator(); + this.annunciatorPanel1 = new AnnunciatorPanel(); + this.Relay0Annunciator = new Annunciator(); + this.Relay1Annunciator = new Annunciator(); + this.Relay2Annunciator = new Annunciator(); + this.Relay3Annunciator = new Annunciator(); + this.Relay4Annunciator = new Annunciator(); + this.Relay5Annunciator = new Annunciator(); + this.Relay6Annunciator = new Annunciator(); + this.Relay7Annunciator = new Annunciator(); this.SetupCommand = new System.Windows.Forms.Button(); this.annunciatorPanel1.SuspendLayout(); this.SuspendLayout(); // // label1 // - this.label1.Location = new System.Drawing.Point(12, 10); + this.label1.Location = new System.Drawing.Point(12, 38); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(104, 21); this.label1.TabIndex = 0; @@ -57,7 +58,7 @@ private void InitializeComponent() // registeredClientCount // this.registeredClientCount.AutoSize = true; - this.registeredClientCount.Location = new System.Drawing.Point(122, 10); + this.registeredClientCount.Location = new System.Drawing.Point(122, 38); this.registeredClientCount.Name = "registeredClientCount"; this.registeredClientCount.Size = new System.Drawing.Size(13, 13); this.registeredClientCount.TabIndex = 1; @@ -66,7 +67,7 @@ private void InitializeComponent() // OnlineClients // this.OnlineClients.AutoSize = true; - this.OnlineClients.Location = new System.Drawing.Point(226, 10); + this.OnlineClients.Location = new System.Drawing.Point(226, 38); this.OnlineClients.Name = "OnlineClients"; this.OnlineClients.Size = new System.Drawing.Size(13, 13); this.OnlineClients.TabIndex = 3; @@ -74,26 +75,14 @@ private void InitializeComponent() // // label3 // - this.label3.Location = new System.Drawing.Point(166, 10); + this.label3.Location = new System.Drawing.Point(166, 38); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(54, 21); this.label3.TabIndex = 2; this.label3.Text = "Online:"; // - // ClientStatus - // - this.ClientStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.ClientStatus.FormattingEnabled = true; - this.ClientStatus.Location = new System.Drawing.Point(13, 35); - this.ClientStatus.Name = "ClientStatus"; - this.ClientStatus.Size = new System.Drawing.Size(514, 108); - this.ClientStatus.TabIndex = 4; - // // annunciatorPanel1 // - this.annunciatorPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.annunciatorPanel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.annunciatorPanel1.Controls.Add(this.Relay0Annunciator); this.annunciatorPanel1.Controls.Add(this.Relay1Annunciator); @@ -103,7 +92,7 @@ private void InitializeComponent() this.annunciatorPanel1.Controls.Add(this.Relay5Annunciator); this.annunciatorPanel1.Controls.Add(this.Relay6Annunciator); this.annunciatorPanel1.Controls.Add(this.Relay7Annunciator); - this.annunciatorPanel1.Location = new System.Drawing.Point(346, 7); + this.annunciatorPanel1.Location = new System.Drawing.Point(93, 14); this.annunciatorPanel1.Name = "annunciatorPanel1"; this.annunciatorPanel1.Size = new System.Drawing.Size(181, 19); this.annunciatorPanel1.TabIndex = 5; @@ -230,7 +219,7 @@ private void InitializeComponent() // // SetupCommand // - this.SetupCommand.Location = new System.Drawing.Point(265, 5); + this.SetupCommand.Location = new System.Drawing.Point(12, 12); this.SetupCommand.Name = "SetupCommand"; this.SetupCommand.Size = new System.Drawing.Size(75, 23); this.SetupCommand.TabIndex = 8; @@ -242,10 +231,11 @@ private void InitializeComponent() // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(539, 160); + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.ClientSize = new System.Drawing.Size(310, 73); this.Controls.Add(this.SetupCommand); this.Controls.Add(this.annunciatorPanel1); - this.Controls.Add(this.ClientStatus); this.Controls.Add(this.OnlineClients); this.Controls.Add(this.label3); this.Controls.Add(this.registeredClientCount); @@ -254,6 +244,7 @@ private void InitializeComponent() this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; this.Location = global::TA.ArduinoPowerController.Server.Properties.Settings.Default.MainFormLocation; this.Name = "ServerStatusDisplay"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; this.Text = "Arduino Power Controller"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.frmMain_FormClosing); this.Load += new System.EventHandler(this.frmMain_Load); @@ -271,17 +262,16 @@ private void InitializeComponent() private System.Windows.Forms.Label registeredClientCount; private System.Windows.Forms.Label OnlineClients; private System.Windows.Forms.Label label3; - private System.Windows.Forms.ListBox ClientStatus; - private ASCOM.Controls.AnnunciatorPanel annunciatorPanel1; + private AnnunciatorPanel annunciatorPanel1; private System.Windows.Forms.Button SetupCommand; - private ASCOM.Controls.Annunciator Relay0Annunciator; - private ASCOM.Controls.Annunciator Relay1Annunciator; - private ASCOM.Controls.Annunciator Relay2Annunciator; - private ASCOM.Controls.Annunciator Relay3Annunciator; - private ASCOM.Controls.Annunciator Relay4Annunciator; - private ASCOM.Controls.Annunciator Relay5Annunciator; - private ASCOM.Controls.Annunciator Relay6Annunciator; - private ASCOM.Controls.Annunciator Relay7Annunciator; + private Annunciator Relay0Annunciator; + private Annunciator Relay1Annunciator; + private Annunciator Relay2Annunciator; + private Annunciator Relay3Annunciator; + private Annunciator Relay4Annunciator; + private Annunciator Relay5Annunciator; + private Annunciator Relay6Annunciator; + private Annunciator Relay7Annunciator; } } diff --git a/TA.ArduinoPowerController.Server/ServerStatusDisplay.cs b/TA.ArduinoPowerController.Server/ServerStatusDisplay.cs index fe0f3fc..99827e0 100644 --- a/TA.ArduinoPowerController.Server/ServerStatusDisplay.cs +++ b/TA.ArduinoPowerController.Server/ServerStatusDisplay.cs @@ -12,11 +12,11 @@ using System.Reactive.Linq; using System.Threading; using System.Windows.Forms; -using ASCOM.Controls; using NLog; using TA.ArduinoPowerController.DeviceInterface; using TA.ArduinoPowerController.Server.Properties; using TA.Ascom.ReactiveCommunications.Diagnostics; +using TA.WinFormsControls; namespace TA.ArduinoPowerController.Server { @@ -94,19 +94,6 @@ private void ObserveClientStatusChanged(EventPattern eventPattern) SetUiButtonState(); SetUiDeviceConnectedState(); var clientStatus = SharedResources.ConnectionManager.Clients; - try - { - ClientStatus.BeginUpdate(); - ClientStatus.Items.Clear(); - foreach (var client in clientStatus) - { - ClientStatus.Items.Add(client); - } - } - finally - { - ClientStatus.EndUpdate(); - } registeredClientCount.Text = clientStatus.Count().ToString(); OnlineClients.Text = clientStatus.Count(p => p.Online).ToString(); ConfigureUiPropertyNotifications(); diff --git a/TA.ArduinoPowerController.Server/Settings.cs b/TA.ArduinoPowerController.Server/Settings.cs index f9b2009..f07c879 100644 --- a/TA.ArduinoPowerController.Server/Settings.cs +++ b/TA.ArduinoPowerController.Server/Settings.cs @@ -22,7 +22,7 @@ namespace TA.ArduinoPowerController.Server.Properties // The SettingsSaving event is raised before the setting values are saved. [SettingsProvider(typeof(SettingsProvider))] [DeviceId(SharedResources.SwitchDriverId, DeviceName = SharedResources.SwitchDriverName)] - public sealed partial class Settings + internal sealed partial class Settings { private readonly ILogger log = LogManager.GetCurrentClassLogger(); diff --git a/TA.ArduinoPowerController.Server/SharedResources.cs b/TA.ArduinoPowerController.Server/SharedResources.cs index 9adbe79..6df12de 100644 --- a/TA.ArduinoPowerController.Server/SharedResources.cs +++ b/TA.ArduinoPowerController.Server/SharedResources.cs @@ -7,9 +7,11 @@ using System; using System.Windows.Forms; +using Ninject; using NLog; using TA.ArduinoPowerController.DeviceInterface; using TA.ArduinoPowerController.Server.Properties; +using TA.Utils.Core.Diagnostics; namespace TA.ArduinoPowerController.Server { @@ -33,58 +35,47 @@ public static class SharedResources /// public const string SwitchDriverName = "Arduino Power Controller"; - private static readonly ILogger Log; + private static readonly ILog Log = CompositionRoot.Kernel.Get(); static SharedResources() { - Log = LogManager.GetCurrentClassLogger(); - ConnectionManager = CreateConnectionManager(); } /// /// Gets the connection manager. /// /// The connection manager. - public static ClientConnectionManager ConnectionManager { get; } + public static ClientConnectionManager ConnectionManager => CompositionRoot.Kernel.Get(); - private static ClientConnectionManager CreateConnectionManager() - { - Log.Info("Creating ClientConnectionManager"); - return new ClientConnectionManager( - CreateTransactionProcessorFactory()); - } - private static ITransactionProcessorFactory CreateTransactionProcessorFactory() - { - Log.Warn( - $"Creating transaction processor factory with connection string {Settings.Default.ConnectionString}"); - return new ReactiveTransactionProcessorFactory(Settings.Default.ConnectionString ?? "(not set)"); - } public static void DoSetupDialog(Guid clientId) { var oldConnectionString = Settings.Default.ConnectionString; - Log.Info($"SetupDialog requested by client {clientId}"); + Log.Info().Message("SetupDialog requested by client {clientId}", clientId).Write(); using (var dialogForm = new SetupDialogForm()) { var result = dialogForm.ShowDialog(); switch (result) { - case DialogResult.OK: - Log.Info($"SetupDialog successful, saving settings"); - Settings.Default.Save(); - var newConnectionString = Settings.Default.ConnectionString; - if (oldConnectionString != newConnectionString) - { - Log.Warn( - $"Connection string has changed from {oldConnectionString} to {newConnectionString} - replacing the TansactionProcessorFactory"); - ConnectionManager.TransactionProcessorFactory = CreateTransactionProcessorFactory(); - } - break; - default: - Log.Warn("SetupDialog cancelled or failed - reverting to previous settings"); - Settings.Default.Reload(); - break; + case DialogResult.OK: + Log.Info().Message("SetupDialog successful, saving settings").Write(); + Settings.Default.Save(); + var newConnectionString = Settings.Default.ConnectionString; + if (oldConnectionString != newConnectionString) + { + Log.Warn().Message( + "Connection string has changed from {old} to {new} - replacing the TansactionProcessorFactory") + .Property("old", oldConnectionString) + .Property("new", newConnectionString) + .Write(); + ConnectionManager.TransactionProcessorFactory = CompositionRoot.Kernel.Get(); + } + break; + default: + Log.Warn().Message("SetupDialog cancelled or failed - reverting to previous settings").Write(); + Settings.Default.Reload(); + break; } } } diff --git a/TA.ArduinoPowerController.Server/TA.ArduinoPowerController.Server.csproj b/TA.ArduinoPowerController.Server/TA.ArduinoPowerController.Server.csproj index c67384a..46c3ce5 100644 --- a/TA.ArduinoPowerController.Server/TA.ArduinoPowerController.Server.csproj +++ b/TA.ArduinoPowerController.Server/TA.ArduinoPowerController.Server.csproj @@ -10,7 +10,7 @@ Properties TA.ArduinoPowerController.Server TA.ArduinoPowerController.Server - v4.6.2 + v4.8 2.0 @@ -33,13 +33,12 @@ true - True true full false - ..\BuildOutput\ + ..\BuildOutput\Debug\ DEBUG;TRACE prompt 4 @@ -50,7 +49,7 @@ pdbonly true - ..\BuildOutput\ + ..\BuildOutput\Release\ TRACE prompt 4 @@ -61,89 +60,25 @@ false - LocalServer.snk + + - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Astrometry.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Attributes.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Controls.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.DeviceInterfaces.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.DriverAccess.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Exceptions.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Internal.Extensions.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.SettingsProvider.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Utilities.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Utilities.Video.dll - True - - - ..\packages\JetBrains.Annotations.10.4.0\lib\net\JetBrains.Annotations.dll - - - ..\packages\NLog.4.4.5\lib\net45\NLog.dll - - - ..\packages\PostSharp.4.3.31\lib\net35-client\PostSharp.dll - True - - - ..\packages\PostSharp.Patterns.Common.4.3.31\lib\net45\PostSharp.Patterns.Common.dll - - - ..\packages\PostSharp.Patterns.Threading.4.3.31\lib\net45\PostSharp.Patterns.Threading.dll - + + + - - ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll - - - ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll - - - ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll - - - ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll - + + + + + - - ..\packages\TA.Ascom.ReactiveCommunications.0.4.2\lib\net45\TA.Ascom.ReactiveCommunications.dll - + - - Properties\GlobalAssemblyInfo.cs - Form @@ -151,15 +86,17 @@ AboutBox.cs + - + UserControl CommunicationSettingsControl.cs + @@ -175,7 +112,7 @@ Settings.settings - + Form @@ -205,34 +142,20 @@ - - LocalServerLayers.layerdiagram + + Dependency Validation.layerdiagram False - Always Designer true - - NLog.config - True - - - NLog.config - True - - - Designer - - SettingsSingleFileGenerator Settings.Designer.cs - @@ -275,6 +198,41 @@ TA.PostSharp.Aspects + + + 6.5.2 + + + 5.9.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + 2021.3.0 + + + 3.3.4 + + + 4.7.15 + + + 4.7.15 + + + 6.10.8 + + + 6.10.8 + + + 1.3.0 + + + 1.0.1 + + + diff --git a/TA.ArduinoPowerController.Setup/AscomSwitchDriver.wxs b/TA.ArduinoPowerController.Setup/AscomSwitchDriver.wxs index 884be6d..c2b87eb 100644 --- a/TA.ArduinoPowerController.Setup/AscomSwitchDriver.wxs +++ b/TA.ArduinoPowerController.Setup/AscomSwitchDriver.wxs @@ -16,16 +16,11 @@ + - diff --git a/TA.ArduinoPowerController.Setup/DeviceControlLayer.wxs b/TA.ArduinoPowerController.Setup/DeviceControlLayer.wxs index c7f5f15..8459ba6 100644 --- a/TA.ArduinoPowerController.Setup/DeviceControlLayer.wxs +++ b/TA.ArduinoPowerController.Setup/DeviceControlLayer.wxs @@ -7,6 +7,7 @@ + + + + + + + + + + + diff --git a/TA.ArduinoPowerController.Setup/NLog.wxs b/TA.ArduinoPowerController.Setup/NLog.wxs index 2a1ea80..abb1670 100644 --- a/TA.ArduinoPowerController.Setup/NLog.wxs +++ b/TA.ArduinoPowerController.Setup/NLog.wxs @@ -9,7 +9,6 @@ KeyPath="yes" /> - diff --git a/TA.ArduinoPowerController.Setup/Ninject.wxs b/TA.ArduinoPowerController.Setup/Ninject.wxs new file mode 100644 index 0000000..7fbb952 --- /dev/null +++ b/TA.ArduinoPowerController.Setup/Ninject.wxs @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/TA.ArduinoPowerController.Setup/PostSharp.wxs b/TA.ArduinoPowerController.Setup/PostSharp.wxs index 80a5a50..704fe0a 100644 --- a/TA.ArduinoPowerController.Setup/PostSharp.wxs +++ b/TA.ArduinoPowerController.Setup/PostSharp.wxs @@ -10,9 +10,15 @@ KeyPath="yes" /> - + + + diff --git a/TA.ArduinoPowerController.Setup/PostSharpAspects.wxs b/TA.ArduinoPowerController.Setup/PostSharpAspects.wxs index 630de0e..f68c4a4 100644 --- a/TA.ArduinoPowerController.Setup/PostSharpAspects.wxs +++ b/TA.ArduinoPowerController.Setup/PostSharpAspects.wxs @@ -5,6 +5,7 @@ + - + + - + + + @@ -20,5 +29,14 @@ NOT VersionNT64 + + + + + = 601)]]> + + = 601)]]> + + diff --git a/TA.ArduinoPowerController.Setup/ReactiveExtensions.wxs b/TA.ArduinoPowerController.Setup/ReactiveExtensions.wxs index 7b61d40..4c18e17 100644 --- a/TA.ArduinoPowerController.Setup/ReactiveExtensions.wxs +++ b/TA.ArduinoPowerController.Setup/ReactiveExtensions.wxs @@ -3,30 +3,13 @@ + - - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Setup/RuntimeSupport.wxs b/TA.ArduinoPowerController.Setup/RuntimeSupport.wxs new file mode 100644 index 0000000..745846d --- /dev/null +++ b/TA.ArduinoPowerController.Setup/RuntimeSupport.wxs @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/TA.ArduinoPowerController.Setup/TA.ArduinoPowerController.Setup.wixproj b/TA.ArduinoPowerController.Setup/TA.ArduinoPowerController.Setup.wixproj index 0fdaff1..58164d2 100644 --- a/TA.ArduinoPowerController.Setup/TA.ArduinoPowerController.Setup.wixproj +++ b/TA.ArduinoPowerController.Setup/TA.ArduinoPowerController.Setup.wixproj @@ -1,5 +1,7 @@  + + Debug x86 @@ -12,10 +14,13 @@ $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets bin\$(Configuration)-$(Platform)\ obj\$(Configuration)-$(Platform)\ + + Debug ICE69 + ..\BuildOutput\$(Configuration)-$(Platform)\ Debug @@ -46,14 +51,6 @@ Binaries;Content;Satellites INSTALLFOLDER - - SwitchDriver - {ed0fe6aa-e1e5-4d21-9b5e-802d50edcce1} - True - True - Binaries;Content;Satellites - INSTALLFOLDER - Aspects {9cdcf319-dadc-41eb-b787-de3862017e95} @@ -70,18 +67,32 @@ + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch.Specifications/ASCOM.K8056.Switch.Specifications.csproj.DotSettings b/TA.ArduinoPowerController.Switch.Specifications/ASCOM.K8056.Switch.Specifications.csproj.DotSettings deleted file mode 100644 index 184255f..0000000 --- a/TA.ArduinoPowerController.Switch.Specifications/ASCOM.K8056.Switch.Specifications.csproj.DotSettings +++ /dev/null @@ -1,4 +0,0 @@ - - Implicit - Implicit - False \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch.Specifications/ASCOM.K8056.Switch.Specifications.v2.ncrunchproject b/TA.ArduinoPowerController.Switch.Specifications/ASCOM.K8056.Switch.Specifications.v2.ncrunchproject deleted file mode 100644 index 05b9746..0000000 --- a/TA.ArduinoPowerController.Switch.Specifications/ASCOM.K8056.Switch.Specifications.v2.ncrunchproject +++ /dev/null @@ -1,27 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - false - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch.Specifications/TransactionSpecs.cs b/TA.ArduinoPowerController.Switch.Specifications/TransactionSpecs.cs deleted file mode 100644 index f282df8..0000000 --- a/TA.ArduinoPowerController.Switch.Specifications/TransactionSpecs.cs +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of the ASCOM.K8056.Switch project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: TransactionSpecs.cs Last modified: 2017-03-06@19:17 by Tim Long - -using System.Collections.Generic; -using Machine.Specifications; -using TA.VellemanK8056.DeviceInterface; - -namespace ASCOM.K8056.Switch.Specifications - { - [Subject(typeof(NoReplyTransaction))] - public class when_computing_a_twos_compliment_checksum_of_bytes - { - static byte actualChecksum; - static byte expectedChecksum; - static List input; - Establish context = () => - { - input = new List {0x0A, 0xAE, 0x00, 0x00, 0x46, 0x31, 0x30, 0x00, 0x41, 0x44, 0x43, 0x00, 0x00}; - expectedChecksum = 0xD9; - }; - Because of = () => actualChecksum = NoReplyTransaction.ComputeChecksum(input); - It should_give_the_expected_checksum = () => actualChecksum.ShouldEqual(expectedChecksum); - } - } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch.Specifications/app.config b/TA.ArduinoPowerController.Switch.Specifications/app.config deleted file mode 100644 index d48b4ff..0000000 --- a/TA.ArduinoPowerController.Switch.Specifications/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch.Specifications/packages.config b/TA.ArduinoPowerController.Switch.Specifications/packages.config deleted file mode 100644 index c5745d5..0000000 --- a/TA.ArduinoPowerController.Switch.Specifications/packages.config +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/AscomDriverAssemblyResolver.cs b/TA.ArduinoPowerController.Switch/AscomDriverAssemblyResolver.cs deleted file mode 100644 index 7f611be..0000000 --- a/TA.ArduinoPowerController.Switch/AscomDriverAssemblyResolver.cs +++ /dev/null @@ -1,73 +0,0 @@ -// This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: AscomDriverAssemblyResolver.cs Last modified: 2017-03-17@17:36 by Tim Long - -using System; -using System.IO; -using System.Reflection; -using NLog; - -namespace TA.ArduinoPowerController.AscomSwitch - { - internal static class AscomDriverAssemblyResolver - { - private static readonly Logger Log = LogManager.GetCurrentClassLogger(); - - /// - /// Resolves support assemblies for an ASCOM driver by searching in the same directory - /// as the driver assembly. - /// - /// The sender. - /// - /// The instance containing the event data. - /// - /// The loaded resolved assembly. - /// - /// - /// The CLR's default assembly resolution strategy is to look in the Global Assembly Cache, - /// then in the application directory and subfolders. However, an ASCOM driver is loaded - /// via COM Interop and is not normally located in the application directory, so the runtime - /// will need help locating an referenced assemblies. - /// - /// - /// By default, ASCOM solves this by including the CodeBase registry value in the driver's - /// COM Interop registration. This requires that the driver is strong named, which means that - /// any referenced assemblies also have to be strong named. This can be problematic, especially - /// if the driver uses third party code that is not signed. - /// - /// - /// Tigra Astronomy prefers to use a custom assembly resolver, which simply looks in the same - /// directory as the driver assembly. This avoids the need to sign the driver with a strong-name, - /// which gives us a bit more flexibility in using third party libraries and tends to simplify - /// build, debug and deployment. - /// - /// - public static Assembly ResolveSupportAssemblies(object sender, ResolveEventArgs args) - { - try - { - Log.Info($"Handling AppDomain.ResolveAssembly event for {args.Name}"); - Log.Info("Attempting to resolve the assembly"); - var me = Assembly.GetExecutingAssembly(); - var here = me.Location; - var myDirectory = Path.GetDirectoryName(here); - var commaPosition = args.Name.IndexOf(','); - var targetName = args.Name.Head(commaPosition); - var targetDll = targetName + ".dll"; - var target = Path.Combine(myDirectory, targetDll); - Log.Info($"Target assembly is {target}"); - var resolvedAssembly = Assembly.LoadFrom(target); - Log.Info($"Successfully resolved assembly load with {resolvedAssembly.GetName()}"); - return resolvedAssembly; - } - catch (Exception ex) - { - Log.Error(ex, $"Failed to resolve assembly: {args.Name}"); - return null; // Let the app raise its own error. - } - } - } - } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/NLog.dll.Debug.nlog b/TA.ArduinoPowerController.Switch/NLog.dll.Debug.nlog deleted file mode 100644 index f2aa0df..0000000 --- a/TA.ArduinoPowerController.Switch/NLog.dll.Debug.nlog +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/NLog.dll.Release.nlog b/TA.ArduinoPowerController.Switch/NLog.dll.Release.nlog deleted file mode 100644 index db7ce87..0000000 --- a/TA.ArduinoPowerController.Switch/NLog.dll.Release.nlog +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/NLog.dll.nlog b/TA.ArduinoPowerController.Switch/NLog.dll.nlog deleted file mode 100644 index 1949941..0000000 --- a/TA.ArduinoPowerController.Switch/NLog.dll.nlog +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/Octet.cs b/TA.ArduinoPowerController.Switch/Octet.cs deleted file mode 100644 index e611c18..0000000 --- a/TA.ArduinoPowerController.Switch/Octet.cs +++ /dev/null @@ -1,234 +0,0 @@ -// This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: Octet.cs Last modified: 2017-04-02@18:44 by Tim Long - -using System; -using System.Diagnostics.Contracts; -using System.Text; -using PostSharp.Patterns.Contracts; - -namespace TA.ArduinoPowerController.AscomSwitch - { - /// - /// An immutable representation of an 8 bit byte, with each bit individually addressable. In - /// most cases an Octet is interchangeable with a (implicit conversion - /// operators are provided). An Octet can be explicitly converted (cast) to or from an - /// integer. - /// - public struct Octet : IEquatable - { - private readonly bool[] bits; - - [ContractInvariantMethod] - private void ObjectInvariant() - { - Contract.Invariant(bits != null); - Contract.Invariant(bits.Length == 8, "Consider using Octet.FromInt() instead of new Octet()"); - } - - /// - /// Initializes a new instance of the struct from an array of at least 8 booleans. - /// - /// The bits; there must be exactly 8. - private Octet([Required] bool[] bits) - { - Contract.Requires(bits != null); - Contract.Requires(bits.Length == 8); - this.bits = bits; - } - - /// - /// Gets an Octet with all the bits set to zero. - /// - public static Octet Zero { get; } = FromInt(0); - - /// - /// Gets an Octet set to the maximum value (i.e. all the bits set to one). - /// - public static Octet Max { get; } = FromInt(0xFF); - - public bool this[int bit] - { - get - { - Contract.Requires(bit >= 0 && bit < 8); - return bits[bit]; - } - } - - /// - /// Factory method: create an Octet from an integer. - /// - /// The source. - /// Octet. - public static Octet FromInt(int source) - { - var bits = new bool[8]; - for (var i = 0; i < 8; i++) - { - var bit = source & 0x01; - bits[i] = bit != 0; - source >>= 1; - } - return new Octet(bits); - } - - /// - /// Factory method: create an Octet from an unisgned integer. - /// - /// The source. - /// Octet. - public static Octet FromUnsignedInt(uint source) - { - return FromInt((int) source); - } - - /// - /// Returns a new octet with the specified bit number set to the specified value. - /// Other bits are duplicated. - /// - /// The bit number to be modified. - /// The value of the specified bit number. - /// A new octet instance with the specified bit number set to the specified value. - public Octet WithBitSetTo(ushort bit, bool value) - { - Contract.Requires(bit < 8); - var newBits = new bool[8]; - bits.CopyTo(newBits, 0); - newBits[bit] = value; - return new Octet(newBits); - } - - /// - /// Returns a new octet with the specified bit number set to the specified value. - /// Other bits are duplicated. - /// - /// The bit number to be modified. - /// The value of the specified bit number. - /// A new octet instance with the specified bit number set to the specified value. - public Octet WithBitSetTo(int bit, bool value) - { - Contract.Requires(bit >= 0 && bit < 8); - return WithBitSetTo((ushort) bit, value); - } - - public override string ToString() - { - var builder = new StringBuilder(); - for (var i = 7; i >= 0; i--) - { - builder.Append(bits[i] ? '1' : '0'); - builder.Append(' '); - } - builder.Length -= 1; - return builder.ToString(); - } - - /// - /// Performs an explicit conversion from to . - /// This conversion is explicit because there is potential loss of information. - /// - /// The integer. - /// The result of the conversion. - public static explicit operator Octet(uint integer) - { - return FromUnsignedInt(integer); - } - - /// - /// Performs an explicit conversion from to . - /// This conversion is explicit because there is potential loss of information. - /// - /// The integer. - /// The result of the conversion. - public static explicit operator Octet(int integer) - { - return FromInt(integer); - } - - /// - /// Performs an implicit conversion from to . - /// - /// The octet. - /// The result of the conversion. - public static implicit operator byte(Octet octet) - { - var sum = 0; - for (var i = 0; i < 8; i++) - { - if (octet[i]) sum += 1 << i; - } - return (byte) sum; - } - - /// - /// Performs an implicit conversion from to . - /// - /// The input byte. - /// The result of the conversion in a new Octet. - public static implicit operator Octet(byte b) - { - return FromUnsignedInt(b); - } - - /// - /// Indicates whether this octet is equal to another octet, using value semantics. - /// - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - /// An object to compare with this object. - public bool Equals(Octet other) - { - for (var i = 0; i < bits.Length; i++) - { - if (bits[i] != other[i]) return false; - } - return true; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - return obj is Octet && Equals((Octet) obj); - } - - public override int GetHashCode() - { - return bits.GetHashCode(); - } - - public static bool operator ==(Octet left, Octet right) - { - return left.Equals(right); - } - - public static bool operator !=(Octet left, Octet right) - { - return !left.Equals(right); - } - - public static Octet operator &(Octet left, Octet right) - { - var result = (bool[]) left.bits.Clone(); - for (var i = 0; i < 8; i++) - { - result[i] &= right[i]; - } - return new Octet(result); - } - - public static Octet operator |(Octet left, Octet right) - { - var result = (bool[]) left.bits.Clone(); - for (var i = 0; i < 8; i++) - { - result[i] |= right[i]; - } - return new Octet(result); - } - } - } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/Properties/AssemblyInfo.cs b/TA.ArduinoPowerController.Switch/Properties/AssemblyInfo.cs deleted file mode 100644 index b9ce97f..0000000 --- a/TA.ArduinoPowerController.Switch/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -// This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: AssemblyInfo.cs Last modified: 2017-04-02@18:44 by Tim Long - -using System.Reflection; - -[assembly: AssemblyTitle("ASCOM.ArduinoPowerController.Switch")] -[assembly: AssemblyDescription("ASCOM Switch Driver for Arduino Power Controller")] \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/Scripts/LoadSwitch.ps1 b/TA.ArduinoPowerController.Switch/Scripts/LoadSwitch.ps1 deleted file mode 100644 index 0e1a13f..0000000 --- a/TA.ArduinoPowerController.Switch/Scripts/LoadSwitch.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -Push-Location ..\bin\Debug -Add-Type -path .\ASCOM.ArduinoPowerController.Switch.dll -$global:sw=New-Object ASCOM.ArduinoPowerController.Switch -$global:sw.Connected = $true -Pop-Location diff --git a/TA.ArduinoPowerController.Switch/Scripts/WalkingRelayTest.ps1 b/TA.ArduinoPowerController.Switch/Scripts/WalkingRelayTest.ps1 deleted file mode 100644 index f64f918..0000000 --- a/TA.ArduinoPowerController.Switch/Scripts/WalkingRelayTest.ps1 +++ /dev/null @@ -1,19 +0,0 @@ - -$sw = New-Object -ComObject ASCOM.ArduinoPowerController.Switch -$sw.Connected = $true - -Function WalkState([bool] $state) - { - For ($i=0; $i -lt 8; $i++) - { - $sw.SetSwitch($i, $state) - Start-Sleep -Milliseconds 250 - } - } - -While ($true) - { - WalkState($true) - WalkState($false) - } - diff --git a/TA.ArduinoPowerController.Switch/StringExtensions.cs b/TA.ArduinoPowerController.Switch/StringExtensions.cs deleted file mode 100644 index 102e609..0000000 --- a/TA.ArduinoPowerController.Switch/StringExtensions.cs +++ /dev/null @@ -1,272 +0,0 @@ -// This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: StringExtensions.cs Last modified: 2017-03-17@17:37 by Tim Long - -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Linq; -using System.Text; - -namespace TA.ArduinoPowerController.AscomSwitch - { - public static class StringExtensions - { - private static readonly string[] AsciiEncoding = - { - "", "", "", "", "", "", "", "\a", "\b", "\t", "\n", "\v", "\f", - "\r", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", " ", "!", "\"", "#", "$", "%", - "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", - ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", - "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", - "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", - "|", "}", "~", "" - }; - - private static readonly IDictionary asciiMnemonics = new Dictionary - { - {0x00, ""}, - {0x01, ""}, - {0x02, ""}, - {0x03, ""}, - {0x04, ""}, - {0x05, ""}, - {0x06, ""}, - {0x07, ""}, - {0x08, ""}, - {0x09, ""}, - {0x0A, ""}, - {0x0B, ""}, - {0x0C, ""}, - {0x0D, ""}, - {0x0E, ""}, - {0x0F, ""}, - {0x11, ""}, - {0x12, ""}, - {0x13, ""}, - {0x14, ""}, - {0x15, ""}, - {0x16, ""}, - {0x17, ""}, - {0x18, ""}, - {0x19, ""}, - {0x1A, ""}, - {0x1B, ""}, - {0x1C, ""}, - {0x1D, ""}, - {0x1E, ""}, - {0x1F, ""}, - //{ 0x20, "" }, - {0x7F, ""} - }; - - public static bool CaseInsensitiveEquals(this string lhs, string rhs) - { - Contract.Requires(lhs != null); - Contract.Requires(rhs != null); - return string.Equals(lhs.ToLower(), rhs.ToLower()); - } - - /// - /// Removes all unwanted characters from a string. - /// - /// The source string. - /// A list of the unwanted characters. All other characters will be preserved. - /// - /// A new string with all of the unwanted characters deleted. Returns - /// if all of the characters were deleted or if the source string was null or empty. - /// - /// - /// Contrast with - /// - /// - public static string Clean(this string source, string clean) - { - if (string.IsNullOrEmpty(source)) - return string.Empty; - var cleanString = new StringBuilder(source.Length); - foreach (var ch in source) - { - if (!clean.Contains(ch)) - cleanString.Append(ch); - } - return cleanString.ToString(); - } - - public static bool Contains(this string s, string value) - { - Contract.Requires(s != null); - Contract.Requires(value != null); - return s.IndexOf(value) >= 0; - } - - public static bool EndsWith(this string s, string value) - { - Contract.Requires(s != null); - Contract.Requires(value != null); - return s.IndexOf(value) == s.Length - value.Length; - } - - /// - /// Utility function. Expands non-printable ASCII characters into mnemonic human-readable form. - /// - /// - /// Returns a new string with non-printing characters replaced by human-readable mnemonics. - /// - public static string ExpandASCII(this string inputString) - { - Contract.Requires(inputString != null); - Contract.Ensures(Contract.Result() != null); - var expanded = new StringBuilder(inputString.Length); - foreach (var c in inputString) - expanded.Append(c.ExpandASCII()); - return expanded.ToString(); - } - - /// - /// Utility function. Expands non-printable ASCII characters into mnemonic human-readable form. - /// printable characters are returned unmodified. - /// - /// The c. - /// - /// If the original character is a non-printing ASCII character, then returns a string containing a - /// human-readable mnemonic for that ASCII character, - /// - /// 0x07 returns <BELL> - /// - /// . - /// Otherwise, returns the original character converted to a string. - /// - public static string ExpandASCII(this char c) - { - Contract.Ensures(Contract.Result() != null); - int asciiCode = c; - return asciiMnemonics.ContainsKey(asciiCode) ? asciiMnemonics[asciiCode] : c.ToString(); - } - - public static string GetString(this byte[] bytes) - { - Contract.Requires(bytes != null); - Contract.Ensures(Contract.Result() != null); - var builder = new StringBuilder(bytes.Length); - for (var i = 0; i < bytes.Length; i++) - builder.Append(AsciiEncoding[bytes[i] & 0x7F]); - return builder.ToString(); - } - - /// - /// Returns the specified number of characters from the head of a string. - /// - /// The source string. - /// The number of characters to be returned, must not be greater than the length of the string. - /// The specified number of characters from the head of the source string, as a new string. - /// Thrown if the requested number of characters exceeds the string length. - public static string Head(this string source, int length) - { - if (length > source.Length) - { - throw new ArgumentOutOfRangeException("source", - "The specified length is greater than the length of the string."); - } - return source.Substring(0, length); - } - - /// - /// Keeps only the wanted (that is, removes all unwanted characters) from the string. - /// - /// The source string. - /// A list of the wanted characters. All other characters will be removed. - /// - /// A new string with all of the unwanted characters deleted. Returns if all - /// the characters were deleted or if the source string was null or empty. - /// - /// - public static string Keep(this string source, string keep) - { - if (string.IsNullOrEmpty(source)) - return string.Empty; - var cleanString = new StringBuilder(source.Length); - foreach (var ch in source) - { - if (keep.Contains(ch)) - cleanString.Append(ch); - } - return cleanString.ToString(); - } - - /// - /// Remove the head of the string, leaving the tail. - /// - /// The source string. - /// Number of characters to remove from the head. - /// A new string containing the old string with characters removed from the head. - public static string RemoveHead(this string source, int length) - { - if (length < 1) - return source; - return source.Tail(source.Length - length); - } - - /// - /// Remove the tail of the string, leaving the head. - /// - /// The source string. - /// Number of characters to remove from the tail. - /// A new string containing the old string with characters removed from the tail. - public static string RemoveTail(this string source, int length) - { - if (length < 1) - return source; - return source.Head(source.Length - length); - } - - public static bool StartsWith(this string s, string value) - { - Contract.Requires(s != null); - Contract.Requires(value != null); - return s.IndexOf(value) == 0; - } - - /// - /// Returns the specified number of characters from the tail of a string. - /// - /// The source string. - /// The number of characters to be returned, must not be greater than the length of the string. - /// The specified number of characters from the tail of the source string, as a new string. - /// Thrown if the requested number of characters exceeds the string length. - public static string Tail(this string source, int length) - { - var srcLength = source.Length; - if (length > srcLength) - { - throw new ArgumentOutOfRangeException("source", - "The specified length is greater than the length of the string."); - } - return source.Substring(srcLength - length, length); - } - - /// - /// Converts a tring to a hex representation, suitable for display in a debugger. - /// - /// The source string. - /// A new string showing each character of the source string as a hex digit. - public static string ToHex(this string source) - { - const string formatWithSeperator = ", {0,2:x}"; - const string formatNoSeperator = "{0,2:x}"; - var hexString = new StringBuilder(source.Length * 7); - hexString.Append('{'); - var seperator = false; - foreach (var ch in source) - { - hexString.AppendFormat(seperator ? formatWithSeperator : formatNoSeperator, (int) ch); - seperator = true; - } - hexString.Append('}'); - return hexString.ToString(); - } - } - } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/TA.ArduinoPowerController.AscomSwitch.csproj b/TA.ArduinoPowerController.Switch/TA.ArduinoPowerController.AscomSwitch.csproj deleted file mode 100644 index 902e939..0000000 --- a/TA.ArduinoPowerController.Switch/TA.ArduinoPowerController.AscomSwitch.csproj +++ /dev/null @@ -1,175 +0,0 @@ - - - - - Debug - AnyCPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1} - Library - Properties - TA.ArduinoPowerController.AscomSwitch - TA.ArduinoPowerController.AscomSwitch - v4.6.2 - 512 - - - - True - - - true - full - false - ..\BuildOutput\ - TRACE;DEBUG - prompt - 4 - x64 - false - - - pdbonly - true - ..\BuildOutput\ - TRACE - prompt - 4 - false - - - true - ..\BuildOutput\ - TRACE;DEBUG;DEBUG_IN_EXTERNAL_APP - full - x64 - prompt - MinimumRecommendedRules.ruleset - false - - - ..\BuildOutput\ - TRACE - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - false - - - - - - - - - ..\packages\JetBrains.Annotations.10.4.0\lib\net\JetBrains.Annotations.dll - - - ..\packages\NLog.4.4.5\lib\net45\NLog.dll - - - ..\packages\PostSharp.4.3.31\lib\net35-client\PostSharp.dll - True - - - ..\packages\PostSharp.Patterns.Common.4.3.31\lib\net45\PostSharp.Patterns.Common.dll - - - ..\packages\PostSharp.Patterns.Model.4.3.31\lib\net40\PostSharp.Patterns.Model.dll - - - ..\packages\PostSharp.Patterns.Threading.4.3.31\lib\net45\PostSharp.Patterns.Threading.dll - - - - - - - ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll - - - ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll - - - ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll - - - ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll - - - - - - - - - ..\packages\TA.Ascom.ReactiveCommunications.0.4.2\lib\net45\TA.Ascom.ReactiveCommunications.dll - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - - - - - NLog.dll.nlog - True - - - Always - true - - - NLog.dll.nlog - True - - - - - - - - {3015f05f-d747-4c3e-a0d4-ec2b3bd76c56} - TA.ArduinoPowerController.DeviceInterface - - - {3689a2cb-94c5-4012-a5cf-7e7d1dd27143} - TA.ArduinoPowerController.Server - - - {9cdcf319-dadc-41eb-b787-de3862017e95} - TA.PostSharp.Aspects - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/app.config b/TA.ArduinoPowerController.Switch/app.config deleted file mode 100644 index 199e35c..0000000 --- a/TA.ArduinoPowerController.Switch/app.config +++ /dev/null @@ -1,69 +0,0 @@ - - - - - -
- - -
- - - - - - - - - - - - - - - - - - - - - - COM1:2400,None,8,One,NoDTR,NoRTS - - - - - Relay 0 - Relay 1 - Relay 2 - Relay 3 - Relay 4 - Relay 5 - Relay 6 - Relay 7 - - - - - COM1 - - - - - - - 500 - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Switch/packages.config b/TA.ArduinoPowerController.Switch/packages.config deleted file mode 100644 index 90ec0ae..0000000 --- a/TA.ArduinoPowerController.Switch/packages.config +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/ClassDiagram1.cd b/TA.ArduinoPowerController.Test/ClassDiagram1.cd new file mode 100644 index 0000000..7b89419 --- /dev/null +++ b/TA.ArduinoPowerController.Test/ClassDiagram1.cd @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/Common/OctetTests.cs b/TA.ArduinoPowerController.Test/Common/OctetTests.cs new file mode 100644 index 0000000..7ca85e9 --- /dev/null +++ b/TA.ArduinoPowerController.Test/Common/OctetTests.cs @@ -0,0 +1,87 @@ +// This file is part of the TA.ArduinoPowerController project +// +// Copyright © 2016-2019 Tigra Astronomy, all rights reserved. +// Licensed under the Tigra MIT license, see http://tigra.mit-license.org/ +// +// File: OctetTests.cs Last modified: 2019-09-08@19:01 by Tim Long + +using Machine.Specifications; +using TA.Utils.Core; + +namespace TA.ArduinoPowerController.Test.Common +{ + [Subject(typeof(Octet), "Initialization")] + internal class when_an_octet_is_initialized_to_zero + { + Because of = () => zero = Octet.Zero; + + It should_have_all_bits_clear = () => + { + for (var i = 0; i < 8; i++) + { + zero[i].ShouldBeFalse(); + } + }; + + static Octet zero; + } + + [Subject(typeof(Octet), "bit clear")] + internal class when_a_bit_is_cleared + { + Establish context = () => immutableOctet = Octet.Max; + + It should_produce_a_new_octet_with_that_bit_cleared = () => + { + var octet = Octet.Max; // All bits set + for (int i = 0; i < 8; ++i) + { + var actual = octet.WithBitSetTo(i, false); + expected[i].ShouldEqual((byte) actual); + } + immutableOctet.ShouldEqual(Octet.Max); + }; + + static byte[] expected = {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F}; + static Octet immutableOctet; + } + + [Subject(typeof(Octet), "bit set")] + internal class when_a_bit_is_set + { + Establish context = () => immutableOctet = Octet.Zero; + + It should_produce_a_new_octet_with_that_bit_set = () => + { + for (int i = 0; i < 8; ++i) + { + var actual = immutableOctet.WithBitSetTo(i, true); + expected[i].ShouldEqual((byte) actual); + } + immutableOctet.ShouldEqual(Octet.Zero); + }; + + static byte[] expected = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + static Octet immutableOctet; + } + + [Subject(typeof(Octet), "bitwise AND")] + internal class when_a_bitwise_and_is_applied + { + Establish context = () => mask = 0xAA; + Because of = () => actual = Octet.Max & mask; + It should_mask_the_false_bits = () => actual.ShouldEqual(mask); + static Octet actual; + static Octet mask; + } + + [Subject(typeof(Octet), "bitwise OR")] + internal class when_a_bitwise_or_is_applied + { + Establish context = () => mask = 0xAA; + Because of = () => actual = Octet.Zero | mask; + It should_set_the_true_bits = () => actual.ShouldEqual(mask); + static Octet actual; + static Octet mask; + } +} \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/DeviceInterface/WriteRelayTransactionTests.cs b/TA.ArduinoPowerController.Test/DeviceInterface/WriteRelayTransactionTests.cs new file mode 100644 index 0000000..7c620d5 --- /dev/null +++ b/TA.ArduinoPowerController.Test/DeviceInterface/WriteRelayTransactionTests.cs @@ -0,0 +1,31 @@ +// This file is part of the TA.ArduinoPowerController project +// +// Copyright © 2016-2019 Tigra Astronomy, all rights reserved. +// Licensed under the Tigra MIT license, see http://tigra.mit-license.org/ +// +// File: WriteRelayTransactionTests.cs Last modified: 2019-09-09@18:46 by Tim Long + +using Machine.Specifications; +using TA.ArduinoPowerController.DeviceInterface; +using TA.ArduinoPowerController.Test.TestContexts; + +namespace TA.ArduinoPowerController.Test.DeviceInterface +{ + [Subject(typeof(WriteRelayTransaction), "command")] + internal class when_a_relay_transaction_is_processed : with_fake_communications_channel + { + Establish context = () => Context = Builder + .WithFakeResponse(":W00#") + .Build(); + + Because of = () => + { + Transaction = new WriteRelayTransaction(0, false); + Context.Processor.CommitTransaction(Transaction); + Transaction.WaitForCompletionOrTimeout(); + }; + + It should_succeed = () => Transaction.Successful.ShouldBeTrue(); + static WriteRelayTransaction Transaction; + } +} \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/Fakes/FakeCommunicationChannel.cs b/TA.ArduinoPowerController.Test/Fakes/FakeCommunicationChannel.cs new file mode 100644 index 0000000..1856b85 --- /dev/null +++ b/TA.ArduinoPowerController.Test/Fakes/FakeCommunicationChannel.cs @@ -0,0 +1,103 @@ +// This file is part of the TA.NexDome.AscomServer project +// Copyright © 2019-2019 Tigra Astronomy, all rights reserved. + +using System.Reactive.Linq; +using System.Reactive.Subjects; +using TA.Ascom.ReactiveCommunications; + +namespace TA.NexDome.Specifications.Fakes + { + using System; + using System.Diagnostics.Contracts; + using System.Text; + using TA.Utils.Core.Diagnostics; + + /// + /// A fake communication channel that logs any sent data in and + /// receives a fake pre-programmed response passed into the constructor. The class also + /// keeps a count of how many times each method of was + /// called. + /// + public class FakeCommunicationChannel : ICommunicationChannel + { + readonly IObservable receivedCharacters; + + readonly Subject receiveChannelSubject = new Subject(); + + readonly StringBuilder sendLog; + + readonly ILog Log = new TA.Utils.Logging.NLog.LoggingService(); + + /// + /// Dependency injection constructor. + /// Initializes a new instance of the class. + /// + /// + /// Implementation of the injected dependency. + public FakeCommunicationChannel(DeviceEndpoint endpoint, string fakeResponse) + { + Contract.Requires(fakeResponse != null); + Endpoint = endpoint; + Response = fakeResponse; + receivedCharacters = fakeResponse.ToCharArray().ToObservable(); + sendLog = new StringBuilder(); + IsOpen = false; + } + + /// + /// Gets the send log. + /// + /// The send log. + public string SendLog => sendLog.ToString(); + + /// + /// Gets a copy of the fake pre-programmed response. + /// + /// The response. + public string Response { get; } + + public int TimesDisposed { get; set; } + + public int TimesClosed { get; set; } + + public int TimesOpened { get; set; } + + public void Dispose() + { + TimesDisposed++; + } + + public void Open() + { + TimesOpened++; + IsOpen = true; + } + + public void Close() + { + TimesClosed++; + IsOpen = false; + } + + public void Send(string txData) + { + Log.Info().Message($"Send: {txData}").Property(nameof(txData), txData).Write(); + sendLog.Append(txData); + foreach (char c in Response) receiveChannelSubject.OnNext(c); + } + + public IObservable ObservableReceivedCharacters => receiveChannelSubject.AsObservable(); + + public bool IsOpen { get; set; } + + public DeviceEndpoint Endpoint { get; } + + public static ICommunicationChannel FromEndpoint(DeviceEndpoint endpoint) + { + if (!(endpoint is FakeEndpoint)) + throw new ArgumentException($"Expected a FakeEndpoint, got a {endpoint.GetType().Name}"); + var fake = endpoint as FakeEndpoint; + return new FakeCommunicationChannel(endpoint, fake.FakeResponse); + } + } +} \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/Fakes/FakeEndpoint.cs b/TA.ArduinoPowerController.Test/Fakes/FakeEndpoint.cs new file mode 100644 index 0000000..acdcf40 --- /dev/null +++ b/TA.ArduinoPowerController.Test/Fakes/FakeEndpoint.cs @@ -0,0 +1,41 @@ +// This file is part of the TA.NexDome.AscomServer project +// Copyright © 2019-2019 Tigra Astronomy, all rights reserved. + +using System; +using System.Text.RegularExpressions; + +namespace TA.NexDome.Specifications.Fakes + { + using TA.Ascom.ReactiveCommunications; + + class FakeEndpoint : DeviceEndpoint + { + public string FakeResponse { get; } + + public FakeEndpoint(string fakeResponse = null) + { + this.FakeResponse = fakeResponse; + } + + public override string ToString() + { + return "fake device"; + } + + public static bool IsConnectionStringValid(string connectionString) => + connectionString.StartsWith("Fake", StringComparison.InvariantCultureIgnoreCase); + + public static DeviceEndpoint FromConnectionString(string connectionString) + { + const string endpointParsePattern = "^Fake(:(?.*))?$"; + var parser = new Regex(endpointParsePattern, + RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | + RegexOptions.IgnoreCase | RegexOptions.Singleline); + var match = parser.Match(connectionString); + if (!match.Success) + throw new ArgumentException("unable to parse the connection string"); + var fakeResponse = match.Groups["Response"].Value; + return new FakeEndpoint(fakeResponse); + } + } + } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/Fakes/FakeTransactionProcessor.cs b/TA.ArduinoPowerController.Test/Fakes/FakeTransactionProcessor.cs new file mode 100644 index 0000000..f691f27 --- /dev/null +++ b/TA.ArduinoPowerController.Test/Fakes/FakeTransactionProcessor.cs @@ -0,0 +1,36 @@ +// This file is part of the TA.NexDome.AscomServer project +// Copyright © 2019-2019 Tigra Astronomy, all rights reserved. + +namespace TA.NexDome.Specifications.Fakes + { + using System.Collections.Generic; + using System.Linq; + + using TA.Ascom.ReactiveCommunications; + + class FakeTransactionProcessor : ITransactionProcessor + { + readonly IEnumerable fakeResponses; + + readonly IEnumerator responseEnumerator; + + public FakeTransactionProcessor(IEnumerable fakeResponses) + { + var enumerable = fakeResponses.ToList(); + this.fakeResponses = enumerable; + responseEnumerator = enumerable.GetEnumerator(); + } + + public List ProcessedTransactions { get; } = new List(); + + public void CommitTransaction(DeviceTransaction transaction) + { + bool moreResponses = responseEnumerator.MoveNext(); + if (moreResponses) + transaction.SimulateCompletionWithResponse(responseEnumerator.Current); + else + transaction.TimedOut("Timeout"); + ProcessedTransactions.Add(transaction); + } + } + } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/Fakes/TestableDeviceTransaction.cs b/TA.ArduinoPowerController.Test/Fakes/TestableDeviceTransaction.cs new file mode 100644 index 0000000..e0c85d3 --- /dev/null +++ b/TA.ArduinoPowerController.Test/Fakes/TestableDeviceTransaction.cs @@ -0,0 +1,42 @@ +// This file is part of the TA.NexDome.AscomServer project +// Copyright © 2019-2019 Tigra Astronomy, all rights reserved. + +namespace TA.NexDome.Specifications.Fakes + { + using System; + + using TA.Ascom.ReactiveCommunications; + using TA.Utils.Core; + + class TestableDeviceTransaction : DeviceTransaction + { + readonly DeviceTransaction sourceTransaction; + + public TestableDeviceTransaction(DeviceTransaction sourceTransaction) + : base(sourceTransaction.Command) + { + this.sourceTransaction = sourceTransaction; + } + + public override void ObserveResponse(IObservable source) + { + throw new NotImplementedException(); + } + + void SetResponse(string response) + { + Response = Maybe.From(response); + } + + internal void SignalCompletion(string fakeResponse) + { + SetResponse(fakeResponse); + OnCompleted(); + } + + public void SignalError(string error) + { + OnError(new TimeoutException(error)); + } + } + } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/Fakes/TransactionExtensions.cs b/TA.ArduinoPowerController.Test/Fakes/TransactionExtensions.cs new file mode 100644 index 0000000..145f0ca --- /dev/null +++ b/TA.ArduinoPowerController.Test/Fakes/TransactionExtensions.cs @@ -0,0 +1,85 @@ +// This file is part of the TA.NexDome.AscomServer project +// Copyright © 2019-2019 Tigra Astronomy, all rights reserved. + +namespace TA.NexDome.Specifications.Fakes + { + using System; + using System.Reflection; + + using JetBrains.Annotations; + + using TA.Ascom.ReactiveCommunications; + using TA.Utils.Core; + + /// + /// Extension methods for manipulating non-public members of transaction classes. + /// + static class TransactionExtensions + { + /// + /// Sets the protected response property. + /// + /// The transaction. + /// The response string, which can be null or empty. + /// + public static void SetResponse(this DeviceTransaction transaction, [CanBeNull] string response) + { + var maybeResponse = Maybe.From(response); + var transactionType = typeof(DeviceTransaction); + var responseProperty = transactionType.GetProperty("Response"); + responseProperty.SetValue( + transaction, + maybeResponse, + BindingFlags.Instance | BindingFlags.NonPublic, + null, + null, + null); + } + + /// + /// Marks a transaction as completed with the supplied response string, which will release any waiting threads. + /// + /// The transaction. + /// The response. + public static void SimulateCompletionWithResponse(this DeviceTransaction transaction, [NotNull] string response) + { + transaction.SetResponse(response); + var transactionType = transaction.GetType(); + var makeHotMethod = transactionType.GetMethod("MakeHot", BindingFlags.Instance | BindingFlags.NonPublic); + makeHotMethod.Invoke( + transaction, + BindingFlags.NonPublic | BindingFlags.Instance, + Type.DefaultBinder, + new object[] { }, + null); + var onCompletedMethod = transactionType.GetMethod( + "OnCompleted", + BindingFlags.Instance | BindingFlags.NonPublic); + onCompletedMethod.Invoke( + transaction, + BindingFlags.NonPublic | BindingFlags.Instance, + Type.DefaultBinder, + new object[] { }, + null); + } + + /// + /// Marks a transaction as Failed and provides a as the source of failure. + /// This also completes the transaction and releases any waiting threads. + /// + /// The transaction. + /// The message. + public static void TimedOut(this DeviceTransaction transaction, string message = null) + { + var exception = new TimeoutException(message ?? "Timeout"); + var type = transaction.GetType(); + var onErrorMethod = type.GetMethod("OnError", BindingFlags.Instance | BindingFlags.NonPublic); + onErrorMethod.Invoke( + transaction, + BindingFlags.NonPublic | BindingFlags.Instance, + Type.DefaultBinder, + new object[] { exception }, + null); + } + } + } \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/LogSetup.cs b/TA.ArduinoPowerController.Test/LogSetup.cs new file mode 100644 index 0000000..d28990a --- /dev/null +++ b/TA.ArduinoPowerController.Test/LogSetup.cs @@ -0,0 +1,35 @@ +using JetBrains.Annotations; +using Machine.Specifications; +using NLog; +using NLog.Config; +using NLog.Targets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TA.Utils.Core.Diagnostics; + +namespace TA.ArduinoPowerController.Test + { + [UsedImplicitly] + public class LogSetup : IAssemblyContext + { + static ILog log; + + public void OnAssemblyStart() + { + var configuration = new LoggingConfiguration(); + var unitTestRunnerTarget = new TraceTarget(); + unitTestRunnerTarget.RawWrite = true; + configuration.AddTarget("Unit test runner", unitTestRunnerTarget); + var logEverything = new LoggingRule("*", LogLevel.Debug, unitTestRunnerTarget); + configuration.LoggingRules.Add(logEverything); + LogManager.Configuration = configuration; + log = new TA.Utils.Logging.NLog.LoggingService(); + log.Info().Message("Logging initialized").Write(); + } + + public void OnAssemblyComplete() { } + } + } diff --git a/TA.ArduinoPowerController.Switch.Specifications/Properties/AssemblyInfo.cs b/TA.ArduinoPowerController.Test/Properties/AssemblyInfo.cs similarity index 77% rename from TA.ArduinoPowerController.Switch.Specifications/Properties/AssemblyInfo.cs rename to TA.ArduinoPowerController.Test/Properties/AssemblyInfo.cs index 845e71d..dc4578a 100644 --- a/TA.ArduinoPowerController.Switch.Specifications/Properties/AssemblyInfo.cs +++ b/TA.ArduinoPowerController.Test/Properties/AssemblyInfo.cs @@ -2,34 +2,34 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following +// General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("ASCOM.K8056.Switch.Specifications")] +[assembly: AssemblyTitle("TA.ArduinoPowerController.Test")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ASCOM.K8056.Switch.Specifications")] -[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyProduct("TA.ArduinoPowerController.Test")] +[assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c006703f-2193-481d-aa15-f46d2fb2a21b")] +[assembly: Guid("4c2ba771-da96-4922-ac98-e68f202b9b47")] // Version information for an assembly consists of the following four values: // // Major Version -// Minor Version +// Minor Version // Build Number // Revision // -// You can specify all the values or you can default the Build and Revision Numbers +// You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] diff --git a/TA.ArduinoPowerController.Test/TA.ArduinoPowerController.Test.csproj b/TA.ArduinoPowerController.Test/TA.ArduinoPowerController.Test.csproj new file mode 100644 index 0000000..9b63c96 --- /dev/null +++ b/TA.ArduinoPowerController.Test/TA.ArduinoPowerController.Test.csproj @@ -0,0 +1,84 @@ + + + + + Debug + AnyCPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47} + Library + Properties + TA.ArduinoPowerController.Test + TA.ArduinoPowerController.Test + v4.8 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.0.0 + + + 1.0.0 + + + 1.4.0 + + + 1.3.0 + + + + + {3015f05f-d747-4c3e-a0d4-ec2b3bd76c56} + TA.ArduinoPowerController.DeviceInterface + + + + + + + \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/TA.ArduinoPowerController.Test.csproj.DotSettings b/TA.ArduinoPowerController.Test/TA.ArduinoPowerController.Test.csproj.DotSettings new file mode 100644 index 0000000..85113d1 --- /dev/null +++ b/TA.ArduinoPowerController.Test/TA.ArduinoPowerController.Test.csproj.DotSettings @@ -0,0 +1,354 @@ + + SUGGESTION + SUGGESTION + True + Implicit + Implicit + <?xml version="1.0" encoding="utf-16"?> +<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> + <FilePattern RemoveRegions="AllExceptGenerated"> + <Region Name=" Context base classes"> + <Entry DisplayName="with_"> + <Entry.Match> + <And> + <Kind Is="Class" /> + <Name Is="^with_.+$" IgnoreCase="True" /> + </And> + </Entry.Match> + </Entry> + </Region> + <Entry DisplayName="when_"> + <Entry.Match> + <And> + <Kind Is="Class" /> + <Or> + <HasAttribute Name="^Subject$" /> + <Name Is="^when_.+$" IgnoreCase="True" /> + </Or> + </And> + </Entry.Match> + </Entry> + </FilePattern> + <TypePattern DisplayName="Machine.Specifications"> + <TypePattern.Match> + <And> + <Kind Is="Class" /> + <Not> + <Access Is="Private" /> + </Not> + <Or> + <HasAttribute Name="^Subject(Attribute)?$" /> + <Name Is="^with_.+$" IgnoreCase="True" /> + <Name Is="^when_.+$" IgnoreCase="True" /> + </Or> + </And> + </TypePattern.Match> + <Entry DisplayName="Establish context"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Kind Is="Field" /> + <Name Is="^context$" IgnoreCase="True" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Because of"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Kind Is="Field" /> + <Name Is="^of$" IgnoreCase="True" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="It should"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Kind Is="Field" /> + <Name Is="^should_.+$" IgnoreCase="True" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Cleanup after"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Kind Is="Field" /> + <Name Is="^after$" IgnoreCase="True" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static fields"> + <Entry.Match> + <And> + <Access Is="Any" /> + <Static /> + <Kind Is="Field" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Access /> + <Name Is="Enter Pattern Here" /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Other fields"> + <Entry.Match> + <Kind Is="Field" /> + </Entry.Match> + </Entry> + </TypePattern> +</Patterns> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private, Protected, ProtectedInternal, Internal, Public" Description="MSpec context base class"><ElementKinds /></Descriptor><Policy Inspect="True" Prefix="with_" Suffix="" Style="aa_bb" /></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private, Protected, ProtectedInternal, Internal, Public" Description="MSpec context class"><ElementKinds /></Descriptor><Policy Inspect="True" Prefix="when_" Suffix="" Style="aa_bb" /></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private, Protected, ProtectedInternal, Internal, Public" Description="MSpec supporting field"><ElementKinds /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private, Protected, ProtectedInternal, Internal, Public" Description="MSpec specification"><ElementKinds /></Descriptor><Policy Inspect="True" Prefix="should_" Suffix="" Style="aa_bb" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + LIVE_MONITOR + LIVE_MONITOR + DO_NOTHING + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + DO_NOTHING + LIVE_MONITOR + True + True + True + Imported 19/05/2016 + MSpec + Short template for MSpec BDD specification + True + 2 + True + 1 + True + 0 + True + True + 2.0 + InCSharpTypeAndNamespace + specs + True + [Subject(typeof($Subject$))] +public class when_$Specification$ +{ + It should_$Behaviour$$END$; +} + True + True + MSpec + cs + Specs + True + Specs + True + 2 + True + 3 + True + fileheader() + -1 + 4 + True + getProjectName() + 0 + True + 1 + True + InCSharpProjectFile + True + $FileHeader$ + +using Machine.Specifications; + +namespace $Namespace$ +{ +[Subject(typeof($Type$), "$Concern$")] +internal class when_$Context$ +{ +$END$ +It should_make_assertions; +} +} + True + True + MSpec + Live template for MSpec 'Because' delegate + True + 2.0 + InCSharpTypeMember + because + True + Because of = () => $END$; + True + True + MSpec + Catch.Exception + True + True + 2.0 + InCSharpFile + True + Exception = Catch.Exception(() => { $SELECTION$ }); + True + True + MSpec + Live template for MSpec 'It' delegate + True + JoarOyenLiveTemplateMacros.ValidIdentifier() + 0 + True + 2.0 + InCSharpTypeMember + it + True + It should_$do$ = () => $END$; + True + True + Imported 19/05/2016 + MSpec + Short template for MSpec BDD context + True + 0 + True + True + 2.0 + InCSharpTypeAndNamespace + specc + True + public abstract class with_$Context$ +{ + Establish context = () => + { + $END$ + }; +} + True + True + Imported 19/05/2016 + MSpec + Template for MSpec BDD specification + True + 3 + True + 4 + True + 2 + True + 1 + True + 0 + True + True + 2.0 + InCSharpTypeAndNamespace + spect + True + [Subject(typeof($Subject$), "$Concern$")] +public class when_$Specification$ : with_$Context$ +{ + Because of = () => + { + + }; + + It should_$Behaviour$$END$; +} + True + True + MSpec + Live template for MSpec 'Establish' delegate + True + 2.0 + InCSharpTypeMember + establish + True + Establish context = () => $END$; + True + True + Imported 19/05/2016 + MSpec + Full template for MSpec BDD specification + True + 2 + True + 1 + True + JoarOyenLiveTemplateMacros.ValidIdentifier() + 3 + True + 0 + True + True + 2.0 + InCSharpTypeAndNamespace + spec + True + [Subject(typeof($Subject$), "$Concern$")] +public class when_$Context$ + { + Establish context ; + Because of ; + It should_$Behaviour$$END$; + } + True + True + MSpec + Assert that an exception of the expected type was thrown + True + completeType("System.Exception") + 0 + True + True + 2.0 + InCSharpFile + should-throw + True + It should_throw = () => Exception.ShouldBeOfExactType<$exception$>(); +static Exception Exception; \ No newline at end of file diff --git a/TA.ArduinoPowerController.Test/TestContexts/CommunicationsChannelContext.cs b/TA.ArduinoPowerController.Test/TestContexts/CommunicationsChannelContext.cs new file mode 100644 index 0000000..79afb5d --- /dev/null +++ b/TA.ArduinoPowerController.Test/TestContexts/CommunicationsChannelContext.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TA.Ascom.ReactiveCommunications; +using TA.NexDome.Specifications.Fakes; + +namespace TA.ArduinoPowerController.Test.TestContexts + { + internal sealed class CommunicationsChannelContext + { + public ICommunicationChannel Channel { get; set; } + + public FakeCommunicationChannel FakeChannel => Channel as FakeCommunicationChannel; + + public DeviceEndpoint Endpoint => Channel.Endpoint; + + public ReactiveTransactionProcessor Processor { get; set; } + + public ChannelFactory Factory { get; set; } + + public TransactionObserver Observer { get; set; } + } + } diff --git a/TA.ArduinoPowerController.Test/TestContexts/CommunicationsChannelContextBuilder.cs b/TA.ArduinoPowerController.Test/TestContexts/CommunicationsChannelContextBuilder.cs new file mode 100644 index 0000000..68bf626 --- /dev/null +++ b/TA.ArduinoPowerController.Test/TestContexts/CommunicationsChannelContextBuilder.cs @@ -0,0 +1,32 @@ +using TA.Ascom.ReactiveCommunications; +using TA.NexDome.Specifications.Fakes; + +namespace TA.ArduinoPowerController.Test.TestContexts + { + class CommunicationsChannelContextBuilder + { + CommunicationsChannelContext context = new CommunicationsChannelContext(); + string connectionString = "Fake"; + string fakeResponse = string.Empty; + + internal CommunicationsChannelContextBuilder WithFakeResponse(string response) + { + connectionString = $"Fake:{response}"; + return this; + } + + internal CommunicationsChannelContext Build() + { + var logService = new TA.Utils.Logging.NLog.LoggingService(); + context.Factory = new ChannelFactory(logService); + context.Factory.RegisterChannelType(FakeEndpoint.IsConnectionStringValid, FakeEndpoint.FromConnectionString, FakeCommunicationChannel.FromEndpoint); + context.Channel = context.Factory.FromConnectionString(connectionString); + context.Observer = new TransactionObserver(context.Channel); + var processor = new ReactiveTransactionProcessor(); + processor.SubscribeTransactionObserver(context.Observer); + context.Processor = processor; + context.Channel.Open(); + return context; + } + } + } diff --git a/TA.ArduinoPowerController.Test/TestContexts/with_fake_communications_channel.cs b/TA.ArduinoPowerController.Test/TestContexts/with_fake_communications_channel.cs new file mode 100644 index 0000000..f57cbee --- /dev/null +++ b/TA.ArduinoPowerController.Test/TestContexts/with_fake_communications_channel.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Machine.Specifications; + +namespace TA.ArduinoPowerController.Test.TestContexts + { + internal class with_fake_communications_channel + { + Establish context = () => + { + Builder = new CommunicationsChannelContextBuilder(); + }; + + Cleanup after = () => + { + Context.Processor.Dispose(); + Context.Channel?.Close(); + Context.Channel?.Dispose(); + Context = null; + Builder = null; + }; + + public static CommunicationsChannelContextBuilder Builder { get; set; } + public static CommunicationsChannelContext Context { get; set; } + } + } diff --git a/TA.ArduinoPowerController.sln b/TA.ArduinoPowerController.sln index 0ee939e..3899f1f 100644 --- a/TA.ArduinoPowerController.sln +++ b/TA.ArduinoPowerController.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.31911.260 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TA.ArduinoPowerController.DeviceInterface", "TA.ArduinoPowerController.DeviceInterface\TA.ArduinoPowerController.DeviceInterface.csproj", "{3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}" EndProject @@ -12,65 +12,98 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Shared Items", "Solution Shared Items", "{2D14AE63-4259-4517-8C0B-B5A2A6D47007}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore - GlobalAssemblyInfo.cs = GlobalAssemblyInfo.cs ReadMe.md = ReadMe.md WalkingSwitches.ps1 = WalkingSwitches.ps1 EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TA.ArduinoPowerController.AscomSwitch", "TA.ArduinoPowerController.Switch\TA.ArduinoPowerController.AscomSwitch.csproj", "{ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}" -EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "TA.ArduinoPowerController.Setup", "TA.ArduinoPowerController.Setup\TA.ArduinoPowerController.Setup.wixproj", "{B847A048-C924-4134-BD60-C111B0D37C8B}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TA.ArduinoPowerController.SharedCode", "TA.ArduinoPowerController.SharedCode\TA.ArduinoPowerController.SharedCode.shproj", "{6778254B-A2CD-4364-BF2B-249B59285136}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TA.ArduinoPowerController.Test", "TA.ArduinoPowerController.Test\TA.ArduinoPowerController.Test.csproj", "{4C2BA771-DA96-4922-AC98-E68F202B9B47}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "images", "images", "{B767F022-7A39-4D9C-AC2B-ADE7D378FA68}" + ProjectSection(SolutionItems) = preProject + images\hardware.png = images\hardware.png + EndProjectSection +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + TA.ArduinoPowerController.SharedCode\TA.ArduinoPowerController.SharedCode.projitems*{3015f05f-d747-4c3e-a0d4-ec2b3bd76c56}*SharedItemsImports = 4 + TA.ArduinoPowerController.SharedCode\TA.ArduinoPowerController.SharedCode.projitems*{3689a2cb-94c5-4012-a5cf-7e7d1dd27143}*SharedItemsImports = 4 + TA.ArduinoPowerController.SharedCode\TA.ArduinoPowerController.SharedCode.projitems*{6778254b-a2cd-4364-bf2b-249b59285136}*SharedItemsImports = 13 + TA.ArduinoPowerController.SharedCode\TA.ArduinoPowerController.SharedCode.projitems*{9cdcf319-dadc-41eb-b787-de3862017e95}*SharedItemsImports = 4 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Debug|Any CPU.Build.0 = Debug|Any CPU {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Debug|x64.ActiveCfg = Debug|Any CPU {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Debug|x64.Build.0 = Debug|Any CPU {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Debug|x86.ActiveCfg = Debug|Any CPU {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Debug|x86.Build.0 = Debug|Any CPU + {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Release|Any CPU.Build.0 = Release|Any CPU {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Release|x64.ActiveCfg = Release|Any CPU {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Release|x64.Build.0 = Release|Any CPU {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Release|x86.ActiveCfg = Release|Any CPU {3015F05F-D747-4C3E-A0D4-EC2B3BD76C56}.Release|x86.Build.0 = Release|Any CPU + {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Debug|Any CPU.Build.0 = Debug|Any CPU {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Debug|x64.ActiveCfg = Debug|Any CPU {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Debug|x64.Build.0 = Debug|Any CPU {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Debug|x86.ActiveCfg = Debug|Any CPU {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Debug|x86.Build.0 = Debug|Any CPU + {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Release|Any CPU.Build.0 = Release|Any CPU {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Release|x64.ActiveCfg = Release|Any CPU {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Release|x64.Build.0 = Release|Any CPU {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Release|x86.ActiveCfg = Release|Any CPU {3689A2CB-94C5-4012-A5CF-7E7D1DD27143}.Release|x86.Build.0 = Release|Any CPU + {9CDCF319-DADC-41EB-B787-DE3862017E95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9CDCF319-DADC-41EB-B787-DE3862017E95}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CDCF319-DADC-41EB-B787-DE3862017E95}.Debug|x64.ActiveCfg = Debug|Any CPU {9CDCF319-DADC-41EB-B787-DE3862017E95}.Debug|x64.Build.0 = Debug|Any CPU {9CDCF319-DADC-41EB-B787-DE3862017E95}.Debug|x86.ActiveCfg = Debug|Any CPU {9CDCF319-DADC-41EB-B787-DE3862017E95}.Debug|x86.Build.0 = Debug|Any CPU + {9CDCF319-DADC-41EB-B787-DE3862017E95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9CDCF319-DADC-41EB-B787-DE3862017E95}.Release|Any CPU.Build.0 = Release|Any CPU {9CDCF319-DADC-41EB-B787-DE3862017E95}.Release|x64.ActiveCfg = Release|Any CPU {9CDCF319-DADC-41EB-B787-DE3862017E95}.Release|x64.Build.0 = Release|Any CPU {9CDCF319-DADC-41EB-B787-DE3862017E95}.Release|x86.ActiveCfg = Release|Any CPU {9CDCF319-DADC-41EB-B787-DE3862017E95}.Release|x86.Build.0 = Release|Any CPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}.Debug|x64.ActiveCfg = Debug|Any CPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}.Debug|x64.Build.0 = Debug|Any CPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}.Debug|x86.ActiveCfg = Debug|Any CPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}.Debug|x86.Build.0 = Debug|Any CPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}.Release|x64.ActiveCfg = Release|Any CPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}.Release|x64.Build.0 = Release|Any CPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}.Release|x86.ActiveCfg = Release|Any CPU - {ED0FE6AA-E1E5-4D21-9B5E-802D50EDCCE1}.Release|x86.Build.0 = Release|Any CPU + {B847A048-C924-4134-BD60-C111B0D37C8B}.Debug|Any CPU.ActiveCfg = Debug|x86 {B847A048-C924-4134-BD60-C111B0D37C8B}.Debug|x64.ActiveCfg = Debug|x64 - {B847A048-C924-4134-BD60-C111B0D37C8B}.Debug|x64.Build.0 = Debug|x64 {B847A048-C924-4134-BD60-C111B0D37C8B}.Debug|x86.ActiveCfg = Debug|x86 - {B847A048-C924-4134-BD60-C111B0D37C8B}.Debug|x86.Build.0 = Debug|x86 + {B847A048-C924-4134-BD60-C111B0D37C8B}.Release|Any CPU.ActiveCfg = Release|x86 {B847A048-C924-4134-BD60-C111B0D37C8B}.Release|x64.ActiveCfg = Release|x64 {B847A048-C924-4134-BD60-C111B0D37C8B}.Release|x64.Build.0 = Release|x64 {B847A048-C924-4134-BD60-C111B0D37C8B}.Release|x86.ActiveCfg = Release|x86 {B847A048-C924-4134-BD60-C111B0D37C8B}.Release|x86.Build.0 = Release|x86 + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Debug|x64.ActiveCfg = Debug|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Debug|x64.Build.0 = Debug|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Debug|x86.ActiveCfg = Debug|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Debug|x86.Build.0 = Debug|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Release|Any CPU.Build.0 = Release|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Release|x64.ActiveCfg = Release|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Release|x64.Build.0 = Release|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Release|x86.ActiveCfg = Release|Any CPU + {4C2BA771-DA96-4922-AC98-E68F202B9B47}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CA042B53-2A8D-4E52-8866-82EAA3672D22} + EndGlobalSection EndGlobal diff --git a/TA.ArduinoPowerController.sln.DotSettings b/TA.ArduinoPowerController.sln.DotSettings deleted file mode 100644 index ce1dd75..0000000 --- a/TA.ArduinoPowerController.sln.DotSettings +++ /dev/null @@ -1,163 +0,0 @@ - - True - Explicit - Explicit - <?xml version="1.0" encoding="utf-16"?> -<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> - <TypePattern DisplayName="COM interfaces or structs"> - <TypePattern.Match> - <Or> - <And> - <Kind Is="Interface" /> - <Or> - <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> - <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> - </Or> - </And> - <Kind Is="Struct" /> - </Or> - </TypePattern.Match> - </TypePattern> - <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> - <TypePattern.Match> - <And> - <Kind Is="Class" /> - <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.TestCaseFixtureAttribute" Inherited="True" /> - </And> - </TypePattern.Match> - <Entry DisplayName="Setup/Teardown Methods"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <Or> - <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /> - </Or> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="All other members" /> - <Entry Priority="100" DisplayName="Test Methods"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <HasAttribute Name="NUnit.Framework.TestAttribute" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> - </TypePattern> - <TypePattern DisplayName="Default Pattern"> - <Entry Priority="100" DisplayName="Public Delegates"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Kind Is="Delegate" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> - <Entry Priority="100" DisplayName="Public Enums"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Kind Is="Enum" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Static Fields and Constants"> - <Entry.Match> - <Or> - <Kind Is="Constant" /> - <And> - <Kind Is="Field" /> - <Static /> - </And> - </Or> - </Entry.Match> - <Entry.SortBy> - <Kind Order="Constant Field" /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Fields"> - <Entry.Match> - <And> - <Kind Is="Field" /> - <Not> - <Static /> - </Not> - </And> - </Entry.Match> - <Entry.SortBy> - <Readonly /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Constructors"> - <Entry.Match> - <Kind Is="Constructor" /> - </Entry.Match> - <Entry.SortBy> - <Static /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Properties, Indexers"> - <Entry.Match> - <Or> - <Kind Is="Property" /> - <Kind Is="Indexer" /> - </Or> - </Entry.Match> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> - <Entry Priority="100" DisplayName="Interface Implementations"> - <Entry.Match> - <And> - <Kind Is="Member" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="All other members"> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Nested Types"> - <Entry.Match> - <Kind Is="Type" /> - </Entry.Match> - </Entry> - </TypePattern> -</Patterns> - This file is part of the $SOLUTION$ project - -Copyright © 2016-$CURRENT_YEAR$ Tigra Astronomy, all rights reserved. -Licensed under the MIT license, see http://tigra.mit-license.org/ - -File: $FILENAME$ Last modified: $CURRENT_YEAR$-$CURRENT_MONTH$-$CURRENT_DAY$@$CURRENT_TIME$ by $USER_NAME$ - True - D:\VS-Projects\ASCOM.K8056.Switch\ASCOM.K8056.Switch.Specifications\ASCOM.K8056.Switch.Specifications.DotSettings - ..\ASCOM.K8056.Switch.Specifications\ASCOM.K8056.Switch.Specifications.DotSettings - True - False - 1 - True - True - AUTO \ No newline at end of file diff --git a/TA.PostSharp.Aspects/MustBeConnectedAttribute.cs b/TA.PostSharp.Aspects/MustBeConnectedAttribute.cs index 9a4a362..5b9bfbf 100644 --- a/TA.PostSharp.Aspects/MustBeConnectedAttribute.cs +++ b/TA.PostSharp.Aspects/MustBeConnectedAttribute.cs @@ -1,9 +1,9 @@ // This file is part of the TA.ArduinoPowerController project -// -// Copyright © 2016-2017 Tigra Astronomy, all rights reserved. -// Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: MustBeConnectedAttribute.cs Last modified: 2017-03-16@23:34 by Tim Long +// +// Copyright © 2016-2019 Tigra Astronomy, all rights reserved. +// Licensed under the Tigra MIT license, see http://tigra.mit-license.org/ +// +// File: MustBeConnectedAttribute.cs Last modified: 2019-09-08@05:25 by Tim Long using System; using System.Reflection; @@ -13,7 +13,7 @@ using PostSharp.Extensibility; namespace TA.PostSharp.Aspects - { +{ /// /// MustBeConnected aspect. Verifies that the controlled device is connected and if not, /// throws a @@ -22,29 +22,19 @@ namespace TA.PostSharp.Aspects [Serializable] [ProvideAspectRole("ASCOM")] public sealed class MustBeConnectedAttribute : OnMethodBoundaryAspect - { + { private static int nesting; - /// - /// Initializes a new instance of the class. Forces - /// the property to false, as this aspect should only - /// check advices upon first entry to any method. - /// - public MustBeConnectedAttribute() - { - ApplyToStateMachine = false; - } - public override bool CompileTimeValidate(MethodBase method) - { + { var targetType = method.DeclaringType; if (!typeof(IAscomDriver).IsAssignableFrom(targetType)) - { + { throw new InvalidAnnotationException( "This aspect can only be applied to members of types that implement IAscomDriver"); - } - return base.CompileTimeValidate(method); } + return base.CompileTimeValidate(method); + } /// /// Verifies that the device is connected. Throws @@ -59,17 +49,17 @@ public override bool CompileTimeValidate(MethodBase method) /// Thrown if the device is not connected. /// public override void OnEntry(MethodExecutionArgs args) - { + { base.OnEntry(args); var instance = args.Instance as IAscomDriver; if (nesting++ > 0) return; // Optimization - no need to check in nested calls. if (!instance.Connected) - { + { var name = args.Method.Name; var message = $"{name} requires that Connected is true but it was false"; throw new NotConnectedException(message); - } } + } /// /// Method executed after the body of methods to which this aspect is applied, @@ -81,9 +71,9 @@ public override void OnEntry(MethodExecutionArgs args) /// is being executed and which are its arguments. /// public override void OnExit(MethodExecutionArgs args) - { + { base.OnExit(args); nesting--; - } } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/TA.PostSharp.Aspects/Properties/AssemblyInfo.cs b/TA.PostSharp.Aspects/Properties/AssemblyInfo.cs index 504a19b..5d0e497 100644 --- a/TA.PostSharp.Aspects/Properties/AssemblyInfo.cs +++ b/TA.PostSharp.Aspects/Properties/AssemblyInfo.cs @@ -1,46 +1,11 @@ // This file is part of the TA.ArduinoPowerController project -// +// // Copyright © 2016-2017 Tigra Astronomy, all rights reserved. // Licensed under the MIT license, see http://tigra.mit-license.org/ -// -// File: AssemblyInfo.cs Last modified: 2017-03-16@23:34 by Tim Long +// +// File: AssemblyInfo.cs Last modified: 2017-03-16@23:33 by Tim Long using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. [assembly: AssemblyTitle("TA.PostSharp.Aspects")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("TA.PostSharp.Aspects")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("9cdcf319-dadc-41eb-b787-de3862017e95")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file +[assembly: AssemblyDescription("AOP Aspects for Arduino Power Controller")] \ No newline at end of file diff --git a/TA.PostSharp.Aspects/ReadMe.md b/TA.PostSharp.Aspects/ReadMe.md new file mode 100644 index 0000000..7280615 --- /dev/null +++ b/TA.PostSharp.Aspects/ReadMe.md @@ -0,0 +1,49 @@ +# About PostShapr Aspects + +This project contains a number of PostSharp "Aspects". + +"Aspect" is a term used in Apect Oriented Programming (AOP) that refers to a specially constructed class that can be injected into user code after compilation has been performed. This allows "boiler plate" code and design patterns to be added declaratively, keeping user code cleaner and simpler. + +`PostSharp` is an AOP framework and compiler for .NET projects. The `PostSharp` compiler runs after the .NET compiler and "weaves" aspects into the already compiled user code. Everything needed for this to work is contained in the `PostSharp.*` NuGet packages. There is a Visual Studio extension that adds some Intellisense and designer support, but the extension is optional and is not needed for PostSharp to work. + +Common uses for AOP are: +- to declaratively introduce logging into classes that otherwise don't have it +- to implement standard patterns patterns such as `INotifyPropertyChanged` +- to enforce certain threading models and patterns, such as Reader-Writer Lock +- to add code contracts to method calls + +It is also possible to produce user-written custom aspects. + +Aspects are implemented as .NET attributes, and applying an aspect to an assembly, class or class member is as easy as applying an attribute. For example, data models can be made to implement `INotifyPropertyChanged` simply by applying the `[NotifyPropertyChanged]` attribute to the class. + +## Solution Aspect Usage + +This solution uses the `[ReaderWriterSynchronized`] threading pattern to implement a Reader-Writer Lock within the `ClientConnectionmanager` class. It also defines some custom aspects that are useful in ASCOM drivers, such as `MustBeConnectedAttribute` and `NLogTraceWithArgumentsAttribute`. These are described in following paragraphs. + +## PostSharp Licensing + +`PostSharp Essentials` is free and provides unlimited logging and all other patterns on up to 10 classes per solution. This will often be enough for an ASCOM driver project if aspects are used judiciously, as is the case in this solution. + +If you do not wish to use PostSharp for any reason, simply uninstall the NuGet packages. You will then have to add equivalent code to your projects for any PostSharp aspects that were in use. These are documented below. + +## `ReaderWriterSynchronized` Threading Model + +This is used to protect the `ClientConnectionmanager` class from race conditions. + +If PostSharp is removed from the solution, then an alternative solution must be found. There are numberous articles on implementing the Reader-Writer Lock pattern, [including this one][ReaderWriterLock]. + +## `MustBeConnected` Aspect + +This aspect attribute can be applied to any method or property of an ASCOM driver that implements the `IAscomDriver` marker interface. When applied to a property or method, that property or method will check the value of the `Connected` property and will throw a `NotConnectedException` if it is false. + +The aspect is coded so that nested calls to properties and methods only check the `Connected` property once at the start. This was done partly for efficience and partly so that log output is more readable. + +This aspect is used in the `Switch` class on the `Action`, `GetSwitch`, `GetSwitchValue`, `SetSwitch` and `SetSwitchValue` methods. If postSharp is removed, these methods should manually check the `Connected` property. + +## `NLogTraceWithArguments` Aspect + +This aspect can be applied to any class and will inject logging of method calls (with argument values) and returns (with return values) into every property and method of that class. + +We use this to inject logging into driver classes so that all driver calls are logged, along with argument values and return values. + +[ReaderWriterLock]: https://www.c-sharpcorner.com/UploadFile/1d42da/readerwriterlock-class-in-C-Sharp-threading/ "CSharp Corner: ReaderWriterLock Class in C# Threading" \ No newline at end of file diff --git a/TA.PostSharp.Aspects/TA.PostSharp.Aspects.csproj b/TA.PostSharp.Aspects/TA.PostSharp.Aspects.csproj index bb31c43..548a4e9 100644 --- a/TA.PostSharp.Aspects/TA.PostSharp.Aspects.csproj +++ b/TA.PostSharp.Aspects/TA.PostSharp.Aspects.csproj @@ -9,11 +9,11 @@ Properties TA.PostSharp.Aspects TA.PostSharp.Aspects - v4.6.2 + v4.8 512 - True + true @@ -33,55 +33,13 @@ 4 - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Astrometry.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Attributes.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Controls.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.DeviceInterfaces.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.DriverAccess.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Exceptions.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Internal.Extensions.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.SettingsProvider.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Utilities.dll - True - - - ..\packages\ASCOM.Platform.6.3.0\lib\net40\ASCOM.Utilities.Video.dll - True - - - ..\packages\NLog.4.4.5\lib\net45\NLog.dll - - - ..\packages\PostSharp.4.3.31\lib\net35-client\PostSharp.dll - True - + + + + + @@ -96,19 +54,31 @@ + + Dependency Validation.layerdiagram + False + - - + + 6.5.2 + + + 5.9.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + 4.7.15 + + + 6.10.8 + + - - - - -