Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Welker
185410b802 fix: ignore case for appdebug {LogLevel} 2025-05-07 08:08:56 -05:00
11 changed files with 1390 additions and 1802 deletions

View File

@@ -353,7 +353,7 @@ namespace PepperDash.Core
return;
}
if(Enum.TryParse<LogEventLevel>(levelString, out var levelEnum))
if(Enum.TryParse<LogEventLevel>(levelString, true, out var levelEnum))
{
SetDebugLevel(levelEnum);
return;

View File

@@ -1,107 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// This defines a device that has screens with layouts
/// Simply decorative
/// </summary>
public interface IHasScreensWithLayouts
{
/// <summary>
/// A dictionary of screens, keyed by screen ID, that contains information about each screen and its layouts.
/// </summary>
Dictionary<uint, ScreenInfo> Screens { get; }
/// <summary>
/// Applies a specific layout to a screen based on the provided screen ID and layout index.
/// </summary>
/// <param name="screenId"></param>
/// <param name="layoutIndex"></param>
void ApplyLayout(uint screenId, uint layoutIndex);
}
/// <summary>
/// Represents information about a screen and its layouts.
/// </summary>
public class ScreenInfo
{
/// <summary>
/// Indicates whether the screen is enabled or not.
/// </summary>
[JsonProperty("enabled")]
public bool Enabled { get; set; }
/// <summary>
/// The name of the screen.
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// The index of the screen.
/// </summary>
[JsonProperty("screenIndex")]
public int ScreenIndex { get; set; }
/// <summary>
/// A dictionary of layout information for the screen, keyed by layout ID.
/// </summary>
[JsonProperty("layouts")]
public Dictionary<uint, LayoutInfo> Layouts { get; set; }
}
/// <summary>
/// Represents information about a layout on a screen.
/// </summary>
public class LayoutInfo
{
/// <summary>
/// The name of the layout.
/// </summary>
[JsonProperty("layoutName")]
public string LayoutName { get; set; }
/// <summary>
/// The index of the layout.
/// </summary>
[JsonProperty("layoutIndex")]
public int LayoutIndex { get; set; }
/// <summary>
/// The type of the layout, which can be "single", "double", "triple", or "quad".
/// </summary>
[JsonProperty("layoutType")]
public string LayoutType { get; set; }
/// <summary>
/// A dictionary of window configurations for the layout, keyed by window ID.
/// </summary>
[JsonProperty("windows")]
public Dictionary<uint, WindowConfig> Windows { get; set; }
}
/// <summary>
/// Represents the configuration of a window within a layout on a screen.
/// </summary>
public class WindowConfig
{
/// <summary>
/// The display label for the window
/// </summary>
[JsonProperty("label")]
public string Label { get; set; }
/// <summary>
/// The input for the window
/// </summary>
[JsonProperty("input")]
public string Input { get; set; }
}
}

View File

@@ -1,86 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Extensions for IPAddress to provide additional functionality such as getting broadcast address, network address, and checking if two addresses are in the same subnet.
/// </summary>
public static class IPAddressExtensions
{
/// <summary>
/// Get the broadcast address for a given IP address and subnet mask.
/// </summary>
/// <param name="address">Address to check</param>
/// <param name="subnetMask">Subnet mask in a.b.c.d format</param>
/// <returns>Broadcast address</returns>
/// <remarks>
/// If the input IP address is 192.168.1.100 and the subnet mask is 255.255.255.0, the broadcast address will be 192.168.1.255
/// </remarks>
/// <exception cref="ArgumentException"></exception>
public static IPAddress GetBroadcastAddress(this IPAddress address, IPAddress subnetMask)
{
byte[] ipAdressBytes = address.GetAddressBytes();
byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length)
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++)
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
}
return new IPAddress(broadcastAddress);
}
/// <summary>
/// Get the network address for a given IP address and subnet mask.
/// </summary>
/// <param name="address">Address to check</param>
/// <param name="subnetMask">Subnet mask in a.b.c.d</param>
/// <returns>Network Address</returns>
/// /// <remarks>
/// If the input IP address is 192.168.1.100 and the subnet mask is 255.255.255.0, the network address will be 192.168.1.0
/// </remarks>
/// <exception cref="ArgumentException"></exception>
public static IPAddress GetNetworkAddress(this IPAddress address, IPAddress subnetMask)
{
byte[] ipAdressBytes = address.GetAddressBytes();
byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length)
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++)
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));
}
return new IPAddress(broadcastAddress);
}
/// <summary>
/// Determine if two IP addresses are in the same subnet.
/// </summary>
/// <param name="address2">Address to check</param>
/// <param name="address">Second address to check</param>
/// <param name="subnetMask">Subnet mask to use to compare the 2 IP Address</param>
/// <returns>True if addresses are in the same subnet</returns>
/// <remarks>
/// If the input IP addresses are 192.168.1.100 and 192.168.1.200, and the subnet mask is 255.255.255.0, this will return true.
/// If the input IP addresses are 10.1.1.100 and 192.168.1.100, and the subnet mask is 255.255.255.0, this will return false.
/// </remarks>
public static bool IsInSameSubnet(this IPAddress address2, IPAddress address, IPAddress subnetMask)
{
IPAddress network1 = address.GetNetworkAddress(subnetMask);
IPAddress network2 = address2.GetNetworkAddress(subnetMask);
return network1.Equals(network2);
}
}
}

View File

@@ -227,7 +227,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
for (int i = 0; i < joinMap.PresetRecallStart.JoinSpan; i++)
for (int i = 0; i < joinMap.NumberOfPresets.JoinSpan; i++)
{
int tempNum = i;

View File

@@ -1,95 +0,0 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
{
/// <summary>
/// Describes the available tracking modes for a Cisco codec's Presenter Track feature.
/// </summary>
public enum ePresenterTrackMode
{
/// <summary>
/// Presenter Track is turned off.
/// </summary>
Off,
/// <summary>
/// Presenter Track follows the speaker's movements.
/// </summary>
Follow,
/// <summary>
/// Presenter Track is set to background mode, where it tracks the speaker but does not actively follow.
/// </summary>
Background,
/// <summary>
/// Presenter Track is set to persistent mode, where it maintains a fixed position or focus on the speaker.
/// </summary>
Persistent
}
/// <summary>
/// Describes the Presenter Track controls for a Cisco codec.
/// </summary>
public interface IPresenterTrack : IKeyed
{
/// <summary>
///
/// </summary>
bool PresenterTrackAvailability { get; }
/// <summary>
/// Feedback indicating whether Presenter Track is available.
/// </summary>
BoolFeedback PresenterTrackAvailableFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Presenter Track is off
/// </summary>
BoolFeedback PresenterTrackStatusOffFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Presenter Track is follow
/// </summary>
BoolFeedback PresenterTrackStatusFollowFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Presenter Track is background
/// </summary>
BoolFeedback PresenterTrackStatusBackgroundFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Presenter Track is persistent
/// </summary>
BoolFeedback PresenterTrackStatusPersistentFeedback { get; }
/// <summary>
/// Indicates the current status of Presenter Track.
/// </summary>
bool PresenterTrackStatus { get; }
/// <summary>
/// Turns off Presenter Track.
/// </summary>
void PresenterTrackOff();
/// <summary>
/// Turns on Presenter Track in follow mode.
/// </summary>
void PresenterTrackFollow();
/// <summary>
/// Turns on Presenter Track in background mode.
/// </summary>
void PresenterTrackBackground();
/// <summary>
/// Turns on Presenter Track in persistent mode.
/// </summary>
void PresenterTrackPersistent();
}
}

View File

@@ -1,40 +0,0 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
{
/// <summary>
/// Describes the available tracking modes for a Cisco codec
/// </summary>
public interface ISpeakerTrack : IKeyed
{
/// <summary>
/// Indicates whether Speaker Track is available on the codec.
/// </summary>
bool SpeakerTrackAvailability { get; }
/// <summary>
///
/// </summary>
BoolFeedback SpeakerTrackAvailableFeedback { get; }
/// <summary>
/// Feedback indicating the current status of Speaker Track is off
/// </summary>
bool SpeakerTrackStatus { get; }
/// <summary>
/// Turns Speaker Track off
/// </summary>
void SpeakerTrackOff();
/// <summary>
/// Turns Speaker Track on
/// </summary>
void SpeakerTrackOn();
}
}

View File

@@ -18,13 +18,9 @@ namespace PepperDash.Essentials.Devices.Common.DSP
public Dictionary<string, DspControlPoint> SwitcherControlPoints { get; private set; }
public DspBase(string key, string name) :
base(key, name)
{
LevelControlPoints = new Dictionary<string, IBasicVolumeWithFeedback>();
DialerControlPoints = new Dictionary<string, DspControlPoint>();
SwitcherControlPoints = new Dictionary<string, DspControlPoint>();
public DspBase(string key, string name) :
base(key, name)
{
}

View File

@@ -12,45 +12,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
/// </summary>
public interface IHasCodecRoomPresets
{
/// <summary>
/// Event that is raised when the list of room presets has changed.
/// </summary>
event EventHandler<EventArgs> CodecRoomPresetsListHasChanged;
/// <summary>
/// List of near end presets that can be recalled.
/// </summary>
List<CodecRoomPreset> NearEndPresets { get; }
/// <summary>
/// List of far end presets that can be recalled.
/// </summary>
List<CodecRoomPreset> FarEndRoomPresets { get; }
/// <summary>
/// Selects a near end preset by its ID.
/// </summary>
/// <param name="preset"></param>
void CodecRoomPresetSelect(int preset);
/// <summary>
/// Stores a near end preset with the given ID and description.
/// </summary>
/// <param name="preset"></param>
/// <param name="description"></param>
void CodecRoomPresetStore(int preset, string description);
void CodecRoomPresetStore(int preset, string description);
/// <summary>
/// Selects a far end preset by its ID. This is typically used to recall a preset that has been defined on the far end codec.
/// </summary>
/// <param name="preset"></param>
void SelectFarEndPreset(int preset);
}
/// <summary>
/// Static class for converting non-generic RoomPresets to generic CameraPresets.
/// </summary>
public static class RoomPresets
public static class RoomPresets
{
/// <summary>
/// Converts non-generic RoomPresets to generic CameraPresets
@@ -72,13 +47,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
/// </summary>
public class CodecRoomPreset : PresetBase
{
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="description"></param>
/// <param name="def"></param>
/// <param name="isDef"></param>
public CodecRoomPreset(int id, string description, bool def, bool isDef)
: base(id, description, def, isDef)
{

View File

@@ -491,7 +491,7 @@ namespace PepperDash.Essentials.Touchpanel
{
public MobileControlTouchpanelControllerFactory()
{
TypeNames = new List<string>() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel", "mcdge1000" };
TypeNames = new List<string>() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel" };
MinimumEssentialsFrameworkVersion = "2.0.0";
}
@@ -555,10 +555,7 @@ namespace PepperDash.Essentials.Touchpanel
return new Tsw1070(id, Global.ControlSystem);
else if (type == "ts1070")
return new Ts1070(id, Global.ControlSystem);
else if (type == "dge1000")
return new Dge1000(id, Global.ControlSystem);
else
else
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW controller with type '{0}'", type);
return null;

View File

@@ -1,145 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.RoomBridges;
using Serilog.Events;
using System;
using System.Text.RegularExpressions;
using WebSocketSharp;
using WebSocketSharp.Server;
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
namespace PepperDash.Essentials.WebSocketServer
{
/// <summary>
/// Represents the behaviour to associate with a UiClient for WebSocket communication
/// </summary>
public class UiClient : WebSocketBehavior
{
public MobileControlSystemController Controller { get; set; }
public string RoomKey { get; set; }
private string _clientId;
private DateTime _connectionTime;
public TimeSpan ConnectedDuration
{
get
{
if (Context.WebSocket.IsAlive)
{
return DateTime.Now - _connectionTime;
}
else
{
return new TimeSpan(0);
}
}
}
public UiClient()
{
}
protected override void OnOpen()
{
base.OnOpen();
var url = Context.WebSocket.Url;
Debug.LogMessage(LogEventLevel.Verbose, "New WebSocket Connection from: {0}", null, url);
var match = Regex.Match(url.AbsoluteUri, "(?:ws|wss):\\/\\/.*(?:\\/mc\\/api\\/ui\\/join\\/)(.*)");
if (!match.Success)
{
_connectionTime = DateTime.Now;
return;
}
var clientId = match.Groups[1].Value;
_clientId = clientId;
if (Controller == null)
{
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Controller is null");
_connectionTime = DateTime.Now;
}
var clientJoinedMessage = new MobileControlMessage
{
Type = "/system/clientJoined",
Content = JToken.FromObject(new
{
clientId,
roomKey = RoomKey,
})
};
Controller.HandleClientMessage(JsonConvert.SerializeObject(clientJoinedMessage));
var bridge = Controller.GetRoomBridge(RoomKey);
if (bridge == null) return;
SendUserCodeToClient(bridge, clientId);
bridge.UserCodeChanged -= Bridge_UserCodeChanged;
bridge.UserCodeChanged += Bridge_UserCodeChanged;
// TODO: Future: Check token to see if there's already an open session using that token and reject/close the session
}
private void Bridge_UserCodeChanged(object sender, EventArgs e)
{
SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, _clientId);
}
private void SendUserCodeToClient(MobileControlBridgeBase bridge, string clientId)
{
var content = new
{
userCode = bridge.UserCode,
qrUrl = bridge.QrCodeUrl,
};
var message = new MobileControlMessage
{
Type = "/system/userCodeChanged",
ClientId = clientId,
Content = JToken.FromObject(content)
};
Controller.SendMessageObjectToDirectClient(message);
}
protected override void OnMessage(MessageEventArgs e)
{
base.OnMessage(e);
if (e.IsText && e.Data.Length > 0 && Controller != null)
{
// Forward the message to the controller to be put on the receive queue
Controller.HandleClientMessage(e.Data);
}
}
protected override void OnClose(CloseEventArgs e)
{
base.OnClose(e);
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Closing: {0} reason: {1}", null, e.Code, e.Reason);
}
protected override void OnError(ErrorEventArgs e)
{
base.OnError(e);
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Error: {exception} message: {message}", e.Exception, e.Message);
}
}
}