mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-01-26 02:44:57 +00:00
Compare commits
4 Commits
1.12.9-alp
...
1.12.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
300c675341 | ||
|
|
a4290e68b1 | ||
|
|
c14e5fe449 | ||
|
|
2677d63553 |
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Bridges
|
||||
{
|
||||
public class IAnalogInputJoinMap : JoinMapBaseAdvanced
|
||||
{
|
||||
|
||||
[JoinName("InputValue")]
|
||||
public JoinDataComplete InputValue = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
|
||||
new JoinMetadata { Description = "Input Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
|
||||
[JoinName("MinimumChange")]
|
||||
public JoinDataComplete MinimumChange = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
|
||||
new JoinMetadata { Description = "Minimum voltage change required to reflect a change", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to use when instantiating this Join Map without inheriting from it
|
||||
/// </summary>
|
||||
/// <param name="joinStart">Join this join map will start at</param>
|
||||
public IAnalogInputJoinMap(uint joinStart)
|
||||
: this(joinStart, typeof(IAnalogInputJoinMap))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to use when extending this Join map
|
||||
/// </summary>
|
||||
/// <param name="joinStart">Join this join map will start at</param>
|
||||
/// <param name="type">Type of the child join map</param>
|
||||
protected IAnalogInputJoinMap(uint joinStart, Type type)
|
||||
: base(joinStart, type)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace PepperDash.Essentials.Core.Bridges
|
||||
|
||||
[JoinName("InputState")]
|
||||
public JoinDataComplete InputState = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
|
||||
new JoinMetadata { Description = "Room Email Url", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
|
||||
new JoinMetadata { Description = "Input State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to use when instantiating this Join Map without inheriting from it
|
||||
|
||||
@@ -15,5 +15,7 @@ namespace PepperDash.Essentials.Core.CrestronIO
|
||||
public uint PortNumber { get; set; }
|
||||
[JsonProperty("disablePullUpResistor")]
|
||||
public bool DisablePullUpResistor { get; set; }
|
||||
[JsonProperty("minimumChange")]
|
||||
public int MinimumChange { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Core.Bridges;
|
||||
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials.Core.CrestronIO
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic digital input deviced tied to a versiport
|
||||
/// </summary>
|
||||
public class GenericVersiportAnalogInputDevice : EssentialsBridgeableDevice, IAnalogInput
|
||||
{
|
||||
public Versiport InputPort { get; private set; }
|
||||
|
||||
public IntFeedback InputValueFeedback { get; private set; }
|
||||
public IntFeedback InputMinimumChangeFeedback { get; private set; }
|
||||
|
||||
Func<int> InputValueFeedbackFunc
|
||||
{
|
||||
get
|
||||
{
|
||||
return () => InputPort.AnalogIn;
|
||||
}
|
||||
}
|
||||
|
||||
Func<int> InputMinimumChangeFeedbackFunc
|
||||
{
|
||||
get { return () => InputPort.AnalogMinChange; }
|
||||
}
|
||||
|
||||
public GenericVersiportAnalogInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
|
||||
base(key, name)
|
||||
{
|
||||
InputValueFeedback = new IntFeedback(InputValueFeedbackFunc);
|
||||
InputMinimumChangeFeedback = new IntFeedback(InputMinimumChangeFeedbackFunc);
|
||||
|
||||
AddPostActivationAction(() =>
|
||||
{
|
||||
InputPort = postActivationFunc(config);
|
||||
|
||||
InputPort.Register();
|
||||
|
||||
InputPort.SetVersiportConfiguration(eVersiportConfiguration.AnalogInput);
|
||||
InputPort.AnalogMinChange = (ushort)(config.MinimumChange > 0 ? config.MinimumChange : 655);
|
||||
if (config.DisablePullUpResistor)
|
||||
InputPort.DisablePullUpResistor = true;
|
||||
|
||||
InputPort.VersiportChange += InputPort_VersiportChange;
|
||||
|
||||
Debug.Console(1, this, "Created GenericVersiportAnalogInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", config.PortNumber, InputPort.DisablePullUpResistor);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set minimum voltage change for device to update voltage changed method
|
||||
/// </summary>
|
||||
/// <param name="value">valid values range from 0 - 65535, representing the full 100% range of the processor voltage source. Check processor documentation for details</param>
|
||||
public void SetMinimumChange(ushort value)
|
||||
{
|
||||
InputPort.AnalogMinChange = value;
|
||||
}
|
||||
|
||||
void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)
|
||||
{
|
||||
Debug.Console(1, this, "Versiport change: {0}", args.Event);
|
||||
|
||||
if(args.Event == eVersiportEvent.AnalogInChange)
|
||||
InputValueFeedback.FireUpdate();
|
||||
if (args.Event == eVersiportEvent.AnalogMinChangeChange)
|
||||
InputMinimumChangeFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
|
||||
#region Bridge Linking
|
||||
|
||||
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
|
||||
{
|
||||
var joinMap = new IAnalogInputJoinMap(joinStart);
|
||||
|
||||
var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
|
||||
|
||||
if (!string.IsNullOrEmpty(joinMapSerialized))
|
||||
joinMap = JsonConvert.DeserializeObject<IAnalogInputJoinMap>(joinMapSerialized);
|
||||
|
||||
if (bridge != null)
|
||||
{
|
||||
bridge.AddJoinMap(Key, joinMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
|
||||
|
||||
// Link feedback for input state
|
||||
InputValueFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputValue.JoinNumber]);
|
||||
InputMinimumChangeFeedback.LinkInputSig(trilist.UShortInput[joinMap.MinimumChange.JoinNumber]);
|
||||
trilist.SetUShortSigAction(joinMap.MinimumChange.JoinNumber, SetMinimumChange);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Console(1, this, "Unable to link device '{0}'. Input is null", Key);
|
||||
Debug.Console(1, this, "Error: {0}", e);
|
||||
}
|
||||
|
||||
trilist.OnlineStatusChange += (d, args) =>
|
||||
{
|
||||
if (!args.DeviceOnLine) return;
|
||||
InputValueFeedback.FireUpdate();
|
||||
InputMinimumChangeFeedback.FireUpdate();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void trilist_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public static Versiport GetVersiportDigitalInput(IOPortConfig dc)
|
||||
{
|
||||
|
||||
IIOPorts ioPortDevice;
|
||||
|
||||
if (dc.PortDeviceKey.Equals("processor"))
|
||||
{
|
||||
if (!Global.ControlSystem.SupportsVersiport)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportAnalogInput: Processor does not support Versiports");
|
||||
return null;
|
||||
}
|
||||
ioPortDevice = Global.ControlSystem;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts;
|
||||
if (ioPortDev == null)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportAnalogInput: Device {0} is not a valid device", dc.PortDeviceKey);
|
||||
return null;
|
||||
}
|
||||
ioPortDevice = ioPortDev;
|
||||
}
|
||||
if (ioPortDevice == null)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportAnalogInput: Device '0' is not a valid IIOPorts Device", dc.PortDeviceKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportAnalogInput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
|
||||
return null;
|
||||
}
|
||||
if(!ioPortDevice.VersiPorts[dc.PortNumber].SupportsAnalogInput)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportAnalogInput: Device {0} does not support AnalogInput on port {1}", dc.PortDeviceKey, dc.PortNumber);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return ioPortDevice.VersiPorts[dc.PortNumber];
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class GenericVersiportAbalogInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportAnalogInputDevice>
|
||||
{
|
||||
public GenericVersiportAbalogInputDeviceFactory()
|
||||
{
|
||||
TypeNames = new List<string>() { "versiportanaloginput" };
|
||||
}
|
||||
|
||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||
{
|
||||
Debug.Console(1, "Factory Attempting to create new Generic Versiport Device");
|
||||
|
||||
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());
|
||||
|
||||
if (props == null) return null;
|
||||
|
||||
var portDevice = new GenericVersiportAnalogInputDevice(dc.Key, dc.Name, GenericVersiportAnalogInputDevice.GetVersiportDigitalInput, props);
|
||||
|
||||
return portDevice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Core.CrestronIO
|
||||
{
|
||||
public interface IAnalogInput
|
||||
{
|
||||
IntFeedback InputValueFeedback { get; }
|
||||
}
|
||||
}
|
||||
@@ -42,18 +42,19 @@ namespace PepperDash.Essentials.Core.CrestronIO
|
||||
OutputPort = postActivationFunc(config);
|
||||
|
||||
OutputPort.Register();
|
||||
|
||||
|
||||
if (!OutputPort.SupportsDigitalOutput)
|
||||
{
|
||||
Debug.Console(0, this, "Device does not support configuration as a Digital Output");
|
||||
return;
|
||||
}
|
||||
|
||||
OutputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput);
|
||||
|
||||
|
||||
OutputPort.VersiportChange += OutputPort_VersiportChange;
|
||||
|
||||
|
||||
|
||||
Debug.Console(1, this, "Created GenericVersiportDigitalOutputDevice on port '{0}'.", config.PortNumber);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -72,7 +73,18 @@ namespace PepperDash.Essentials.Core.CrestronIO
|
||||
/// <param name="state">value to set the output to</param>
|
||||
public void SetOutput(bool state)
|
||||
{
|
||||
OutputPort.DigitalOut = state;
|
||||
if (OutputPort.SupportsDigitalOutput)
|
||||
{
|
||||
Debug.Console(0, this, "Passed the Check");
|
||||
|
||||
OutputPort.DigitalOut = state;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(0, this, "Versiport does not support Digital Output Mode");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region Bridge Linking
|
||||
@@ -115,40 +127,40 @@ namespace PepperDash.Essentials.Core.CrestronIO
|
||||
|
||||
public static Versiport GetVersiportDigitalOutput(IOPortConfig dc)
|
||||
{
|
||||
|
||||
IIOPorts ioPortDevice;
|
||||
|
||||
if (dc.PortDeviceKey.Equals("processor"))
|
||||
{
|
||||
if (!Global.ControlSystem.SupportsVersiport)
|
||||
IIOPorts ioPortDevice;
|
||||
|
||||
if (dc.PortDeviceKey.Equals("processor"))
|
||||
{
|
||||
Debug.Console(0, "GetVersiportDigitalOuptut: Processor does not support Versiports");
|
||||
if (!Global.ControlSystem.SupportsVersiport)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportDigitalOuptut: Processor does not support Versiports");
|
||||
return null;
|
||||
}
|
||||
ioPortDevice = Global.ControlSystem;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts;
|
||||
if (ioPortDev == null)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportDigitalOuptut: Device {0} is not a valid device", dc.PortDeviceKey);
|
||||
return null;
|
||||
}
|
||||
ioPortDevice = ioPortDev;
|
||||
}
|
||||
if (ioPortDevice == null)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportDigitalOuptut: Device '0' is not a valid IOPorts Device", dc.PortDeviceKey);
|
||||
return null;
|
||||
}
|
||||
ioPortDevice = Global.ControlSystem;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts;
|
||||
if (ioPortDev == null)
|
||||
|
||||
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportDigitalOuptut: Device {0} is not a valid device", dc.PortDeviceKey);
|
||||
return null;
|
||||
Debug.Console(0, "GetVersiportDigitalOuptut: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
|
||||
}
|
||||
ioPortDevice = ioPortDev;
|
||||
}
|
||||
if (ioPortDevice == null)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportDigitalOuptut: Device '0' is not a valid IIOPorts Device", dc.PortDeviceKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
|
||||
{
|
||||
Debug.Console(0, "GetVersiportDigitalOuptut: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
|
||||
}
|
||||
|
||||
return ioPortDevice.VersiPorts[dc.PortNumber];
|
||||
var port = ioPortDevice.VersiPorts[dc.PortNumber];
|
||||
return port;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
<Compile Include="Bridges\IBridge.cs" />
|
||||
<Compile Include="Bridges\JoinMaps\AirMediaControllerJoinMap.cs" />
|
||||
<Compile Include="Bridges\JoinMaps\AppleTvJoinMap.cs" />
|
||||
<Compile Include="Bridges\JoinMaps\IAnalogInputJoinMap.cs" />
|
||||
<Compile Include="Bridges\JoinMaps\IDigitalOutputJoinMap.cs" />
|
||||
<Compile Include="Bridges\JoinMaps\PduJoinMapBase.cs" />
|
||||
<Compile Include="Bridges\JoinMaps\C2nRthsControllerJoinMap.cs" />
|
||||
@@ -182,8 +183,10 @@
|
||||
<Compile Include="Crestron IO\DinCenCn\IHasCresnetBranches.cs" />
|
||||
<Compile Include="Crestron IO\DinIo8\DinIo8Controller.cs" />
|
||||
<Compile Include="Crestron IO\Inputs\CenIoDigIn104Controller.cs" />
|
||||
<Compile Include="Crestron IO\Inputs\GenericVersiportAnalogInputDevice.cs" />
|
||||
<Compile Include="Crestron IO\Inputs\GenericDigitalInputDevice.cs" />
|
||||
<Compile Include="Crestron IO\Inputs\GenericVersiportInputDevice.cs" />
|
||||
<Compile Include="Crestron IO\Inputs\IAnalogInput.cs" />
|
||||
<Compile Include="Crestron IO\Inputs\IDigitalInput.cs" />
|
||||
<Compile Include="Crestron IO\IOPortConfig.cs" />
|
||||
<Compile Include="Crestron IO\Ir\CenIoIr104Controller.cs" />
|
||||
|
||||
@@ -1,425 +1,425 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DM;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class RouteRequest
|
||||
{
|
||||
public IRoutingSink Destination {get; set;}
|
||||
public IRoutingOutputs Source {get; set;}
|
||||
public eRoutingSignalType SignalType {get; set;}
|
||||
|
||||
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||
{
|
||||
var coolingDevice = sender as IWarmingCooling;
|
||||
|
||||
if(args.BoolValue == false)
|
||||
{
|
||||
Destination.ReleaseAndMakeRoute(Source, SignalType);
|
||||
|
||||
if(sender == null) return;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
|
||||
/// on those destinations.
|
||||
/// </summary>
|
||||
public static class IRoutingInputsExtensions
|
||||
{
|
||||
private static Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
||||
/// <summary>
|
||||
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
||||
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
||||
/// in RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
public static void ReleaseAndMakeRoute(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
||||
{
|
||||
var routeRequest = new RouteRequest {
|
||||
Destination = destination,
|
||||
Source = source,
|
||||
SignalType = signalType
|
||||
};
|
||||
|
||||
var coolingDevice = destination as IWarmingCooling;
|
||||
|
||||
RouteRequest existingRouteRequest;
|
||||
|
||||
//We already have a route request for this device, and it's a cooling device and is cooling
|
||||
if (RouteRequests.TryGetValue(destination.Key, out existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||
{
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DM;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class RouteRequest
|
||||
{
|
||||
public IRoutingSink Destination {get; set;}
|
||||
public IRoutingOutputs Source {get; set;}
|
||||
public eRoutingSignalType SignalType {get; set;}
|
||||
|
||||
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||
{
|
||||
var coolingDevice = sender as IWarmingCooling;
|
||||
|
||||
if(args.BoolValue == false)
|
||||
{
|
||||
Destination.ReleaseAndMakeRoute(Source, SignalType);
|
||||
|
||||
if(sender == null) return;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
|
||||
/// on those destinations.
|
||||
/// </summary>
|
||||
public static class IRoutingInputsExtensions
|
||||
{
|
||||
private static Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
||||
/// <summary>
|
||||
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
||||
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
||||
/// in RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
public static void ReleaseAndMakeRoute(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
||||
{
|
||||
var routeRequest = new RouteRequest {
|
||||
Destination = destination,
|
||||
Source = source,
|
||||
SignalType = signalType
|
||||
};
|
||||
|
||||
var coolingDevice = destination as IWarmingCooling;
|
||||
|
||||
RouteRequest existingRouteRequest;
|
||||
|
||||
//We already have a route request for this device, and it's a cooling device and is cooling
|
||||
if (RouteRequests.TryGetValue(destination.Key, out existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||
{
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||
|
||||
RouteRequests[destination.Key] = routeRequest;
|
||||
|
||||
Debug.Console(2, "******************************************************** Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//New Request
|
||||
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||
{
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||
|
||||
Debug.Console(2, "******************************************************** Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//New Request
|
||||
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||
{
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||
|
||||
RouteRequests.Add(destination.Key, routeRequest);
|
||||
|
||||
Debug.Console(2, "******************************************************** Device: {0} is cooling down. Storing route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
||||
{
|
||||
RouteRequests.Remove(destination.Key);
|
||||
Debug.Console(2, "******************************************************** Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", destination.Key, routeRequest.Source.Key);
|
||||
}
|
||||
|
||||
destination.ReleaseRoute();
|
||||
|
||||
RunRouteRequest(routeRequest);
|
||||
}
|
||||
|
||||
public static void RunRouteRequest(RouteRequest request)
|
||||
{
|
||||
if (request.Source == null) return;
|
||||
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
|
||||
if (newRoute == null) return;
|
||||
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
|
||||
Debug.Console(2, request.Destination, "Executing full route");
|
||||
newRoute.ExecuteRoutes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route on the destination, if it is found in
|
||||
/// RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
public static void ReleaseRoute(this IRoutingSink destination)
|
||||
{
|
||||
RouteRequest existingRequest;
|
||||
|
||||
if (RouteRequests.TryGetValue(destination.Key, out existingRequest) && destination is IWarmingCooling)
|
||||
{
|
||||
var coolingDevice = destination as IWarmingCooling;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
|
||||
}
|
||||
|
||||
RouteRequests.Remove(destination.Key);
|
||||
|
||||
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
|
||||
if (current != null)
|
||||
{
|
||||
Debug.Console(1, destination, "Releasing current route: {0}", current.Source.Key);
|
||||
current.ReleaseRoutes();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
||||
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
||||
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
|
||||
/// of an audio/video route are discovered a route descriptor is returned. If no route is
|
||||
/// discovered, then null is returned
|
||||
/// </summary>
|
||||
public static RouteDescriptor GetRouteToSource(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
||||
{
|
||||
var routeDescr = new RouteDescriptor(source, destination, signalType);
|
||||
// if it's a single signal type, find the route
|
||||
if ((signalType & (eRoutingSignalType.Audio & eRoutingSignalType.Video)) == (eRoutingSignalType.Audio & eRoutingSignalType.Video))
|
||||
{
|
||||
Debug.Console(1, destination, "Attempting to build source route from {0}", source.Key);
|
||||
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescr))
|
||||
routeDescr = null;
|
||||
}
|
||||
// otherwise, audioVideo needs to be handled as two steps.
|
||||
else
|
||||
{
|
||||
Debug.Console(1, destination, "Attempting to build audio and video routes from {0}", source.Key);
|
||||
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescr);
|
||||
if (!audioSuccess)
|
||||
Debug.Console(1, destination, "Cannot find audio route to {0}", source.Key);
|
||||
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescr);
|
||||
if (!videoSuccess)
|
||||
Debug.Console(1, destination, "Cannot find video route to {0}", source.Key);
|
||||
if (!audioSuccess && !videoSuccess)
|
||||
routeDescr = null;
|
||||
}
|
||||
|
||||
//Debug.Console(1, destination, "Route{0} discovered", routeDescr == null ? " NOT" : "");
|
||||
return routeDescr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The recursive part of this. Will stop on each device, search its inputs for the
|
||||
/// desired source and if not found, invoke this function for the each input port
|
||||
/// hoping to find the source.
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
|
||||
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
|
||||
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
|
||||
/// <param name="cycle">Just an informational counter</param>
|
||||
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
|
||||
/// <returns>true if source is hit</returns>
|
||||
static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
|
||||
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
|
||||
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable)
|
||||
{
|
||||
cycle++;
|
||||
Debug.Console(2, "GetRouteToSource: {0} {1}--> {2}", cycle, source.Key, destination.Key);
|
||||
|
||||
RoutingInputPort goodInputPort = null;
|
||||
var destDevInputTies = TieLineCollection.Default.Where(t =>
|
||||
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || (t.Type & (eRoutingSignalType.Audio | eRoutingSignalType.Video)) == (eRoutingSignalType.Audio | eRoutingSignalType.Video)));
|
||||
|
||||
// find a direct tie
|
||||
var directTie = destDevInputTies.FirstOrDefault(
|
||||
t => t.DestinationPort.ParentDevice == destination
|
||||
&& t.SourcePort.ParentDevice == source);
|
||||
if (directTie != null) // Found a tie directly to the source
|
||||
{
|
||||
goodInputPort = directTie.DestinationPort;
|
||||
}
|
||||
else // no direct-connect. Walk back devices.
|
||||
{
|
||||
Debug.Console(2, destination, "is not directly connected to {0}. Walking down tie lines", source.Key);
|
||||
|
||||
// No direct tie? Run back out on the inputs' attached devices...
|
||||
// Only the ones that are routing devices
|
||||
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
|
||||
|
||||
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
|
||||
if (alreadyCheckedDevices == null)
|
||||
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
|
||||
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
|
||||
|
||||
foreach (var inputTieToTry in attachedMidpoints)
|
||||
{
|
||||
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
|
||||
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
|
||||
Debug.Console(2, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key);
|
||||
|
||||
// Check if this previous device has already been walked
|
||||
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
|
||||
{
|
||||
Debug.Console(2, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key);
|
||||
continue;
|
||||
}
|
||||
// haven't seen this device yet. Do it. Pass the output port to the next
|
||||
// level to enable switching on success
|
||||
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
|
||||
alreadyCheckedDevices, signalType, cycle, routeTable);
|
||||
if (upstreamRoutingSuccess)
|
||||
{
|
||||
Debug.Console(2, destination, "Upstream device route found");
|
||||
goodInputPort = inputTieToTry.DestinationPort;
|
||||
break; // Stop looping the inputs in this cycle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we have a route on corresponding inputPort. *** Do the route ***
|
||||
if (goodInputPort != null)
|
||||
{
|
||||
//Debug.Console(2, destination, "adding RouteDescriptor");
|
||||
if (outputPortToUse == null)
|
||||
{
|
||||
// it's a sink device
|
||||
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
|
||||
}
|
||||
else if (destination is IRouting)
|
||||
{
|
||||
routeTable.Routes.Add(new RouteSwitchDescriptor (outputPortToUse, goodInputPort));
|
||||
}
|
||||
else // device is merely IRoutingInputOutputs
|
||||
Debug.Console(2, destination, " No routing. Passthrough device");
|
||||
//Debug.Console(2, destination, "Exiting cycle {0}", cycle);
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.Console(2, destination, "No route found to {0}", source.Key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
|
||||
/// </summary>
|
||||
public class RouteDescriptorCollection
|
||||
{
|
||||
public static RouteDescriptorCollection DefaultCollection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DefaultCollection == null)
|
||||
_DefaultCollection = new RouteDescriptorCollection();
|
||||
return _DefaultCollection;
|
||||
}
|
||||
}
|
||||
static RouteDescriptorCollection _DefaultCollection;
|
||||
|
||||
List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
|
||||
/// destination exists already, it will not be added - in order to preserve
|
||||
/// proper route releasing.
|
||||
/// </summary>
|
||||
/// <param name="descriptor"></param>
|
||||
public void AddRouteDescriptor(RouteDescriptor descriptor)
|
||||
{
|
||||
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
|
||||
{
|
||||
Debug.Console(1, descriptor.Destination,
|
||||
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
|
||||
return;
|
||||
}
|
||||
RouteDescriptors.Add(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RouteDescriptor for a destination
|
||||
/// </summary>
|
||||
/// <returns>null if no RouteDescriptor for a destination exists</returns>
|
||||
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
|
||||
{
|
||||
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
|
||||
/// Returns null if no route with the provided destination exists.
|
||||
/// </summary>
|
||||
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
|
||||
{
|
||||
var descr = GetRouteDescriptorForDestination(destination);
|
||||
if (descr != null)
|
||||
RouteDescriptors.Remove(descr);
|
||||
return descr;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an collection of individual route steps between Source and Destination
|
||||
/// </summary>
|
||||
public class RouteDescriptor
|
||||
{
|
||||
public IRoutingInputs Destination { get; private set; }
|
||||
public IRoutingOutputs Source { get; private set; }
|
||||
public eRoutingSignalType SignalType { get; private set; }
|
||||
public List<RouteSwitchDescriptor> Routes { get; private set; }
|
||||
|
||||
|
||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
|
||||
{
|
||||
Destination = destination;
|
||||
Source = source;
|
||||
SignalType = signalType;
|
||||
Routes = new List<RouteSwitchDescriptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all routes described in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ExecuteRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
Debug.Console(2, "ExecuteRoutes: {0}", route.ToString());
|
||||
if (route.SwitchingDevice is IRoutingSink)
|
||||
{
|
||||
var device = route.SwitchingDevice as IRoutingSinkWithSwitching;
|
||||
if (device == null)
|
||||
continue;
|
||||
|
||||
device.ExecuteSwitch(route.InputPort.Selector);
|
||||
}
|
||||
else if (route.SwitchingDevice is IRouting)
|
||||
{
|
||||
(route.SwitchingDevice as IRouting).ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
|
||||
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
|
||||
Debug.Console(2, "Output port {0} routing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all routes in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ReleaseRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
if (route.SwitchingDevice is IRouting)
|
||||
{
|
||||
// Pull the route from the port. Whatever is watching the output's in use tracker is
|
||||
// responsible for responding appropriately.
|
||||
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
|
||||
Debug.Console(2, "Port {0} releasing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an individual link for a route
|
||||
/// </summary>
|
||||
public class RouteSwitchDescriptor
|
||||
{
|
||||
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
|
||||
public RouteSwitchDescriptor(RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
}
|
||||
|
||||
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
OutputPort = outputPort;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if(SwitchingDevice is IRouting)
|
||||
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
|
||||
else
|
||||
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destination.ReleaseRoute();
|
||||
|
||||
RunRouteRequest(routeRequest);
|
||||
}
|
||||
|
||||
public static void RunRouteRequest(RouteRequest request)
|
||||
{
|
||||
if (request.Source == null) return;
|
||||
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
|
||||
if (newRoute == null) return;
|
||||
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
|
||||
Debug.Console(2, request.Destination, "Executing full route");
|
||||
newRoute.ExecuteRoutes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route on the destination, if it is found in
|
||||
/// RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
public static void ReleaseRoute(this IRoutingSink destination)
|
||||
{
|
||||
RouteRequest existingRequest;
|
||||
|
||||
if (RouteRequests.TryGetValue(destination.Key, out existingRequest) && destination is IWarmingCooling)
|
||||
{
|
||||
var coolingDevice = destination as IWarmingCooling;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
|
||||
}
|
||||
|
||||
RouteRequests.Remove(destination.Key);
|
||||
|
||||
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
|
||||
if (current != null)
|
||||
{
|
||||
Debug.Console(1, destination, "Releasing current route: {0}", current.Source.Key);
|
||||
current.ReleaseRoutes();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
||||
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
||||
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
|
||||
/// of an audio/video route are discovered a route descriptor is returned. If no route is
|
||||
/// discovered, then null is returned
|
||||
/// </summary>
|
||||
public static RouteDescriptor GetRouteToSource(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
||||
{
|
||||
var routeDescr = new RouteDescriptor(source, destination, signalType);
|
||||
// if it's a single signal type, find the route
|
||||
if ((signalType & (eRoutingSignalType.Audio & eRoutingSignalType.Video)) == (eRoutingSignalType.Audio & eRoutingSignalType.Video))
|
||||
{
|
||||
Debug.Console(1, destination, "Attempting to build source route from {0}", source.Key);
|
||||
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescr))
|
||||
routeDescr = null;
|
||||
}
|
||||
// otherwise, audioVideo needs to be handled as two steps.
|
||||
else
|
||||
{
|
||||
Debug.Console(1, destination, "Attempting to build audio and video routes from {0}", source.Key);
|
||||
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescr);
|
||||
if (!audioSuccess)
|
||||
Debug.Console(1, destination, "Cannot find audio route to {0}", source.Key);
|
||||
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescr);
|
||||
if (!videoSuccess)
|
||||
Debug.Console(1, destination, "Cannot find video route to {0}", source.Key);
|
||||
if (!audioSuccess && !videoSuccess)
|
||||
routeDescr = null;
|
||||
}
|
||||
|
||||
//Debug.Console(1, destination, "Route{0} discovered", routeDescr == null ? " NOT" : "");
|
||||
return routeDescr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The recursive part of this. Will stop on each device, search its inputs for the
|
||||
/// desired source and if not found, invoke this function for the each input port
|
||||
/// hoping to find the source.
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
|
||||
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
|
||||
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
|
||||
/// <param name="cycle">Just an informational counter</param>
|
||||
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
|
||||
/// <returns>true if source is hit</returns>
|
||||
static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
|
||||
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
|
||||
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable)
|
||||
{
|
||||
cycle++;
|
||||
Debug.Console(2, "GetRouteToSource: {0} {1}--> {2}", cycle, source.Key, destination.Key);
|
||||
|
||||
RoutingInputPort goodInputPort = null;
|
||||
var destDevInputTies = TieLineCollection.Default.Where(t =>
|
||||
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || (t.Type & (eRoutingSignalType.Audio | eRoutingSignalType.Video)) == (eRoutingSignalType.Audio | eRoutingSignalType.Video)));
|
||||
|
||||
// find a direct tie
|
||||
var directTie = destDevInputTies.FirstOrDefault(
|
||||
t => t.DestinationPort.ParentDevice == destination
|
||||
&& t.SourcePort.ParentDevice == source);
|
||||
if (directTie != null) // Found a tie directly to the source
|
||||
{
|
||||
goodInputPort = directTie.DestinationPort;
|
||||
}
|
||||
else // no direct-connect. Walk back devices.
|
||||
{
|
||||
Debug.Console(2, destination, "is not directly connected to {0}. Walking down tie lines", source.Key);
|
||||
|
||||
// No direct tie? Run back out on the inputs' attached devices...
|
||||
// Only the ones that are routing devices
|
||||
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
|
||||
|
||||
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
|
||||
if (alreadyCheckedDevices == null)
|
||||
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
|
||||
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
|
||||
|
||||
foreach (var inputTieToTry in attachedMidpoints)
|
||||
{
|
||||
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
|
||||
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
|
||||
Debug.Console(2, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key);
|
||||
|
||||
// Check if this previous device has already been walked
|
||||
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
|
||||
{
|
||||
Debug.Console(2, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key);
|
||||
continue;
|
||||
}
|
||||
// haven't seen this device yet. Do it. Pass the output port to the next
|
||||
// level to enable switching on success
|
||||
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
|
||||
alreadyCheckedDevices, signalType, cycle, routeTable);
|
||||
if (upstreamRoutingSuccess)
|
||||
{
|
||||
Debug.Console(2, destination, "Upstream device route found");
|
||||
goodInputPort = inputTieToTry.DestinationPort;
|
||||
break; // Stop looping the inputs in this cycle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we have a route on corresponding inputPort. *** Do the route ***
|
||||
if (goodInputPort != null)
|
||||
{
|
||||
//Debug.Console(2, destination, "adding RouteDescriptor");
|
||||
if (outputPortToUse == null)
|
||||
{
|
||||
// it's a sink device
|
||||
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
|
||||
}
|
||||
else if (destination is IRouting)
|
||||
{
|
||||
routeTable.Routes.Add(new RouteSwitchDescriptor (outputPortToUse, goodInputPort));
|
||||
}
|
||||
else // device is merely IRoutingInputOutputs
|
||||
Debug.Console(2, destination, " No routing. Passthrough device");
|
||||
//Debug.Console(2, destination, "Exiting cycle {0}", cycle);
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.Console(2, destination, "No route found to {0}", source.Key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
|
||||
/// </summary>
|
||||
public class RouteDescriptorCollection
|
||||
{
|
||||
public static RouteDescriptorCollection DefaultCollection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DefaultCollection == null)
|
||||
_DefaultCollection = new RouteDescriptorCollection();
|
||||
return _DefaultCollection;
|
||||
}
|
||||
}
|
||||
static RouteDescriptorCollection _DefaultCollection;
|
||||
|
||||
List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
|
||||
/// destination exists already, it will not be added - in order to preserve
|
||||
/// proper route releasing.
|
||||
/// </summary>
|
||||
/// <param name="descriptor"></param>
|
||||
public void AddRouteDescriptor(RouteDescriptor descriptor)
|
||||
{
|
||||
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
|
||||
{
|
||||
Debug.Console(1, descriptor.Destination,
|
||||
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
|
||||
return;
|
||||
}
|
||||
RouteDescriptors.Add(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RouteDescriptor for a destination
|
||||
/// </summary>
|
||||
/// <returns>null if no RouteDescriptor for a destination exists</returns>
|
||||
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
|
||||
{
|
||||
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
|
||||
/// Returns null if no route with the provided destination exists.
|
||||
/// </summary>
|
||||
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
|
||||
{
|
||||
var descr = GetRouteDescriptorForDestination(destination);
|
||||
if (descr != null)
|
||||
RouteDescriptors.Remove(descr);
|
||||
return descr;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an collection of individual route steps between Source and Destination
|
||||
/// </summary>
|
||||
public class RouteDescriptor
|
||||
{
|
||||
public IRoutingInputs Destination { get; private set; }
|
||||
public IRoutingOutputs Source { get; private set; }
|
||||
public eRoutingSignalType SignalType { get; private set; }
|
||||
public List<RouteSwitchDescriptor> Routes { get; private set; }
|
||||
|
||||
|
||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
|
||||
{
|
||||
Destination = destination;
|
||||
Source = source;
|
||||
SignalType = signalType;
|
||||
Routes = new List<RouteSwitchDescriptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all routes described in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ExecuteRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
Debug.Console(2, "ExecuteRoutes: {0}", route.ToString());
|
||||
if (route.SwitchingDevice is IRoutingSink)
|
||||
{
|
||||
var device = route.SwitchingDevice as IRoutingSinkWithSwitching;
|
||||
if (device == null)
|
||||
continue;
|
||||
|
||||
device.ExecuteSwitch(route.InputPort.Selector);
|
||||
}
|
||||
else if (route.SwitchingDevice is IRouting)
|
||||
{
|
||||
(route.SwitchingDevice as IRouting).ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
|
||||
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
|
||||
Debug.Console(2, "Output port {0} routing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all routes in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ReleaseRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
if (route.SwitchingDevice is IRouting)
|
||||
{
|
||||
// Pull the route from the port. Whatever is watching the output's in use tracker is
|
||||
// responsible for responding appropriately.
|
||||
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
|
||||
Debug.Console(2, "Port {0} releasing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an individual link for a route
|
||||
/// </summary>
|
||||
public class RouteSwitchDescriptor
|
||||
{
|
||||
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
|
||||
public RouteSwitchDescriptor(RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
}
|
||||
|
||||
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
OutputPort = outputPort;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if(SwitchingDevice is IRouting)
|
||||
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
|
||||
else
|
||||
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user