diff --git a/.gitignore b/.gitignore index f6cf4da1..6b764fc8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ [Rr]elease/ [Rr]eleases/ x64/ -build/ bld/ [Bb]in/ [Oo]bj/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..2c16fedb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "submodules/IOWrapper"] + path = submodules/IOWrapper + url = https://github.com/evilC/IOWrapper.git diff --git a/.nuke b/.nuke new file mode 100644 index 00000000..a12280b7 Binary files /dev/null and b/.nuke differ diff --git a/README.md b/README.md index 58771a31..86a3138b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Universal Control Remapper -[![GitHub release](https://img.shields.io/badge/release-v0.2.0-blue.svg)](https://github.com/Snoothy/UCR/releases/tag/v0.2.0) [![IOWrapper version](https://img.shields.io/badge/IOWrapper-v0.3.1-blue.svg)](https://github.com/evilC/IOWrapper) [![license](https://img.shields.io/github/license/snoothy/ucr.svg)](https://github.com/Snoothy/UCR/blob/master/LICENSE) [![Github All Releases](https://img.shields.io/github/downloads/snoothy/ucr/total.svg)](https://github.com/Snoothy/UCR/releases) +[![GitHub release](https://img.shields.io/badge/release-v0.3.0-blue.svg)](https://github.com/Snoothy/UCR/releases/tag/v0.3.0) [![IOWrapper version](https://img.shields.io/badge/IOWrapper-v0.5.1-blue.svg)](https://github.com/evilC/IOWrapper) [![license](https://img.shields.io/github/license/snoothy/ucr.svg)](https://github.com/Snoothy/UCR/blob/master/LICENSE) [![Github All Releases](https://img.shields.io/github/downloads/snoothy/ucr/total.svg)](https://github.com/Snoothy/UCR/releases) Universal Control Remapper is a complete rewrite of the original [UCR](https://github.com/evilC/UCR), created in collaboration with [evilC](https://github.com/evilC/). diff --git a/UCR.Core/Attributes/PluginAttribute.cs b/UCR.Core/Attributes/PluginAttribute.cs new file mode 100644 index 00000000..d203b178 --- /dev/null +++ b/UCR.Core/Attributes/PluginAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace HidWizards.UCR.Core.Attributes +{ + public class PluginAttribute : Attribute + { + private string name; + private bool disabled; + + public PluginAttribute(string name) + { + this.name = name; + disabled = false; + } + + public virtual string Name => name; + + public virtual bool Disabled + { + get => disabled; + set => disabled = value; + } + } +} diff --git a/UCR.Core/Attributes/PluginGuiAttribute.cs b/UCR.Core/Attributes/PluginGuiAttribute.cs new file mode 100644 index 00000000..4425652d --- /dev/null +++ b/UCR.Core/Attributes/PluginGuiAttribute.cs @@ -0,0 +1,33 @@ +using System; + +namespace HidWizards.UCR.Core.Attributes +{ + [AttributeUsage(AttributeTargets.Property)] + public class PluginGuiAttribute : Attribute + { + private string name; + private int rowOrder; + private int columnOrder; + + public PluginGuiAttribute(string name) + { + this.name = name; + rowOrder = 0; + columnOrder = 0; + } + + public virtual string Name => name; + + public virtual int RowOrder + { + get => rowOrder; + set => rowOrder = value; + } + + public virtual int ColumnOrder + { + get => columnOrder; + set => columnOrder = value; + } + } +} diff --git a/UCR.Core/Attributes/PluginInput.cs b/UCR.Core/Attributes/PluginInput.cs new file mode 100644 index 00000000..5ef8fe6d --- /dev/null +++ b/UCR.Core/Attributes/PluginInput.cs @@ -0,0 +1,12 @@ +using HidWizards.UCR.Core.Models; +using HidWizards.UCR.Core.Models.Binding; + +namespace HidWizards.UCR.Core.Attributes +{ + public class PluginInput : PluginIoAttribute + { + public PluginInput(DeviceBindingCategory deviceBindingCategory, string name) : base(DeviceIoType.Input, deviceBindingCategory, name) + { + } + } +} diff --git a/UCR.Core/Attributes/PluginIoAttribute.cs b/UCR.Core/Attributes/PluginIoAttribute.cs new file mode 100644 index 00000000..6525f60a --- /dev/null +++ b/UCR.Core/Attributes/PluginIoAttribute.cs @@ -0,0 +1,27 @@ +using System; +using HidWizards.UCR.Core.Models; +using HidWizards.UCR.Core.Models.Binding; + +namespace HidWizards.UCR.Core.Attributes +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class PluginIoAttribute : Attribute + { + private DeviceIoType deviceIoType; + private DeviceBindingCategory deviceBindingCategory; + private string name; + + public PluginIoAttribute(DeviceIoType deviceIoType, DeviceBindingCategory deviceBindingCategory, string name) + { + this.deviceIoType = deviceIoType; + this.deviceBindingCategory = deviceBindingCategory; + this.name = name; + } + + public virtual DeviceIoType DeviceIoType => deviceIoType; + + public virtual DeviceBindingCategory DeviceBindingCategory => deviceBindingCategory; + + public virtual string Name => name; + } +} diff --git a/UCR.Core/Attributes/PluginOutput.cs b/UCR.Core/Attributes/PluginOutput.cs new file mode 100644 index 00000000..8d062823 --- /dev/null +++ b/UCR.Core/Attributes/PluginOutput.cs @@ -0,0 +1,12 @@ +using HidWizards.UCR.Core.Models; +using HidWizards.UCR.Core.Models.Binding; + +namespace HidWizards.UCR.Core.Attributes +{ + public class PluginOutput : PluginIoAttribute + { + public PluginOutput(DeviceBindingCategory deviceBindingCategory, string name) : base(DeviceIoType.Output, deviceBindingCategory, name) + { + } + } +} diff --git a/UCR.Core/Context.cs b/UCR.Core/Context.cs index 33fb76cd..72890a9f 100644 --- a/UCR.Core/Context.cs +++ b/UCR.Core/Context.cs @@ -4,15 +4,13 @@ using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; -using IOWrapper; +using HidWizards.IOWrapper.Core; +using HidWizards.UCR.Core.Managers; +using HidWizards.UCR.Core.Models; using Mono.Options; using NLog; -using UCR.Core.Managers; -using UCR.Core.Models.Device; -using UCR.Core.Models.Plugin; -using UCR.Core.Models.Profile; -namespace UCR.Core +namespace HidWizards.UCR.Core { public class Context : IDisposable { @@ -20,12 +18,12 @@ public class Context : IDisposable private const string ContextName = "context.xml"; private const string PluginPath = "Plugins"; - // Persistence + /* Persistence */ public List Profiles { get; set; } public List InputGroups { get; set; } public List OutputGroups { get; set; } - // Runtime + /* Runtime */ [XmlIgnore] public Profile ActiveProfile { get; set; } [XmlIgnore] @@ -36,11 +34,12 @@ public class Context : IDisposable public DeviceGroupsManager DeviceGroupsManager { get; set; } [XmlIgnore] public SubscriptionsManager SubscriptionsManager { get; set; } + [XmlIgnore] + public PluginsManager PluginManager { get; set; } internal bool IsNotSaved { get; private set; } internal IOController IOController { get; set; } internal readonly List ActiveProfileCallbacks = new List(); - private PluginLoader _pluginLoader; private OptionSet options; public Context() @@ -61,7 +60,7 @@ private void Init() DevicesManager = new DevicesManager(this); DeviceGroupsManager = new DeviceGroupsManager(this, InputGroups, OutputGroups); SubscriptionsManager = new SubscriptionsManager(this); - _pluginLoader = new PluginLoader(PluginPath); + PluginManager = new PluginsManager(PluginPath); } private void SetCommandLineOptions() @@ -91,7 +90,7 @@ public void SetActiveProfileCallback(Action profileActivated) public List GetPlugins() { - return _pluginLoader.Plugins; + return PluginManager.Plugins.Where(p => !p.IsDisabled).ToList(); } public void ContextChanged() @@ -148,7 +147,7 @@ private static XmlSerializer GetXmlSerializer(List additionalPluginTypes) private static XmlSerializer GetXmlSerializer(List additionalPluginTypes, Type type) { - var plugins = new PluginLoader(PluginPath); + var plugins = new PluginsManager(PluginPath); var pluginTypes = plugins.Plugins.Select(p => p.GetType()).ToList(); if (additionalPluginTypes != null) pluginTypes.AddRange(additionalPluginTypes); return new XmlSerializer(type, pluginTypes.ToArray()); diff --git a/UCR.Core/Managers/DeviceGroupsManager.cs b/UCR.Core/Managers/DeviceGroupsManager.cs index e9518a7b..2f955abc 100644 --- a/UCR.Core/Managers/DeviceGroupsManager.cs +++ b/UCR.Core/Managers/DeviceGroupsManager.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using UCR.Core.Models.Device; +using HidWizards.UCR.Core.Models; -namespace UCR.Core.Managers +namespace HidWizards.UCR.Core.Managers { public class DeviceGroupsManager { diff --git a/UCR.Core/Managers/DevicesManager.cs b/UCR.Core/Managers/DevicesManager.cs index cab6b691..7ebc57f0 100644 --- a/UCR.Core/Managers/DevicesManager.cs +++ b/UCR.Core/Managers/DevicesManager.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; -using UCR.Core.Models.Binding; -using UCR.Core.Models.Device; +using HidWizards.UCR.Core.Models; -namespace UCR.Core.Managers +namespace HidWizards.UCR.Core.Managers { public class DevicesManager { diff --git a/UCR.Core/Models/Plugin/PluginLoader.cs b/UCR.Core/Managers/PluginsManager.cs similarity index 71% rename from UCR.Core/Models/Plugin/PluginLoader.cs rename to UCR.Core/Managers/PluginsManager.cs index 9698d2b7..b2d8fdab 100644 --- a/UCR.Core/Models/Plugin/PluginLoader.cs +++ b/UCR.Core/Managers/PluginsManager.cs @@ -1,18 +1,20 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.IO; +using HidWizards.UCR.Core.Models; -namespace UCR.Core.Models.Plugin +namespace HidWizards.UCR.Core.Managers { - internal class PluginLoader + public class PluginsManager { private CompositionContainer _Container; [ImportMany(typeof(Plugin))] public List Plugins { get; set; } - public PluginLoader(string basePath) + public PluginsManager(string basePath) { var catalog = new AggregateCatalog(); @@ -28,5 +30,10 @@ public PluginLoader(string basePath) _Container = new CompositionContainer(catalog); _Container.ComposeParts(this); } + + public Plugin GetNewPlugin(Plugin plugin) + { + return (Plugin)Activator.CreateInstance(plugin.GetType()); + } } } diff --git a/UCR.Core/Managers/ProfilesManager.cs b/UCR.Core/Managers/ProfilesManager.cs index 75e2e162..17a5e8ed 100644 --- a/UCR.Core/Managers/ProfilesManager.cs +++ b/UCR.Core/Managers/ProfilesManager.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using HidWizards.UCR.Core.Models; using NLog; -using UCR.Core.Models.Profile; -namespace UCR.Core.Managers +namespace HidWizards.UCR.Core.Managers { public class ProfilesManager { @@ -18,6 +18,11 @@ public ProfilesManager(Context context, List profiles) _profiles = profiles; } + public Profile CreateProfile() + { + return Profile.CreateProfile(_context, ""); + } + public bool AddProfile(string title) { _profiles.Add(Profile.CreateProfile(_context, title)); @@ -25,6 +30,21 @@ public bool AddProfile(string title) return true; } + public bool AddProfile(Profile newProfile, Profile parentProfile = null) + { + if (parentProfile != null) + { + parentProfile.AddChildProfile(newProfile); + } + else + { + _profiles.Add(newProfile); + } + + _context.ContextChanged(); + return true; + } + public bool CopyProfile(Profile profile, string title = "Untitled") { var newProfile = Context.DeepXmlClone(profile); diff --git a/UCR.Core/Managers/SubscriptionsManager.cs b/UCR.Core/Managers/SubscriptionsManager.cs index e980b34d..f351d8e5 100644 --- a/UCR.Core/Managers/SubscriptionsManager.cs +++ b/UCR.Core/Managers/SubscriptionsManager.cs @@ -1,13 +1,13 @@ using System; +using System.Collections.Generic; +using HidWizards.IOWrapper.DataTransferObjects; +using HidWizards.UCR.Core.Models; +using HidWizards.UCR.Core.Models.Binding; +using HidWizards.UCR.Core.Models.Subscription; using NLog; -using Providers; -using UCR.Core.Models.Binding; -using UCR.Core.Models.Device; -using UCR.Core.Models.Profile; -using UCR.Core.Models.Subscription; using Logger = NLog.Logger; -namespace UCR.Core.Managers +namespace HidWizards.UCR.Core.Managers { public class SubscriptionsManager : IDisposable { @@ -70,23 +70,24 @@ public bool DeactivateProfile() if (!state.IsActive) return true; - foreach (var deviceBindingSubscriptionsGroup in state.DeviceBindingSubscriptions) + foreach (var mappingSubscription in state.MappingSubscriptions) { - foreach (var subscription in deviceBindingSubscriptionsGroup.Value) + if (mappingSubscription.Overriden) continue; + + foreach (var deviceBindingSubscription in mappingSubscription.DeviceBindingSubscriptions) { - if (subscription.IsOverwritten) continue; - success &= UnsubscribeDeviceBindingInput(state, subscription); + success &= UnsubscribeDeviceBindingInput(state, deviceBindingSubscription); } - } - foreach (var deviceSubscription in state.DeviceSubscriptions) - { - success &= UnsubscribeOutput(state, deviceSubscription); + foreach (var pluginSubscription in mappingSubscription.PluginSubscriptions) + { + pluginSubscription.Plugin.OnDeactivate(); + } } - foreach (var plugin in state.ActivePlugins) + foreach (var deviceSubscription in state.OutputDeviceSubscriptions) { - plugin.OnDeactivate(); + success &= UnsubscribeOutput(state, deviceSubscription); } _context.IOController.SetProfileState(state.StateGuid, false); @@ -106,67 +107,83 @@ public bool DeactivateProfile() private bool PopulateSubscriptionStateForProfile(SubscriptionState state, Profile profile) { var success = true; + profile.PrepareProfile(); + if (profile.ParentProfile != null) { success &= PopulateSubscriptionStateForProfile(state, profile.ParentProfile); } // Output devices + var profileOutputDevices = new List(); var outputDeviceGroup = _context.DeviceGroupsManager.GetDeviceGroup(DeviceIoType.Output, profile.OutputDeviceGroupGuid); foreach (var device in outputDeviceGroup.Devices) { - state.AddOutputDevice(device, profile); + profileOutputDevices.Add(state.AddOutputDevice(device, profile)); } - foreach (var profilePlugin in profile.Plugins) + // Devices are inherited, load them for the subscription model + if (outputDeviceGroup.Devices.Count == 0) { - state.AddDeviceBindingSubscriptions(profilePlugin); + var deviceGroup = profile.GetDeviceGroup(DeviceIoType.Output); + if (deviceGroup != null) + { + foreach (var device in deviceGroup.Devices) + { + profileOutputDevices.Add(state.AddOutputDevice(device, profile)); + } + } } - state.BuildActivePluginsList(); - + state.AddMappings(profile, profileOutputDevices); + return success; } - // Subscribes the backend when it is built private bool ActivateSubscriptionState(SubscriptionState state) { var success = true; if (state.IsActive) return true; - foreach (var deviceSubscription in state.DeviceSubscriptions) + foreach (var deviceSubscription in state.OutputDeviceSubscriptions) { success &= SubscribeOutput(state, deviceSubscription); } - foreach (var deviceBindingSubscriptionsGroup in state.DeviceBindingSubscriptions) + foreach (var mappingSubscription in state.MappingSubscriptions) { - foreach (var subscription in deviceBindingSubscriptionsGroup.Value) + if (mappingSubscription.Overriden) continue; + + mappingSubscription.Mapping.PrepareMapping(); + + foreach (var deviceBindingSubscription in mappingSubscription.DeviceBindingSubscriptions) { - if (subscription.IsOverwritten) continue; - success &= SubscribeDeviceBindingInput(state, subscription); + success &= SubscribeDeviceBindingInput(state, deviceBindingSubscription); } - } - foreach (var plugin in state.ActivePlugins) - { - plugin.OnActivate(); + foreach (var pluginSubscription in mappingSubscription.PluginSubscriptions) + { + pluginSubscription.Plugin.OnActivate(); + } } state.IsActive = true; return success; } - private bool SubscribeDeviceBindingInput(SubscriptionState state, DeviceBindingSubscription deviceBindingSubscription) + + #region Subscriber Actions + + private bool SubscribeDeviceBindingInput(SubscriptionState state, InputSubscription deviceBindingSubscription) { - return deviceBindingSubscription.DeviceBinding.IsBound - ? _context.IOController.SubscribeInput(GetInputSubscriptionRequest(state, deviceBindingSubscription)) - : UnsubscribeDeviceBindingInput(state, deviceBindingSubscription); + if (!deviceBindingSubscription.DeviceBinding.IsBound) return true; + return _context.IOController.SubscribeInput(GetInputSubscriptionRequest(state, deviceBindingSubscription)); } - private bool UnsubscribeDeviceBindingInput(SubscriptionState state, DeviceBindingSubscription deviceBindingSubscription) + private bool UnsubscribeDeviceBindingInput(SubscriptionState state, InputSubscription deviceBindingSubscription) { + if (!deviceBindingSubscription.DeviceBinding.IsBound) return true; return _context.IOController.UnsubscribeInput(GetInputSubscriptionRequest(state, deviceBindingSubscription)); } @@ -192,9 +209,11 @@ private bool UnsubscribeOutput(SubscriptionState state, DeviceSubscription devic return _context.IOController.UnsubscribeOutput(GetOutputSubscriptionRequest(state.StateGuid, deviceSubscription)); } + #endregion + #region DescriptionHelpers - private InputSubscriptionRequest GetInputSubscriptionRequest(SubscriptionState state, DeviceBindingSubscription deviceBindingSubscription) + private InputSubscriptionRequest GetInputSubscriptionRequest(SubscriptionState state, InputSubscription deviceBindingSubscription) { var device = deviceBindingSubscription.DeviceSubscription.Device; return new InputSubscriptionRequest() diff --git a/UCR.Core/Models/Binding/DeviceBinding.cs b/UCR.Core/Models/Binding/DeviceBinding.cs index 03c68199..57f39c35 100644 --- a/UCR.Core/Models/Binding/DeviceBinding.cs +++ b/UCR.Core/Models/Binding/DeviceBinding.cs @@ -1,9 +1,8 @@ using System; using System.Xml.Serialization; -using Providers; -using UCR.Core.Models.Device; +using HidWizards.IOWrapper.DataTransferObjects; -namespace UCR.Core.Models.Binding +namespace HidWizards.UCR.Core.Models.Binding { public enum DeviceBindingCategory { @@ -15,20 +14,20 @@ public enum DeviceBindingCategory public class DeviceBinding { - // Persistence + /* Persistence */ public bool IsBound { get; set; } // Index in its device list - public int DeviceNumber { get; set; } + public Guid DeviceGuid { get; set; } // Subscription key public int KeyType { get; set; } public int KeyValue { get; set; } public int KeySubValue { get; set; } - // Runtime + /* Runtime */ [XmlIgnore] public Guid Guid { get; } [XmlIgnore] - public Models.Plugin.Plugin Plugin { get; set; } + public Profile Profile { get; set; } [XmlIgnore] public DeviceIoType DeviceIoType { get; set; } [XmlIgnore] @@ -41,15 +40,15 @@ public class DeviceBinding [XmlIgnore] public ValueChanged OutputSink { get; set; } - private DeviceBinding() + public DeviceBinding() { Guid = Guid.NewGuid(); } - public DeviceBinding(ValueChanged callback, Plugin.Plugin plugin, DeviceIoType deviceIoType) + public DeviceBinding(ValueChanged callback, Profile profile, DeviceIoType deviceIoType) { Callback = callback; - Plugin = plugin; + Profile = profile; DeviceIoType = deviceIoType; Guid = Guid.NewGuid(); IsBound = false; @@ -57,19 +56,19 @@ public DeviceBinding(ValueChanged callback, Plugin.Plugin plugin, DeviceIoType d public DeviceBinding(DeviceBinding deviceBinding) { - DeviceNumber = deviceBinding.DeviceNumber; + DeviceGuid = deviceBinding.DeviceGuid; KeyType = deviceBinding.KeyType; KeyValue = deviceBinding.KeyValue; - Plugin = deviceBinding.Plugin; + Profile = deviceBinding.Profile; Callback = deviceBinding.Callback; Guid = deviceBinding.Guid; IsBound = deviceBinding.IsBound; } - public void SetDeviceNumber(int number) + public void SetDeviceGuid(Guid deviceGuid) { - DeviceNumber = number; - Plugin.ParentProfile.context.ContextChanged(); + DeviceGuid = deviceGuid; + Profile.Context.ContextChanged(); } public void SetKeyTypeValue(int type, int value, int subValue) @@ -78,12 +77,12 @@ public void SetKeyTypeValue(int type, int value, int subValue) KeyValue = value; KeySubValue = subValue; IsBound = true; - Plugin.ParentProfile.context.ContextChanged(); + Profile.Context.ContextChanged(); } public string BoundName() { - return Plugin.GetDevice(this)?.GetBindingName(this) ?? "Device unavailable"; + return Profile.GetDevice(this)?.GetBindingName(this) ?? "Device unavailable"; } public static DeviceBindingCategory MapCategory(BindingCategory bindingInfoCategory) diff --git a/UCR.Core/Models/Binding/DeviceBindingNode.cs b/UCR.Core/Models/Binding/DeviceBindingNode.cs index 1d869bce..dc82ed96 100644 --- a/UCR.Core/Models/Binding/DeviceBindingNode.cs +++ b/UCR.Core/Models/Binding/DeviceBindingNode.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace UCR.Core.Models.Binding +namespace HidWizards.UCR.Core.Models.Binding { public class DeviceBindingNode { diff --git a/UCR.Core/Models/CallbackMultiplexer.cs b/UCR.Core/Models/CallbackMultiplexer.cs new file mode 100644 index 00000000..4fdb265e --- /dev/null +++ b/UCR.Core/Models/CallbackMultiplexer.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using HidWizards.UCR.Core.Models.Binding; + +namespace HidWizards.UCR.Core.Models +{ + public class CallbackMultiplexer + { + private DeviceBinding.ValueChanged _mappingUpdate; + private readonly int _index; + private readonly List _cache; + + public CallbackMultiplexer(List cache, int index, DeviceBinding.ValueChanged mappingUpdate) + { + _mappingUpdate = mappingUpdate; + _index = index; + _cache = cache; + } + + public void Update(long value) + { + _cache[_index] = value; + _mappingUpdate(value); + } + + ~CallbackMultiplexer() + { + _mappingUpdate = null; + } + } +} diff --git a/UCR.Core/Models/Device/Device.cs b/UCR.Core/Models/Device.cs similarity index 94% rename from UCR.Core/Models/Device/Device.cs rename to UCR.Core/Models/Device.cs index 6e4aa29e..1aa7b207 100644 --- a/UCR.Core/Models/Device/Device.cs +++ b/UCR.Core/Models/Device.cs @@ -1,14 +1,11 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Dynamic; -using System.Resources; using System.Xml.Serialization; +using HidWizards.IOWrapper.DataTransferObjects; +using HidWizards.UCR.Core.Models.Binding; using NLog; -using Providers; -using UCR.Core.Models.Binding; -namespace UCR.Core.Models.Device +namespace HidWizards.UCR.Core.Models { public enum DeviceIoType { @@ -20,17 +17,14 @@ public class Device { private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger(); - // Persistance + /* Persistance */ + public Guid Guid { get; set; } public string Title { get; set; } public string ProviderName { get; set; } public string DeviceHandle { get; set; } public int DeviceNumber { get; set; } - // Runtime - [XmlIgnore] - public Guid Guid { get; set; } - [XmlIgnore] - public Profile.Profile ParentProfile { get; private set; } + /* Runtime */ [XmlIgnore] private List DeviceBindingMenu { get; set; } @@ -46,7 +40,7 @@ public Device() Guid = (guid == Guid.Empty) ? Guid.NewGuid() : guid; } - public Device(Device device) : this(device.Guid) + public Device(Device device) : this((Guid) device.Guid) { Title = device.Title; DeviceHandle = device.DeviceHandle; diff --git a/UCR.Core/Models/Device/DeviceGroup.cs b/UCR.Core/Models/DeviceGroup.cs similarity index 95% rename from UCR.Core/Models/Device/DeviceGroup.cs rename to UCR.Core/Models/DeviceGroup.cs index 58814946..a6c3b7a8 100644 --- a/UCR.Core/Models/Device/DeviceGroup.cs +++ b/UCR.Core/Models/DeviceGroup.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace UCR.Core.Models.Device +namespace HidWizards.UCR.Core.Models { public class DeviceGroup { diff --git a/UCR.Core/Models/Mapping.cs b/UCR.Core/Models/Mapping.cs new file mode 100644 index 00000000..fb8f4dfe --- /dev/null +++ b/UCR.Core/Models/Mapping.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; +using HidWizards.UCR.Core.Models.Binding; + +namespace HidWizards.UCR.Core.Models +{ + public class Mapping + { + /* Persistence */ + public string Title { get; set; } + public Guid Guid { get; set; } + public List DeviceBindings { get; set; } + public List Plugins { get; set; } + + /* Runtime */ + private Profile Profile { get; set; } + private List InputCache { get; set; } + private List Multiplexer { get; set; } + + [XmlIgnore] + public string FullTitle + { + get + { + var mapping = GetOverridenMapping(); + return mapping != null ? $"{Title} (Overrides {GetOverridenMapping().Profile.Title})" : Title; + } + } + + public Mapping() + { + Guid = Guid.NewGuid(); + DeviceBindings = new List(); + Plugins = new List(); + } + + public Mapping(Profile profile, string title) : this() + { + Profile = profile; + Title = title; + } + + internal bool IsBound() + { + if (DeviceBindings.Count == 0) return false; + var result = true; + foreach (var deviceBinding in DeviceBindings) + { + result &= deviceBinding.IsBound; + } + return result; + } + + internal void PrepareMapping() + { + InputCache = new List(); + DeviceBindings.ForEach(_ => InputCache.Add(0L)); + Multiplexer = new List(); + for (var i = 0; i < DeviceBindings.Count; i++) + { + var cm = new CallbackMultiplexer(InputCache, i, Update); + Multiplexer.Add(cm); + DeviceBindings[i].Callback = cm.Update; + } + } + + internal Mapping GetOverridenMapping() + { + var list = new List(); + var parentProfile = Profile.ParentProfile; + if (parentProfile != null) list.AddRange(parentProfile.Mappings); + + while (list.Count > 0) + { + var mapping = list[0]; + list.RemoveAt(0); + if (string.Compare(Title, mapping.Title, StringComparison.CurrentCultureIgnoreCase) == 0) + { + return mapping; + } + parentProfile = parentProfile?.ParentProfile; + if (parentProfile != null) list.AddRange(parentProfile.Mappings); + } + + return null; + } + + public void Update(long value) + { + foreach (var plugin in Plugins) + { + if (plugin.State == Guid.Empty || Profile.GetRuntimeState(plugin.State)) + { + plugin.Update(InputCache.ToArray()); + } + } + } + + #region Plugin + + internal List GetPluginList() + { + var plugins = Profile.Context.GetPlugins(); + plugins.Sort(); + if (Plugins.Count > 0) + { + plugins = plugins.FindAll(p => p.HasSameInputCategories(Plugins[0])); + } + return plugins; + } + + public bool AddPlugin(Plugin plugin, Guid? state) + { + if (Plugins.Count == 0) + { + foreach (var _ in plugin.InputCategories) + { + DeviceBindings.Add(new DeviceBinding(Update, Profile, DeviceIoType.Input)); + } + } + + plugin.SetProfile(Profile); + if (state != null) plugin.State = state.Value; + Plugins.Add(plugin); + + Profile.Context.ContextChanged(); + return true; + } + + public bool RemovePlugin(Plugin plugin) + { + if (!Plugins.Remove(plugin)) return false; + + if (Plugins.Count == 0) + { + DeviceBindings = new List(); + } + Profile.Context.ContextChanged(); + + return true; + } + + #endregion + + + internal void PostLoad(Context context, Profile profile = null) + { + Profile = profile; + foreach (var deviceBinding in DeviceBindings) + { + deviceBinding.Profile = profile; + } + + foreach (var plugin in Plugins) + { + plugin.PostLoad(context, profile); + } + } + + internal void InitializeMappings(int amount) + { + DeviceBindings = new List(); + for (var i = 0; i < amount; i++) + { + DeviceBindings.Add(new DeviceBinding(Update, Profile, DeviceIoType.Input)); + } + } + } +} diff --git a/UCR.Core/Models/Plugin.cs b/UCR.Core/Models/Plugin.cs new file mode 100644 index 00000000..9242202e --- /dev/null +++ b/UCR.Core/Models/Plugin.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Xml.Serialization; +using HidWizards.UCR.Core.Attributes; +using HidWizards.UCR.Core.Models.Binding; + +namespace HidWizards.UCR.Core.Models +{ + [InheritedExport(typeof(Plugin))] + public abstract class Plugin : IComparable + { + /* Persistence */ + public Guid State { get; set; } + public List Outputs { get; } + + /* Runtime */ + internal Profile Profile { get; set; } + private List _inputCategories; + private List _outputCategories; + private List> _guiMatrix; + + #region Properties + + [XmlIgnore] + public List InputCategories + { + get + { + if (_inputCategories != null) return _inputCategories; + _inputCategories = GetIODefinitions(DeviceIoType.Input); + return _inputCategories; + } + } + + [XmlIgnore] + public List OutputCategories + { + get + { + if (_outputCategories != null) return _outputCategories; + _outputCategories = GetIODefinitions(DeviceIoType.Output); + return _outputCategories; + } + } + + [XmlIgnore] + public List> GuiMatrix + { + get + { + if (_guiMatrix != null) return _guiMatrix; + _guiMatrix = GetGuiMatrix(); + return _guiMatrix; + } + } + + [XmlIgnore] + public string PluginName => State.Equals(Guid.Empty) ? GetPluginAttribute().Name : $"{GetPluginAttribute().Name} - State: {StateTitle}"; + + [XmlIgnore] + public bool IsDisabled => GetPluginAttribute().Disabled; + + public string StateTitle + { + get => Profile.GetStateTitle(State); + } + + #endregion + + public struct IODefinition + { + public string Name; + public DeviceBindingCategory Category; + } + + protected Plugin() + { + Outputs = new List(); + foreach (var _ in OutputCategories) + { + Outputs.Add(new DeviceBinding(null, Profile, DeviceIoType.Output)); + } + } + + #region Life cycle + + public virtual void OnActivate() + { + + } + + public virtual void Update(params long[] values) + { + + } + + public virtual void OnDeactivate() + { + + } + + #endregion + + #region Plugin methods + + protected void WriteOutput(int number, long value) + { + Outputs[number].WriteOutput(value); + } + + protected void SetState(Guid stateGuid, bool newState) + { + Profile.SetRuntimeState(stateGuid, newState); + } + + protected bool GetState(Guid stateGuid) + { + return Profile.GetRuntimeState(stateGuid); + } + + #endregion + + public void SetProfile(Profile profile) + { + Profile = profile; + Outputs.ForEach(o => o.Profile = profile); + } + + public Plugin Duplicate() + { + var newPlugin = Context.DeepXmlClone(this); + newPlugin.PostLoad(Profile.Context, Profile); + return newPlugin; + } + + public void ContextChanged() + { + Profile?.Context?.ContextChanged(); + } + + #region Loading + + public void PostLoad(Context context, Profile parentProfile) + { + SetProfile(parentProfile); + ZipDeviceBindingList(Outputs); + Outputs.ForEach(o => o.DeviceIoType = DeviceIoType.Output); + + } + + private static void ZipDeviceBindingList(IList deviceBindings) + { + if (deviceBindings.Count == 0) return; + var split = deviceBindings.Count / 2; + for (var i = 0; i < split; i++) + { + deviceBindings[i].IsBound = deviceBindings[i + split].IsBound; + deviceBindings[i].DeviceGuid = deviceBindings[i + split].DeviceGuid; + deviceBindings[i].KeyType = deviceBindings[i + split].KeyType; + deviceBindings[i].KeyValue = deviceBindings[i + split].KeyValue; + deviceBindings[i].KeySubValue = deviceBindings[i + split].KeySubValue; + } + + for (var i = deviceBindings.Count - 1; i >= split; i--) + { + deviceBindings.Remove(deviceBindings[i]); + } + } + + #endregion + + #region Comparison + + public int CompareTo(Plugin other) + { + return string.Compare(PluginName, other.PluginName, StringComparison.Ordinal); + } + + public bool HasSameInputCategories(Plugin other) + { + if (InputCategories.Count > other.InputCategories.Count) return false; + for (var i = 0; i < InputCategories.Count; i++) + { + if (InputCategories[i].Category != other.InputCategories[i].Category) return false; + } + + return true; + } + + #endregion + + #region Attributes + + private PluginAttribute GetPluginAttribute() + { + var pluginAttribute = (PluginAttribute)Attribute.GetCustomAttribute(GetType(), typeof(PluginAttribute)); + + return pluginAttribute ?? new PluginAttribute("Invalid plugin"); + } + + private List GetIODefinitions(DeviceIoType deviceIoType) + { + var attributes = (PluginIoAttribute[])Attribute.GetCustomAttributes(GetType(), typeof(PluginIoAttribute)); + + return attributes.Where(a => a.DeviceIoType == deviceIoType).Select(a => new IODefinition() + { + Category = a.DeviceBindingCategory, + Name = a.Name + }).ToList(); + } + + private List GetGuiProperties() + { + var properties = from p in GetType().GetProperties() + let attr = p.GetCustomAttributes(typeof(PluginGuiAttribute), true) + where attr.Length == 1 + select new { Property = p, Attribute = attr.First() as PluginGuiAttribute }; + + + return properties.Select(prop => new PluginProperty(this, prop.Property, prop.Attribute.Name, prop.Attribute.RowOrder, prop.Attribute.ColumnOrder)).ToList(); + } + + public List> GetGuiMatrix() + { + var result = new List>(); + var currentRow = new List(); + result.Add(currentRow); + + var guiProperties = GetGuiProperties(); + guiProperties.Sort(); + + foreach (var pluginProperty in guiProperties) + { + if (currentRow.Count != 0 && currentRow[0].RowOrder != pluginProperty.RowOrder) + { + currentRow = new List(); + result.Add(currentRow); + } + + currentRow.Add(pluginProperty); + } + + foreach (var pluginPropertiesRow in result) + { + pluginPropertiesRow.Sort((x, y) => x.ColumnOrder.CompareTo(y.ColumnOrder)); + } + + return result; + } + + #endregion + } +} diff --git a/UCR.Core/Models/Plugin/Plugin.cs b/UCR.Core/Models/Plugin/Plugin.cs deleted file mode 100644 index 5cb07905..00000000 --- a/UCR.Core/Models/Plugin/Plugin.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Xml.Serialization; -using UCR.Core.Models.Binding; -using UCR.Core.Models.Device; - -namespace UCR.Core.Models.Plugin -{ - public abstract class Plugin : IComparable - { - // Persistence - public string Title { get; set; } - public List Inputs { get; } - public List Outputs { get; } - - // Runtime - internal Profile.Profile ParentProfile { get; set; } - internal List ContainingList { get; set; } - - // Abstract - public abstract string PluginName(); - - protected Plugin() - { - Inputs = new List(); - Outputs = new List(); - } - - public bool Remove() - { - ContainingList.Remove(this); - ParentProfile.context.ContextChanged(); - return true; - } - - public virtual void OnActivate() - { - - } - - public virtual void OnDeactivate() - { - - } - - public Device.Device GetDevice(DeviceBinding deviceBinding) - { - return ParentProfile.GetDevice(deviceBinding); - } - - protected void WriteOutput(DeviceBinding output, long value) - { - output.WriteOutput(value); - } - - public virtual List GetInputs() - { - return Inputs; - // TODO Delete? - return Inputs.Select(input => new DeviceBinding(input)).ToList(); - } - - protected DeviceBinding InitializeInputMapping(DeviceBinding.ValueChanged callbackFunc) - { - return InitializeMapping(DeviceIoType.Input, callbackFunc); - } - - protected DeviceBinding InitializeOutputMapping() - { - return InitializeMapping(DeviceIoType.Output, null); - } - - private DeviceBinding InitializeMapping(DeviceIoType deviceIoType, DeviceBinding.ValueChanged callbackFunc) - { - var deviceBinding = new DeviceBinding(callbackFunc, this, deviceIoType); - switch(deviceIoType) - { - case DeviceIoType.Input: - Inputs.Add(deviceBinding); - break; - case DeviceIoType.Output: - Outputs.Add(deviceBinding); - break; - default: - throw new ArgumentOutOfRangeException(nameof(deviceIoType), deviceIoType, null); - } - return deviceBinding; - } - - public List GetDeviceList(DeviceBinding deviceBinding) - { - return ParentProfile.GetDeviceList(deviceBinding); - } - - public void Rename(string title) - { - Title = title; - ParentProfile.context.ContextChanged(); - } - - public void PostLoad(Context context, Profile.Profile parentProfile) - { - ParentProfile = parentProfile; - ContainingList = parentProfile.Plugins; - - ZipDeviceBindingList(Inputs); - ZipDeviceBindingList(Outputs); - } - - public Plugin Duplicate() - { - var newPlugin = Context.DeepXmlClone(this); - newPlugin.PostLoad(ParentProfile.context, ParentProfile); - return newPlugin; - } - - protected void ContextChanged() - { - ParentProfile?.context?.ContextChanged(); - } - - private static void ZipDeviceBindingList(IList deviceBindings) - { - if (deviceBindings.Count == 0) return; - var split = deviceBindings.Count / 2; - for (var i = 0; i < split; i++) - { - deviceBindings[i].IsBound = deviceBindings[i + split].IsBound; - deviceBindings[i].DeviceNumber = deviceBindings[i + split].DeviceNumber; - deviceBindings[i].KeyType = deviceBindings[i + split].KeyType; - deviceBindings[i].KeyValue = deviceBindings[i + split].KeyValue; - deviceBindings[i].KeySubValue = deviceBindings[i + split].KeySubValue; - } - - for (var i = deviceBindings.Count - 1; i >= split ; i--) - { - deviceBindings.Remove(deviceBindings[i]); - } - } - - public int CompareTo(Plugin other) - { - return string.Compare(PluginName(), other.PluginName(), StringComparison.Ordinal); - } - } -} diff --git a/UCR.Core/Models/Plugin/PluginGroup.cs b/UCR.Core/Models/Plugin/PluginGroup.cs deleted file mode 100644 index 0c76e34d..00000000 --- a/UCR.Core/Models/Plugin/PluginGroup.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UCR.Core.Models.Binding; - -namespace UCR.Core.Models.Plugin -{ - public abstract class PluginGroup : Plugin - { - public List Plugins { get; set; } - - public PluginGroup() - { - Plugins = new List(); - } - - public override List GetInputs() - { - List newBindings = Inputs.Select(input => new DeviceBinding(input)).ToList(); - - foreach (var plugin in Plugins) - { - newBindings.AddRange(plugin.GetInputs()); - } - - return newBindings; - } - - public void AddPlugin(Plugin plugin, string title) - { - var newPlugin = (Plugin) Activator.CreateInstance(plugin.GetType()); - newPlugin.ParentProfile = ParentProfile; - newPlugin.Title = title; - newPlugin.ContainingList = Plugins; - Plugins.Add(newPlugin); - ParentProfile.context.ContextChanged(); - } - - internal new void PostLoad(Context context, Profile.Profile parentProfile) - { - (this as Plugin).PostLoad(context, parentProfile); - foreach (var plugin in Plugins) - { - plugin.PostLoad(context, parentProfile); - plugin.ContainingList = Plugins; - } - } - } -} diff --git a/UCR.Core/Models/PluginProperty.cs b/UCR.Core/Models/PluginProperty.cs new file mode 100644 index 00000000..32748cd6 --- /dev/null +++ b/UCR.Core/Models/PluginProperty.cs @@ -0,0 +1,39 @@ +using System; +using System.Reflection; + +namespace HidWizards.UCR.Core.Models +{ + public class PluginProperty : IComparable + { + public string Name { get; } + public Plugin Plugin { get; } + public int RowOrder { get; } + public int ColumnOrder { get; } + + public PropertyInfo PropertyInfo { get; } + public dynamic Property + { + get => PropertyInfo.GetValue(Plugin); + set + { + if (value.Equals(PropertyInfo.GetValue(Plugin))) return; + PropertyInfo.SetValue(Plugin, Convert.ChangeType(value, PropertyInfo.PropertyType)); + Plugin.ContextChanged(); + } + } + + public PluginProperty(Plugin plugin, PropertyInfo propertyInfo, string name, int rowOrder = 0, int columnOrder = 0) + { + Plugin = plugin; + PropertyInfo = propertyInfo; + Name = name; + RowOrder = rowOrder; + ColumnOrder = columnOrder; + } + + public int CompareTo(PluginProperty other) + { + return RowOrder.CompareTo(other.RowOrder); + } + } +} diff --git a/UCR.Core/Models/Profile.cs b/UCR.Core/Models/Profile.cs new file mode 100644 index 00000000..204d6b04 --- /dev/null +++ b/UCR.Core/Models/Profile.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Serialization; +using HidWizards.UCR.Core.Models.Binding; +using NLog; + +namespace HidWizards.UCR.Core.Models +{ + public class Profile + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + /* Persistence */ + public string Title { get; set; } + public Guid Guid { get; set; } + public List ChildProfiles { get; set; } + public List States { get; set; } + public List Mappings { get; set; } + public Guid InputDeviceGroupGuid { get; set; } + public Guid OutputDeviceGroupGuid { get; set; } + + /* Property helpers */ + [XmlIgnore] + public List AllStates + { + get + { + var list = new List + { + new State + { + Guid = Guid.Empty, + Title = "Default" + } + }; + list.AddRange(States); + return list; + } + } + + + /* Runtime */ + [XmlIgnore] + public Context Context; + [XmlIgnore] + public Profile ParentProfile { get; set; } + internal ConcurrentDictionary StateDictionary { get; set; } + + #region Constructors + + public Profile() + { + Init(); + } + + public Profile(Context context) + { + Context = context; + Init(); + } + + private void Init() + { + Guid = Guid.NewGuid(); + ChildProfiles = new List(); + States = new List(); + Mappings = new List(); + } + + public Profile(Context context, Profile parentProfile = null) : this(context) + { + ParentProfile = parentProfile; + } + + #endregion + + #region Actions + + public static Profile CreateProfile(Context context, string title, Profile parent = null) + { + var profile = new Profile(context, parent) + { + Title = title + }; + + return profile; + } + + public void AddNewChildProfile(string title) + { + if (ChildProfiles == null) ChildProfiles = new List(); + ChildProfiles.Add(CreateProfile(Context, title, this)); + Context.ContextChanged(); + } + + public void AddChildProfile(Profile profile) + { + if (ChildProfiles == null) ChildProfiles = new List(); + profile.Context = Context; + profile.ParentProfile = this; + ChildProfiles.Add(profile); + Context.ContextChanged(); + } + + public bool Rename(string title) + { + Title = title; + Context.ContextChanged(); + return true; + } + + public void Remove() + { + if (ParentProfile == null) + { + Context.Profiles.Remove(this); + } + else + { + ParentProfile.ChildProfiles.Remove(this); + } + Context.ContextChanged(); + } + + public void SetDeviceGroup(DeviceIoType ioType, Guid deviceGroupGuid) + { + switch (ioType) + { + case DeviceIoType.Input: + InputDeviceGroupGuid = deviceGroupGuid; + break; + case DeviceIoType.Output: + OutputDeviceGroupGuid = deviceGroupGuid; + break; + default: + throw new ArgumentOutOfRangeException(nameof(ioType), ioType, null); + } + Context.ContextChanged(); + } + + public bool ActivateProfile() + { + return Context.SubscriptionsManager.ActivateProfile(this); + } + + public bool Deactivate() + { + return Context.SubscriptionsManager.DeactivateProfile(); + } + + internal void PrepareProfile() + { + StateDictionary = new ConcurrentDictionary(); + States.ForEach(s => StateDictionary.TryAdd(s.Guid, false)); + } + + #endregion + + #region Mapping + + public Mapping AddMapping(string title) + { + var mapping = new Mapping(this, title); + Mappings.Add(mapping); + Context.ContextChanged(); + return mapping; + } + + public bool RemoveMapping(Mapping mapping) + { + if (!Mappings.Remove(mapping)) return false; + Context.ContextChanged(); + return true; + } + + #endregion + + #region Device + + public Device GetDevice(DeviceBinding deviceBinding) + { + var deviceList = GetDeviceList(deviceBinding); + return deviceList.FirstOrDefault(d => d.Guid == deviceBinding.DeviceGuid); + } + + public List GetDeviceList(DeviceBinding deviceBinding) + { + return GetDeviceList(deviceBinding.DeviceIoType); + } + + public List GetDeviceList(DeviceIoType deviceIoType) + { + return GetDeviceGroup(deviceIoType)?.Devices ?? new List(); + } + + public DeviceGroup GetDeviceGroup(DeviceIoType deviceIoType) + { + var deviceGroupGuid = GetDeviceGroupGuid(deviceIoType); + if (deviceGroupGuid.Equals(Guid.Empty)) + { + return ParentProfile?.GetDeviceGroup(deviceIoType); + } + return Context.DeviceGroupsManager.GetDeviceGroup(deviceIoType, deviceGroupGuid); + } + + public string GetInheritedDeviceGroupName(DeviceIoType deviceIoType) + { + var parentDeviceGroupName = "None"; + var parentDeviceGroup = ParentProfile?.GetDeviceGroup(deviceIoType); + if (parentDeviceGroup != null) parentDeviceGroupName = parentDeviceGroup.Title; + if (ParentProfile != null) parentDeviceGroupName += " (Inherited)"; + return parentDeviceGroupName; + } + + #endregion + + #region Plugin + + public bool AddNewPlugin(Mapping mapping, Plugin plugin, Guid? state = null) + { + return AddPlugin(mapping, (Plugin)Activator.CreateInstance(plugin.GetType()), state); + } + + public bool AddPlugin(Mapping mapping, Plugin plugin, Guid? state = null) + { + if (!Mappings.Contains(mapping)) return false; + plugin.State = state ?? Guid.Empty; + plugin.Profile = this; + mapping.Plugins.Add(plugin); + Context.ContextChanged(); + return true; + } + + public bool RemovePlugin(Mapping mapping, Plugin plugin) + { + if (!Mappings.Contains(mapping)) return false; + mapping.Plugins.Remove(plugin); + Context.ContextChanged(); + return true; + } + + #endregion + + #region State + + public State AddState(string title) + { + if (States.Find(s => s.Title == title) != null) return null; + var newState = new State(title); + States.Add(newState); + Context.ContextChanged(); + return newState; + } + + public bool RemoveState(State state) + { + var success = States.Remove(state); + // TODO remove all plugins with state + + Context.ContextChanged(); + return success; + } + + public string GetStateTitle(Guid stateGuid) + { + var state = States.Find(s => s.Guid == stateGuid); + return state == null ? "" : state.Title; + } + + public void SetRuntimeState(Guid stateGuid, bool newState) + { + if (StateDictionary.TryGetValue(stateGuid, out var currentState)) + { + StateDictionary.TryUpdate(stateGuid, newState, currentState); + } + } + + public bool GetRuntimeState(Guid stateGuid) + { + return StateDictionary.TryGetValue(stateGuid, out var currentState) && currentState; + } + + #endregion + + #region Helpers + + public string ProfileBreadCrumbs() + { + return ParentProfile != null ? ParentProfile.ProfileBreadCrumbs() + " > " + Title : Title; + } + + /// + /// Returns true if bindings are currently subscribed to the backend + /// + /// + public bool IsActive() + { + return Context.SubscriptionsManager.GetActiveProfile() != null && Context.SubscriptionsManager.GetActiveProfile().Guid == Guid; + } + + private Guid GetDeviceGroupGuid(DeviceIoType deviceIoType) + { + switch (deviceIoType) + { + case DeviceIoType.Input: + return InputDeviceGroupGuid; + case DeviceIoType.Output: + return OutputDeviceGroupGuid; + default: + throw new ArgumentOutOfRangeException(nameof(deviceIoType), deviceIoType, null); + } + } + + #endregion + + internal void PostLoad(Context context, Profile parentProfile = null) + { + Context = context; + ParentProfile = parentProfile; + + foreach (var profile in ChildProfiles) + { + profile.PostLoad(context, this); + } + + foreach (var mapping in Mappings) + { + mapping.PostLoad(context, this); + } + } + } +} \ No newline at end of file diff --git a/UCR.Core/Models/Profile/Profile.cs b/UCR.Core/Models/Profile/Profile.cs deleted file mode 100644 index 14d7b8b0..00000000 --- a/UCR.Core/Models/Profile/Profile.cs +++ /dev/null @@ -1,257 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Serialization; -using NLog; -using UCR.Core.Models.Binding; -using UCR.Core.Models.Device; -using UCR.Core.Models.Plugin; - -namespace UCR.Core.Models.Profile -{ - public class Profile - { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - /* Persistence */ - public string Title { get; set; } - public Guid Guid { get; set; } - public List ChildProfiles { get; set; } - public List Plugins { get; set; } - - // IO - public Guid InputDeviceGroupGuid { get; set; } - public Guid OutputDeviceGroupGuid { get; set; } - - - /* Runtime */ - [XmlIgnore] - public Context context; - [XmlIgnore] - public Profile ParentProfile { get; set; } - - #region Constructors - - public Profile() - { - Init(); - } - - public Profile(Context context) - { - this.context = context; - Init(); - } - - private void Init() - { - Plugins = Plugins ?? new List(); - ChildProfiles = ChildProfiles ?? new List(); - Plugins = Plugins ?? new List(); - - Guid = Guid == Guid.Empty ? Guid.NewGuid() : Guid; - } - - public Profile(Context context, Profile parentProfile = null) : this(context) - { - ParentProfile = parentProfile; - } - - #endregion - - #region Actions - - public static Profile CreateProfile(Context context, string title, Profile parent = null) - { - var profile = new Profile(context, parent) - { - Title = title - }; - - return profile; - } - - public void AddNewChildProfile(string title) - { - if (ChildProfiles == null) ChildProfiles = new List(); - ChildProfiles.Add(CreateProfile(context, title, this)); - context.ContextChanged(); - } - - public void AddChildProfile(Profile profile) - { - if (ChildProfiles == null) ChildProfiles = new List(); - profile.context = context; - profile.ParentProfile = this; - ChildProfiles.Add(profile); - context.ContextChanged(); - } - - public bool Rename(string title) - { - Title = title; - context.ContextChanged(); - return true; - } - - public void Remove() - { - if (ParentProfile == null) - { - context.Profiles.Remove(this); - } - else - { - ParentProfile.ChildProfiles.Remove(this); - } - context.ContextChanged(); - } - - public void SetDeviceGroup(DeviceIoType ioType, Guid deviceGroupGuid) - { - switch (ioType) - { - case DeviceIoType.Input: - InputDeviceGroupGuid = deviceGroupGuid; - break; - case DeviceIoType.Output: - OutputDeviceGroupGuid = deviceGroupGuid; - break; - default: - throw new ArgumentOutOfRangeException(nameof(ioType), ioType, null); - } - context.ContextChanged(); - } - - public bool ActivateProfile() - { - return context.SubscriptionsManager.ActivateProfile(this); - } - - public bool Deactivate() - { - return context.SubscriptionsManager.DeactivateProfile(); - } - - #endregion - - #region Device - - public Device.Device GetDevice(DeviceBinding deviceBinding) - { - var deviceList = GetDeviceList(deviceBinding); - return deviceBinding.DeviceNumber < deviceList.Count - ? deviceList[deviceBinding.DeviceNumber] - : null; - } - - public List GetDeviceList(DeviceBinding deviceBinding) - { - return GetDeviceList(deviceBinding.DeviceIoType); - } - - private List GetDeviceList(DeviceIoType deviceIoType) - { - return GetDeviceGroup(deviceIoType)?.Devices ?? new List(); - } - - public DeviceGroup GetDeviceGroup(DeviceIoType deviceIoType) - { - var deviceGroupGuid = GetDeviceGroupGuid(deviceIoType); - if (deviceGroupGuid.Equals(Guid.Empty)) - { - return ParentProfile?.GetDeviceGroup(deviceIoType); - } - return context.DeviceGroupsManager.GetDeviceGroup(deviceIoType, deviceGroupGuid); - } - - #endregion - - #region Plugin - - public void AddNewPlugin(Plugin.Plugin plugin, string title = "Untitled") - { - AddPlugin((Plugin.Plugin)Activator.CreateInstance(plugin.GetType()), title); - } - - public void AddPlugin(Plugin.Plugin plugin, string title = "Untitled") - { - - if (plugin.Title == null) plugin.Title = title; - plugin.ParentProfile = this; - plugin.ContainingList = Plugins; - Plugins.Add(plugin); - context.ContextChanged(); - } - - public void RemovePlugin(Models.Plugin.Plugin plugin) - { - Plugins.Remove(plugin); - context.ContextChanged(); - } - - #endregion - - #region Helpers - - public string ProfileBreadCrumbs() - { - return ParentProfile != null ? ParentProfile.ProfileBreadCrumbs() + " > " + Title : Title; - } - - /// - /// Returns true if bindings are currently subscribed to the backend - /// - /// - public bool IsActive() - { - return context.SubscriptionsManager.GetActiveProfile() != null && context.SubscriptionsManager.GetActiveProfile().Guid == Guid; - } - - private Guid GetDeviceGroupGuid(DeviceIoType deviceIoType) - { - switch (deviceIoType) - { - case DeviceIoType.Input: - return InputDeviceGroupGuid; - case DeviceIoType.Output: - return OutputDeviceGroupGuid; - default: - throw new ArgumentOutOfRangeException(nameof(deviceIoType), deviceIoType, null); - } - } - - public List GetAncestry() - { - var result = new List(); - if (ParentProfile != null) result.AddRange(ParentProfile.GetAncestry()); - result.Add(this); - return result; - } - - #endregion - - internal void PostLoad(Context context, Profile parentProfile = null) - { - this.context = context; - ParentProfile = parentProfile; - - foreach (var profile in ChildProfiles) - { - profile.PostLoad(context, this); - } - - foreach (var plugin in Plugins) - { - var group = plugin as PluginGroup; - if (group != null) - { - group.PostLoad(context, this); - } - else - { - plugin.PostLoad(context, this); - } - } - } - } -} \ No newline at end of file diff --git a/UCR.Core/Models/State.cs b/UCR.Core/Models/State.cs new file mode 100644 index 00000000..8ef1b8e4 --- /dev/null +++ b/UCR.Core/Models/State.cs @@ -0,0 +1,20 @@ +using System; + +namespace HidWizards.UCR.Core.Models +{ + public class State + { + public string Title { get; set; } + public Guid Guid { get; set; } + + public State() + { + } + + public State(string title) + { + Title = title; + Guid = Guid.NewGuid(); + } + } +} diff --git a/UCR.Core/Models/Subscription/DeviceBindingSubscription.cs b/UCR.Core/Models/Subscription/DeviceBindingSubscription.cs deleted file mode 100644 index dfb23f68..00000000 --- a/UCR.Core/Models/Subscription/DeviceBindingSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UCR.Core.Managers; -using UCR.Core.Models.Binding; - -namespace UCR.Core.Models.Subscription -{ - public class DeviceBindingSubscription - { - public DeviceBinding DeviceBinding { get; } - public Profile.Profile Profile { get; } - public Guid SubscriptionStateGuid { get; set; } - public Guid DeviceBindingSubscriptionGuid { get; set; } - public bool IsOverwritten { get; set; } - public DeviceSubscription DeviceSubscription { get; } - - public DeviceBindingSubscription(DeviceBinding deviceBinding, Profile.Profile profile, Guid subscriptionStateGuid, List deviceSubscriptions) - { - DeviceBinding = deviceBinding; - deviceBinding.OutputSink = WriteOutput; - Profile = profile; - SubscriptionStateGuid = subscriptionStateGuid; - DeviceBindingSubscriptionGuid = Guid.NewGuid(); - IsOverwritten = false; - - var device = GetDevice(); - DeviceSubscription = deviceSubscriptions.Find(ds => ds.Device.Guid == device.Guid) ?? new DeviceSubscription(device, profile); - } - - public static List GetSubscriptionsFromList(List deviceBindings, Guid subscriptionStateGuid, List deviceSubscriptions) - { - return deviceBindings.Select(deviceBinding => new DeviceBindingSubscription(deviceBinding, deviceBinding.Plugin.ParentProfile, subscriptionStateGuid, deviceSubscriptions)).ToList(); - } - - private Device.Device GetDevice() - { - return Profile.GetDevice(DeviceBinding); - } - - public void WriteOutput(long value) - { - // TODO get context properly - var success = Profile.context.IOController.SetOutputstate(SubscriptionsManager.GetOutputSubscriptionRequest(SubscriptionStateGuid, DeviceSubscription), SubscriptionsManager.GetBindingDescriptor(DeviceBinding), (int)value); - var a = 1; - } - } -} diff --git a/UCR.Core/Models/Subscription/DeviceSubscription.cs b/UCR.Core/Models/Subscription/DeviceSubscription.cs index 77f3797b..d35c0484 100644 --- a/UCR.Core/Models/Subscription/DeviceSubscription.cs +++ b/UCR.Core/Models/Subscription/DeviceSubscription.cs @@ -1,14 +1,14 @@ using System; -namespace UCR.Core.Models.Subscription +namespace HidWizards.UCR.Core.Models.Subscription { public class DeviceSubscription { public Guid DeviceSubscriptionGuid { get; } - public Device.Device Device { get; } - public Profile.Profile Profile { get; } + public Device Device { get; } + public Profile Profile { get; } - public DeviceSubscription(Device.Device device, Profile.Profile profile) + public DeviceSubscription(Device device, Profile profile) { DeviceSubscriptionGuid = Guid.NewGuid(); Device = device; diff --git a/UCR.Core/Models/Subscription/InputSubscription.cs b/UCR.Core/Models/Subscription/InputSubscription.cs new file mode 100644 index 00000000..8d1c160d --- /dev/null +++ b/UCR.Core/Models/Subscription/InputSubscription.cs @@ -0,0 +1,30 @@ +using System; +using HidWizards.UCR.Core.Models.Binding; + +namespace HidWizards.UCR.Core.Models.Subscription +{ + public class InputSubscription + { + public DeviceBinding DeviceBinding { get; } + public Profile Profile { get; } + public Guid SubscriptionStateGuid { get; set; } + public Guid DeviceBindingSubscriptionGuid { get; set; } + public bool IsOverwritten { get; set; } + public DeviceSubscription DeviceSubscription { get; } + + public InputSubscription(DeviceBinding deviceBinding, Profile profile, Guid subscriptionStateGuid) + { + DeviceBinding = deviceBinding; + Profile = profile; + SubscriptionStateGuid = subscriptionStateGuid; + DeviceBindingSubscriptionGuid = Guid.NewGuid(); + IsOverwritten = false; + DeviceSubscription = new DeviceSubscription(GetDevice(), profile); + } + + private Device GetDevice() + { + return Profile.GetDevice(DeviceBinding); + } + } +} diff --git a/UCR.Core/Models/Subscription/MappingSubscription.cs b/UCR.Core/Models/Subscription/MappingSubscription.cs new file mode 100644 index 00000000..417a0659 --- /dev/null +++ b/UCR.Core/Models/Subscription/MappingSubscription.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace HidWizards.UCR.Core.Models.Subscription +{ + public class MappingSubscription + { + public Mapping Mapping { get; } + public List DeviceBindingSubscriptions { get; } + public List PluginSubscriptions { get; } + public bool Overriden { get; set; } + + public MappingSubscription(Profile profile, Mapping mapping, Guid subscriptionStateGuid, List profileOutputDevices) + { + Mapping = mapping; + Overriden = false; + DeviceBindingSubscriptions = new List(); + foreach (var mappingDeviceBinding in Mapping.DeviceBindings) + { + DeviceBindingSubscriptions.Add(new InputSubscription(mappingDeviceBinding, profile, subscriptionStateGuid)); + } + + PluginSubscriptions = new List(); + foreach (var mappingPlugin in Mapping.Plugins) + { + PluginSubscriptions.Add(new PluginSubscription(mappingPlugin, subscriptionStateGuid, profileOutputDevices)); + } + } + } +} diff --git a/UCR.Core/Models/Subscription/OutputSubscription.cs b/UCR.Core/Models/Subscription/OutputSubscription.cs new file mode 100644 index 00000000..f24448ab --- /dev/null +++ b/UCR.Core/Models/Subscription/OutputSubscription.cs @@ -0,0 +1,28 @@ + +using System; +using HidWizards.UCR.Core.Managers; +using HidWizards.UCR.Core.Models.Binding; + +namespace HidWizards.UCR.Core.Models.Subscription +{ + public class OutputSubscription + { + public DeviceBinding DeviceBinding { get; } + public Guid SubscriptionStateGuid { get; } + public DeviceSubscription DeviceSubscription { get; } + + public OutputSubscription(DeviceBinding deviceBinding, Guid subscriptionStateGuid, DeviceSubscription outputDeviceSubscription) + { + DeviceBinding = deviceBinding; + SubscriptionStateGuid = subscriptionStateGuid; + DeviceSubscription = outputDeviceSubscription; + deviceBinding.OutputSink = WriteOutput; + } + + + private void WriteOutput(long value) + { + DeviceBinding.Profile.Context.IOController.SetOutputstate(SubscriptionsManager.GetOutputSubscriptionRequest(SubscriptionStateGuid, DeviceSubscription), SubscriptionsManager.GetBindingDescriptor(DeviceBinding), (int)value); + } + } +} diff --git a/UCR.Core/Models/Subscription/PluginSubscription.cs b/UCR.Core/Models/Subscription/PluginSubscription.cs new file mode 100644 index 00000000..146f9260 --- /dev/null +++ b/UCR.Core/Models/Subscription/PluginSubscription.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace HidWizards.UCR.Core.Models.Subscription +{ + public class PluginSubscription + { + public Plugin Plugin { get; set; } + public Guid SubscriptionStateGuid { get; set; } + public List OutputSubscriptions { get; set; } + + public PluginSubscription(Plugin plugin, Guid subscriptionStateGuid, List outputDeviceSubscriptions) + { + Plugin = plugin; + SubscriptionStateGuid = subscriptionStateGuid; + OutputSubscriptions = new List(); + + foreach (var deviceBinding in Plugin.Outputs) + { + if (!deviceBinding.IsBound) continue; + var outputDeviceSubscription = outputDeviceSubscriptions.FirstOrDefault(d => d.Device.Guid == deviceBinding.DeviceGuid); + OutputSubscriptions.Add(new OutputSubscription(deviceBinding, subscriptionStateGuid, outputDeviceSubscription)); + } + } + } +} diff --git a/UCR.Core/Models/Subscription/SubscriptionState.cs b/UCR.Core/Models/Subscription/SubscriptionState.cs index 9bb564b0..12f26cb6 100644 --- a/UCR.Core/Models/Subscription/SubscriptionState.cs +++ b/UCR.Core/Models/Subscription/SubscriptionState.cs @@ -1,66 +1,58 @@ using System; using System.Collections.Generic; -using System.Linq; -using UCR.Core.Models.Device; -namespace UCR.Core.Models.Subscription +namespace HidWizards.UCR.Core.Models.Subscription { public class SubscriptionState { public Guid StateGuid { get; } - public Profile.Profile ActiveProfile { get; } + public Profile ActiveProfile { get; } public bool IsActive { get; set; } - public List DeviceSubscriptions { get; } - public Dictionary> DeviceBindingSubscriptions { get; } - public Dictionary> OutputDeviceBindingSubscriptions { get; } - public List ActivePlugins { get; } + public List OutputDeviceSubscriptions { get; } + public List MappingSubscriptions { get; set; } - public SubscriptionState(Profile.Profile profile) + + public SubscriptionState(Profile profile) { StateGuid = Guid.NewGuid(); ActiveProfile = profile; - DeviceSubscriptions = new List(); - DeviceBindingSubscriptions = new Dictionary>(); - OutputDeviceBindingSubscriptions = new Dictionary>(); - ActivePlugins = new List(); + OutputDeviceSubscriptions = new List(); + MappingSubscriptions = new List(); IsActive = false; } - public void AddOutputDevice(Device.Device device, Profile.Profile profile) + public DeviceSubscription AddOutputDevice(Device device, Profile profile) { - DeviceSubscriptions.Add(new DeviceSubscription(device, profile)); + var deviceSubscription = new DeviceSubscription(device, profile); + OutputDeviceSubscriptions.Add(deviceSubscription); + return deviceSubscription; } - - public void AddDeviceBindingSubscriptions(Plugin.Plugin plugin) + + public void AddMappings(Profile profile, List profileOutputDevices) { - AddDeviceBindingSubscriptions(plugin, DeviceIoType.Input); - AddDeviceBindingSubscriptions(plugin, DeviceIoType.Output); - } + var profileMappings = new List(); - private void AddDeviceBindingSubscriptions(Plugin.Plugin plugin, DeviceIoType deviceIoType) - { - var deviceBindings = deviceIoType == DeviceIoType.Input ? plugin.GetInputs() : plugin.Outputs; - var deviceBindingsList = deviceIoType == DeviceIoType.Input ? DeviceBindingSubscriptions : OutputDeviceBindingSubscriptions; - if (deviceBindingsList.ContainsKey(plugin.Title)) + foreach (var profileMapping in profile.Mappings) { - foreach (var deviceBindingSubscription in deviceBindingsList[plugin.Title]) - { - deviceBindingSubscription.IsOverwritten = true; - } - deviceBindingsList[plugin.Title].AddRange(DeviceBindingSubscription.GetSubscriptionsFromList(deviceBindings, StateGuid, DeviceSubscriptions)); - } - else - { - deviceBindingsList[plugin.Title] = DeviceBindingSubscription.GetSubscriptionsFromList(deviceBindings, StateGuid, DeviceSubscriptions); + profileMappings.Add(new MappingSubscription(profile, profileMapping, StateGuid, profileOutputDevices)); } + + OverrideParentMappings(profileMappings); + MappingSubscriptions.AddRange(profileMappings); } - public void BuildActivePluginsList() + private void OverrideParentMappings(List profileMappingSubscriptions) { - foreach (var subscription in DeviceBindingSubscriptions) + foreach (var profileMappingSubscription in profileMappingSubscriptions) { - ActivePlugins.Add(subscription.Value.First(d => d.IsOverwritten == false).DeviceBinding.Plugin); + foreach (var subscription in MappingSubscriptions) + { + if (profileMappingSubscription.Mapping.Title.Equals(subscription.Mapping.Title)) + { + subscription.Overriden = true; + } + } } } } diff --git a/UCR.Core/Properties/AssemblyInfo.cs b/UCR.Core/Properties/AssemblyInfo.cs index 3752d3d6..6d5ec423 100644 --- a/UCR.Core/Properties/AssemblyInfo.cs +++ b/UCR.Core/Properties/AssemblyInfo.cs @@ -31,11 +31,11 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +// [assembly: AssemblyVersion("0.2.1.0")] +[assembly: AssemblyVersion("0.2.1.0")] +[assembly: AssemblyFileVersion("0.2.1.0")] // Make internal visible for tests [assembly: InternalsVisibleTo("UCR")] [assembly: InternalsVisibleTo("UCR.Tests")] -[assembly: AssemblyInformationalVersion("0.1.0+92.Branch.master.Sha.243cf89d1765596e93767c63363e9548ab86fdc2")] +[assembly: AssemblyInformationalVersion("0.2.1-Restructure.1+3.Branch.Restructure.Sha.88ebad1368a193b56f94c186d418205b2069510e")] diff --git a/UCR.Core/UCR.Core.csproj b/UCR.Core/UCR.Core.csproj index cd1b2c8f..0c687e08 100644 --- a/UCR.Core/UCR.Core.csproj +++ b/UCR.Core/UCR.Core.csproj @@ -7,7 +7,7 @@ {676D3228-C3F0-4BE7-8951-B83F7A16CEA7} Library Properties - UCR.Core + HidWizards.UCR.Core UCR.Core v4.5.2 512 @@ -30,11 +30,14 @@ 4 - - ..\dependencies\IOController\IOWrapper.dll + + ..\dependencies\IOWrapper\IOWrapper.Core.dll - - ..\dependencies\IOController\IProvider.dll + + ..\dependencies\IOWrapper\IOWrapper.DTOs.dll + + + ..\dependencies\IOWrapper\IOWrapper.IProvider.dll ..\packages\Mono.Options.5.3.0.1\lib\net4-client\Mono.Options.dll @@ -55,24 +58,36 @@ + + + + + - - + + + - - - - - + + + + + + + + + + + @@ -84,7 +99,16 @@ + + + nuget restore $(SolutionDir)\submodules\IOWrapper\IOWrapper.sln +"$(MSBuildBinPath)\msbuild.exe" /t:restore $(SolutionDir)\submodules\IOWrapper\IOWrapper.sln +"$(MSBuildBinPath)\msbuild.exe" $(SolutionDir)\submodules\IOWrapper\IOWrapper.sln + + + xcopy /Y /S "$(SolutionDir)\submodules\IOWrapper\Artifacts" "$(SolutionDir)\dependencies" + + * + False + + + + + + + + + + + + + + + + + + diff --git a/build/.build.csproj.DotSettings b/build/.build.csproj.DotSettings new file mode 100644 index 00000000..5af2bdc9 --- /dev/null +++ b/build/.build.csproj.DotSettings @@ -0,0 +1,23 @@ + + False + Implicit + Implicit + ExpressionBody + 0 + NEXT_LINE + True + False + 120 + IF_OWNER_IS_SINGLE_LINE + WRAP_IF_LONG + False + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True + True + True + True + True diff --git a/build/Build.cs b/build/Build.cs new file mode 100644 index 00000000..00aef004 --- /dev/null +++ b/build/Build.cs @@ -0,0 +1,99 @@ +using System; +using System.Linq; +using Nuke.Common; +using Nuke.Common.Git; +using Nuke.Common.Gitter; +using Nuke.Common.Tools.DotNet; +using Nuke.Common.Tools.GitVersion; +using Nuke.Common.Tools.MSBuild; +using Nuke.Common.Tools.NuGet; +using Nuke.Core; +using Nuke.Core.Tooling; +using static Nuke.Common.Tools.Git.GitTasks; +using static Nuke.Common.Tools.MSBuild.MSBuildTasks; +using static Nuke.Core.IO.FileSystemTasks; +using static Nuke.Core.IO.PathConstruction; +using static Nuke.Core.EnvironmentInfo; + +class Build : NukeBuild +{ + // Console application entry. Also defines the default target. + public static int Main() => Execute(x => x.Test); + + // Auto-injection fields: + + // [GitVersion] readonly GitVersion GitVersion; + // Semantic versioning. Must have 'GitVersion.CommandLine' referenced. + + // [GitRepository] readonly GitRepository GitRepository; + // Parses origin, branch name and head from git config. + + // [Parameter] readonly string MyGetApiKey; + // Returns command-line arguments and environment variables. + + string IoWrapper => "IOWrapper"; + AbsolutePath IoWrapperDirectory => RootDirectory / "submodules" / IoWrapper; + AbsolutePath IoWrapperSolution => IoWrapperDirectory / (IoWrapper + ".sln"); + + MSBuildSettings DefaultIoWrapperMSBuild => DefaultMSBuild + .SetWorkingDirectory(IoWrapperDirectory) + .SetSolutionFile(IoWrapperSolution); + + + Target Clean => _ => _ + .OnlyWhen(() => false) // Disabled for safety. + .Executes(() => + { + DeleteDirectories(GlobDirectories(SourceDirectory, "**/bin", "**/obj")); + EnsureCleanDirectory(OutputDirectory); + }); + + Target RestoreSubmodules => _ => _ + .Executes(() => + { + Git("submodule init"); + Git("submodule update"); + MSBuild(s => DefaultIoWrapperMSBuild + .SetTargets("Restore") + ); + NuGetTasks.NuGetRestore(s => s.SetTargetPath(IoWrapperSolution)); + }); + + Target CompileSubmodules => _ => _ + .DependsOn(RestoreSubmodules) + .Executes(() => + { + MSBuild(s => DefaultIoWrapperMSBuild + .SetTargets("Rebuild") + ); + }); + + Target InitProject => _ => _ + .DependsOn(RestoreSubmodules) + .DependsOn(CompileSubmodules) + .Executes(() => + { + CopyRecursively(IoWrapperDirectory / "Artifacts", SolutionDirectory / "dependencies", FileExistsPolicy.Overwrite); + }); + + Target Restore => _ => _ + .DependsOn(Clean) + .Executes(() => + { + MSBuild(s => DefaultMSBuildRestore); + }); + + Target Compile => _ => _ + .DependsOn(Restore) + .Executes(() => + { + MSBuild(s => DefaultMSBuildCompile); + }); + + Target Test => _ => _ + .DependsOn(Compile) + .Executes(() => + { + MSBuild(s => DefaultMSBuildCompile); + }); +} diff --git a/build_cake.ps1 b/build_cake.ps1 new file mode 100644 index 00000000..265bd123 --- /dev/null +++ b/build_cake.ps1 @@ -0,0 +1,234 @@ +########################################################################## +# This is the Cake bootstrapper script for PowerShell. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +<# + +.SYNOPSIS +This is a Powershell script to bootstrap a Cake build. + +.DESCRIPTION +This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) +and execute your Cake build script with the parameters you provide. + +.PARAMETER Script +The build script to execute. +.PARAMETER Target +The build script target to run. +.PARAMETER Configuration +The build configuration to use. +.PARAMETER Verbosity +Specifies the amount of information to be displayed. +.PARAMETER ShowDescription +Shows description about tasks. +.PARAMETER DryRun +Performs a dry run. +.PARAMETER Experimental +Uses the nightly builds of the Roslyn script engine. +.PARAMETER Mono +Uses the Mono Compiler rather than the Roslyn script engine. +.PARAMETER SkipToolPackageRestore +Skips restoring of packages. +.PARAMETER ScriptArgs +Remaining arguments are added here. + +.LINK +https://cakebuild.net + +#> + +[CmdletBinding()] +Param( + [string]$Script = "build.cake", + [string]$Target, + [string]$Configuration, + [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] + [string]$Verbosity, + [switch]$ShowDescription, + [Alias("WhatIf", "Noop")] + [switch]$DryRun, + [switch]$Experimental, + [switch]$Mono, + [switch]$SkipToolPackageRestore, + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$ScriptArgs +) + +[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null +function MD5HashFile([string] $filePath) +{ + if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) + { + return $null + } + + [System.IO.Stream] $file = $null; + [System.Security.Cryptography.MD5] $md5 = $null; + try + { + $md5 = [System.Security.Cryptography.MD5]::Create() + $file = [System.IO.File]::OpenRead($filePath) + return [System.BitConverter]::ToString($md5.ComputeHash($file)) + } + finally + { + if ($file -ne $null) + { + $file.Dispose() + } + } +} + +function GetProxyEnabledWebClient +{ + $wc = New-Object System.Net.WebClient + $proxy = [System.Net.WebRequest]::GetSystemWebProxy() + $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $wc.Proxy = $proxy + return $wc +} + +Write-Host "Preparing to run build script..." + +if(!$PSScriptRoot){ + $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent +} + +$TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" +$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" +$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" +$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" +$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" +$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" +$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" +$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" +$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" + +# Make sure tools folder exists +if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { + Write-Verbose -Message "Creating tools directory..." + New-Item -Path $TOOLS_DIR -Type directory | out-null +} + +# Make sure that packages.config exist. +if (!(Test-Path $PACKAGES_CONFIG)) { + Write-Verbose -Message "Downloading packages.config..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { + Throw "Could not download packages.config." + } +} + +# Try find NuGet.exe in path if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Trying to find nuget.exe in PATH..." + $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } + $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 + if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { + Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." + $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName + } +} + +# Try download NuGet.exe if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Downloading NuGet.exe..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile($NUGET_URL, $NUGET_EXE) + } catch { + Throw "Could not download NuGet.exe." + } +} + +# Save nuget.exe path to environment to be available to child processed +$ENV:NUGET_EXE = $NUGET_EXE + +# Restore tools from NuGet? +if(-Not $SkipToolPackageRestore.IsPresent) { + Push-Location + Set-Location $TOOLS_DIR + + # Check for changes in packages.config and remove installed tools if true. + [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) + if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or + ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { + Write-Verbose -Message "Missing or changed package.config hash..." + Remove-Item * -Recurse -Exclude packages.config,nuget.exe + } + + Write-Verbose -Message "Restoring tools from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occured while restoring NuGet tools." + } + else + { + $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" + } + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore addins from NuGet +if (Test-Path $ADDINS_PACKAGES_CONFIG) { + Push-Location + Set-Location $ADDINS_DIR + + Write-Verbose -Message "Restoring addins from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occured while restoring NuGet addins." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore modules from NuGet +if (Test-Path $MODULES_PACKAGES_CONFIG) { + Push-Location + Set-Location $MODULES_DIR + + Write-Verbose -Message "Restoring modules from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occured while restoring NuGet modules." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Make sure that Cake has been installed. +if (!(Test-Path $CAKE_EXE)) { + Throw "Could not find Cake.exe at $CAKE_EXE" +} + + + +# Build Cake arguments +$cakeArguments = @("$Script"); +if ($Target) { $cakeArguments += "-target=$Target" } +if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } +if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } +if ($ShowDescription) { $cakeArguments += "-showdescription" } +if ($DryRun) { $cakeArguments += "-dryrun" } +if ($Experimental) { $cakeArguments += "-experimental" } +if ($Mono) { $cakeArguments += "-mono" } +$cakeArguments += $ScriptArgs + +# Start Cake +Write-Host "Running build script..." +&$CAKE_EXE $cakeArguments +exit $LASTEXITCODE diff --git a/submodules/IOWrapper b/submodules/IOWrapper new file mode 160000 index 00000000..bb8efbe2 --- /dev/null +++ b/submodules/IOWrapper @@ -0,0 +1 @@ +Subproject commit bb8efbe2636d6919b4e2fcc8fcf64465c2bd19a6