Files
Essentials/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlSIMPLRoomBridge.cs
2025-03-25 21:55:37 -05:00

1126 lines
43 KiB
C#

using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.EthernetCommunication;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.AppServer;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Devices.Common.Cameras;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Room.Config;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Room.MobileControl
{
// ReSharper disable once InconsistentNaming
public class MobileControlSIMPLRoomBridge : MobileControlBridgeBase, IDelayedConfiguration
{
private const int SupportedDisplayCount = 10;
/// <summary>
/// Fires when config is ready to go
/// </summary>
public event EventHandler<EventArgs> ConfigurationIsReady;
public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc { get; private set; }
public MobileControlSIMPLRoomJoinMap JoinMap { get; private set; }
public Dictionary<string, MessengerBase> DeviceMessengers { get; private set; }
/// <summary>
///
/// </summary>
public bool ConfigIsLoaded { get; private set; }
public override string RoomName
{
get
{
var name = Eisc.StringOutput[JoinMap.ConfigRoomName.JoinNumber].StringValue;
return string.IsNullOrEmpty(name) ? "Not Loaded" : name;
}
}
public override string RoomKey
{
get { return "room1"; }
}
private readonly MobileControlSimplDeviceBridge _sourceBridge;
private SIMPLAtcMessenger _atcMessenger;
private SIMPLVtcMessenger _vtcMessenger;
private SimplDirectRouteMessenger _directRouteMessenger;
private const string _syntheticDeviceKey = "syntheticDevice";
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <param name="ipId"></param>
public MobileControlSIMPLRoomBridge(string key, string name, uint ipId)
: base(key, "")
{
Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipId, "127.0.0.2", Global.ControlSystem);
var reg = Eisc.Register();
if (reg != eDeviceRegistrationUnRegistrationResponse.Success)
Debug.Console(0, this, "Cannot connect EISC at IPID {0}: \r{1}", ipId, reg);
JoinMap = new MobileControlSIMPLRoomJoinMap(1);
_sourceBridge = new MobileControlSimplDeviceBridge(key + "-sourceBridge", "SIMPL source bridge", Eisc);
DeviceManager.AddDevice(_sourceBridge);
CrestronConsole.AddNewConsoleCommand((s) => JoinMap.PrintJoinMapInfo(), "printmobilejoinmap", "Prints the MobileControlSIMPLRoomBridge JoinMap", ConsoleAccessLevelEnum.AccessOperator);
AddPostActivationAction(() =>
{
// Inform the SIMPL program that config can be sent
Eisc.BooleanInput[JoinMap.ReadyForConfig.JoinNumber].BoolValue = true;
Eisc.SigChange += EISC_SigChange;
Eisc.OnlineStatusChange += (o, a) =>
{
if (!a.DeviceOnLine)
{
return;
}
Debug.Console(1, this, "SIMPL EISC online={0}. Config is ready={1}. Use Essentials Config={2}",
a.DeviceOnLine, Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue,
Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue);
if (Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue)
LoadConfigValues();
if (Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue)
UseEssentialsConfig();
};
// load config if it's already there
if (Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue)
{
LoadConfigValues();
}
if (Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue)
{
UseEssentialsConfig();
}
});
}
/// <summary>
/// Finish wiring up everything after all devices are created. The base class will hunt down the related
/// parent controller and link them up.
/// </summary>
/// <returns></returns>
public override bool CustomActivate()
{
Debug.Console(0, this, "Final activation. Setting up actions and feedbacks");
//SetupFunctions();
//SetupFeedbacks();
var atcKey = string.Format("atc-{0}-{1}", Key, Key);
_atcMessenger = new SIMPLAtcMessenger(atcKey, Eisc, "/device/audioCodec");
_atcMessenger.RegisterWithAppServer(Parent);
var vtcKey = string.Format("atc-{0}-{1}", Key, Key);
_vtcMessenger = new SIMPLVtcMessenger(vtcKey, Eisc, "/device/videoCodec");
_vtcMessenger.RegisterWithAppServer(Parent);
var drKey = string.Format("directRoute-{0}-{1}", Key, Key);
_directRouteMessenger = new SimplDirectRouteMessenger(drKey, Eisc, "/routing");
_directRouteMessenger.RegisterWithAppServer(Parent);
CrestronConsole.AddNewConsoleCommand(s =>
{
JoinMap.PrintJoinMapInfo();
_atcMessenger.JoinMap.PrintJoinMapInfo();
_vtcMessenger.JoinMap.PrintJoinMapInfo();
_directRouteMessenger.JoinMap.PrintJoinMapInfo();
// TODO: Update Source Bridge to use new JoinMap scheme
//_sourceBridge.JoinMap.PrintJoinMapInfo();
}, "printmobilebridge", "Prints MC-SIMPL bridge EISC data", ConsoleAccessLevelEnum.AccessOperator);
return base.CustomActivate();
}
private void UseEssentialsConfig()
{
ConfigIsLoaded = false;
SetupDeviceMessengers();
Debug.Console(0, this, "******* ESSENTIALS CONFIG: \r{0}",
JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));
ConfigurationIsReady?.Invoke(this, new EventArgs());
ConfigIsLoaded = true;
}
protected override void RegisterActions()
{
SetupFunctions();
SetupFeedbacks();
}
/// <summary>
/// Setup the actions to take place on various incoming API calls
/// </summary>
private void SetupFunctions()
{
AddAction(@"/promptForCode",
(id, content) => Eisc.PulseBool(JoinMap.PromptForCode.JoinNumber));
AddAction(@"/clientJoined", (id, content) => Eisc.PulseBool(JoinMap.ClientJoined.JoinNumber));
AddAction(@"/status", (id, content) => SendFullStatus());
AddAction(@"/source", (id, content) =>
{
var msg = content.ToObject<SourceSelectMessageContent>();
Eisc.SetString(JoinMap.CurrentSourceKey.JoinNumber, msg.SourceListItemKey);
Eisc.PulseBool(JoinMap.SourceHasChanged.JoinNumber);
});
AddAction(@"/defaultsource", (id, content) =>
Eisc.PulseBool(JoinMap.ActivityShare.JoinNumber));
AddAction(@"/activityPhone", (id, content) =>
Eisc.PulseBool(JoinMap.ActivityPhoneCall.JoinNumber));
AddAction(@"/activityVideo", (id, content) =>
Eisc.PulseBool(JoinMap.ActivityVideoCall.JoinNumber));
AddAction(@"/volumes/master/level", (id, content) =>
{
var value = content["value"].Value<ushort>();
Eisc.SetUshort(JoinMap.MasterVolume.JoinNumber, value);
});
AddAction(@"/volumes/master/muteToggle", (id, content) =>
Eisc.PulseBool(JoinMap.MasterVolume.JoinNumber));
AddAction(@"/volumes/master/privacyMuteToggle", (id, content) =>
Eisc.PulseBool(JoinMap.PrivacyMute.JoinNumber));
// /xyzxyz/volumes/master/muteToggle ---> BoolInput[1]
var volumeStart = JoinMap.VolumeJoinStart.JoinNumber;
var volumeEnd = JoinMap.VolumeJoinStart.JoinNumber + JoinMap.VolumeJoinStart.JoinSpan;
for (uint i = volumeStart; i <= volumeEnd; i++)
{
var index = i;
AddAction(string.Format(@"/volumes/level-{0}/level", index), (id, content) =>
{
var value = content["value"].Value<ushort>();
Eisc.SetUshort(index, value);
});
AddAction(string.Format(@"/volumes/level-{0}/muteToggle", index), (id, content) =>
Eisc.PulseBool(index));
}
AddAction(@"/shutdownStart", (id, content) =>
Eisc.PulseBool(JoinMap.ShutdownStart.JoinNumber));
AddAction(@"/shutdownEnd", (id, content) =>
Eisc.PulseBool(JoinMap.ShutdownEnd.JoinNumber));
AddAction(@"/shutdownCancel", (id, content) =>
Eisc.PulseBool(JoinMap.ShutdownCancel.JoinNumber));
}
/// <summary>
///
/// </summary>
/// <param name="devKey"></param>
private void SetupSourceFunctions(string devKey)
{
var sourceJoinMap = new SourceDeviceMapDictionary();
var prefix = string.Format("/device/{0}/", devKey);
foreach (var item in sourceJoinMap)
{
var join = item.Value;
AddAction(string.Format("{0}{1}", prefix, item.Key), (id, content) =>
{
HandlePressAndHoldEisc(content, b => Eisc.SetBool(join, b));
});
}
}
private void HandlePressAndHoldEisc(JToken content, Action<bool> action)
{
var state = content.ToObject<MobileControlSimpleContent<string>>();
var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);
if (timerHandler == null)
{
return;
}
timerHandler(state.Value, action);
action(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase));
}
/// <summary>
/// Links feedbacks to whatever is gonna happen!
/// </summary>
private void SetupFeedbacks()
{
// Power
Eisc.SetBoolSigAction(JoinMap.RoomIsOn.JoinNumber, b =>
PostStatus(new
{
isOn = b
}));
// Source change things
Eisc.SetSigTrueAction(JoinMap.SourceHasChanged.JoinNumber, () =>
PostStatus(new
{
selectedSourceKey = Eisc.StringOutput[JoinMap.CurrentSourceKey.JoinNumber].StringValue
}));
// Volume things
Eisc.SetUShortSigAction(JoinMap.MasterVolume.JoinNumber, u =>
PostStatus(new
{
volumes = new
{
master = new
{
level = u
}
}
}));
// map MasterVolumeIsMuted join -> status/volumes/master/muted
//
Eisc.SetBoolSigAction(JoinMap.MasterVolume.JoinNumber, b =>
PostStatus(new
{
volumes = new
{
master = new
{
muted = b
}
}
}));
Eisc.SetBoolSigAction(JoinMap.PrivacyMute.JoinNumber, b =>
PostStatus(new
{
volumes = new
{
master = new
{
privacyMuted = b
}
}
}));
var volumeStart = JoinMap.VolumeJoinStart.JoinNumber;
var volumeEnd = JoinMap.VolumeJoinStart.JoinNumber + JoinMap.VolumeJoinStart.JoinSpan;
for (uint i = volumeStart; i <= volumeEnd; i++)
{
var index = i; // local scope for lambdas
Eisc.SetUShortSigAction(index, u => // start at join 2
{
// need a dict in order to create the level-n property on auxFaders
var dict = new Dictionary<string, object> { { "level-" + index, new { level = u } } };
PostStatus(new
{
volumes = new
{
auxFaders = dict,
}
});
});
Eisc.SetBoolSigAction(index, b =>
{
// need a dict in order to create the level-n property on auxFaders
var dict = new Dictionary<string, object> { { "level-" + index, new { muted = b } } };
PostStatus(new
{
volumes = new
{
auxFaders = dict,
}
});
});
}
Eisc.SetUShortSigAction(JoinMap.NumberOfAuxFaders.JoinNumber, u =>
PostStatus(new
{
volumes = new
{
numberOfAuxFaders = u,
}
}));
// shutdown things
Eisc.SetSigTrueAction(JoinMap.ShutdownCancel.JoinNumber, () =>
PostMessage("/shutdown/", new
{
state = "wasCancelled"
}));
Eisc.SetSigTrueAction(JoinMap.ShutdownEnd.JoinNumber, () =>
PostMessage("/shutdown/", new
{
state = "hasFinished"
}));
Eisc.SetSigTrueAction(JoinMap.ShutdownStart.JoinNumber, () =>
PostMessage("/shutdown/", new
{
state = "hasStarted",
duration = Eisc.UShortOutput[JoinMap.ShutdownPromptDuration.JoinNumber].UShortValue
}));
// Config things
Eisc.SetSigTrueAction(JoinMap.ConfigIsReady.JoinNumber, LoadConfigValues);
// Activity modes
Eisc.SetSigTrueAction(JoinMap.ActivityShare.JoinNumber, () => UpdateActivity(1));
Eisc.SetSigTrueAction(JoinMap.ActivityPhoneCall.JoinNumber, () => UpdateActivity(2));
Eisc.SetSigTrueAction(JoinMap.ActivityVideoCall.JoinNumber, () => UpdateActivity(3));
AppServerController.ApiOnlineAndAuthorized.LinkInputSig(Eisc.BooleanInput[JoinMap.ApiOnlineAndAuthorized.JoinNumber]);
}
/// <summary>
/// Updates activity states
/// </summary>
private void UpdateActivity(int mode)
{
PostStatus(new
{
activityMode = mode,
});
}
/// <summary>
/// Synthesizes a source device config from the SIMPL config join data
/// </summary>
/// <param name="sli"></param>
/// <param name="type"></param>
/// <param name="i"></param>
/// <returns></returns>
private DeviceConfig GetSyntheticSourceDevice(SourceListItem sli, string type, uint i)
{
var groupMap = GetSourceGroupDictionary();
var key = sli.SourceKey;
var name = sli.Name;
// If not, synthesize the device config
var group = "genericsource";
if (groupMap.ContainsKey(type))
{
group = groupMap[type];
}
// add dev to devices list
var devConf = new DeviceConfig
{
Group = group,
Key = key,
Name = name,
Type = type,
Properties = new JObject(new JProperty(_syntheticDeviceKey, true)),
};
if (group.ToLower().StartsWith("settopbox")) // Add others here as needed
{
SetupSourceFunctions(key);
}
if (group.ToLower().Equals("simplmessenger"))
{
if (type.ToLower().Equals("simplcameramessenger"))
{
var props = new SimplMessengerPropertiesConfig
{
DeviceKey = key,
JoinMapKey = ""
};
var joinStart = 1000 + (i * 100) + 1; // 1001, 1101, 1201, 1301... etc.
props.JoinStart = joinStart;
devConf.Properties = JToken.FromObject(props);
}
}
return devConf;
}
/// <summary>
/// Reads in config values when the Simpl program is ready
/// </summary>
private void LoadConfigValues()
{
Debug.Console(1, this, "Loading configuration from SIMPL EISC bridge");
ConfigIsLoaded = false;
var co = ConfigReader.ConfigObject;
if (!string.IsNullOrEmpty(Eisc.StringOutput[JoinMap.PortalSystemUrl.JoinNumber].StringValue))
{
ConfigReader.ConfigObject.SystemUrl = Eisc.StringOutput[JoinMap.PortalSystemUrl.JoinNumber].StringValue;
}
co.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
var version = Assembly.GetExecutingAssembly().GetName().Version;
co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor,
version.Build);
//Room
//if (co.Rooms == null)
// always start fresh in case simpl changed
co.Rooms = new List<DeviceConfig>();
var rm = new DeviceConfig();
if (co.Rooms.Count == 0)
{
Debug.Console(0, this, "Adding room to config");
co.Rooms.Add(rm);
}
else
{
Debug.Console(0, this, "Replacing Room[0] in config");
co.Rooms[0] = rm;
}
rm.Name = Eisc.StringOutput[JoinMap.ConfigRoomName.JoinNumber].StringValue;
rm.Key = "room1";
rm.Type = "SIMPL01";
var rmProps = rm.Properties == null
? new SimplRoomPropertiesConfig()
: JsonConvert.DeserializeObject<SimplRoomPropertiesConfig>(rm.Properties.ToString());
rmProps.Help = new EssentialsHelpPropertiesConfig
{
CallButtonText = Eisc.StringOutput[JoinMap.ConfigHelpNumber.JoinNumber].StringValue,
Message = Eisc.StringOutput[JoinMap.ConfigHelpMessage.JoinNumber].StringValue
};
rmProps.Environment = new EssentialsEnvironmentPropertiesConfig(); // enabled defaults to false
rmProps.RoomPhoneNumber = Eisc.StringOutput[JoinMap.ConfigRoomPhoneNumber.JoinNumber].StringValue;
rmProps.RoomURI = Eisc.StringOutput[JoinMap.ConfigRoomUri.JoinNumber].StringValue;
rmProps.SpeedDials = new List<SimplSpeedDial>();
// This MAY need a check
if (Eisc.BooleanOutput[JoinMap.ActivityPhoneCallEnable.JoinNumber].BoolValue)
{
rmProps.AudioCodecKey = "audioCodec";
}
if (Eisc.BooleanOutput[JoinMap.ActivityVideoCallEnable.JoinNumber].BoolValue)
{
rmProps.VideoCodecKey = "videoCodec";
}
// volume control names
//// use Volumes object or?
//rmProps.VolumeSliderNames = new List<string>();
//for(uint i = 701; i <= 700 + volCount; i++)
//{
// rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue);
//}
// There should be Mobile Control devices in here, I think...
if (co.Devices == null)
co.Devices = new List<DeviceConfig>();
// clear out previous SIMPL devices
co.Devices.RemoveAll(d =>
d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)
|| d.Key.Equals("audioCodec", StringComparison.OrdinalIgnoreCase)
|| d.Key.Equals("videoCodec", StringComparison.OrdinalIgnoreCase)
|| d.Key.StartsWith("destination-", StringComparison.OrdinalIgnoreCase));
rmProps.SourceListKey = "default";
rm.Properties = JToken.FromObject(rmProps);
// Source list! This might be brutal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
co.SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>();
var newSl = new Dictionary<string, SourceListItem>();
// add "none" source if VTC present
if (!string.IsNullOrEmpty(rmProps.VideoCodecKey))
{
var codecOsd = new SourceListItem
{
Name = "None",
IncludeInSourceList = true,
Order = 1,
Type = eSourceListItemType.Route,
SourceKey = ""
};
newSl.Add("Source-None", codecOsd);
}
// add sources...
var useSourceEnabled = Eisc.BooleanOutput[JoinMap.UseSourceEnabled.JoinNumber].BoolValue;
for (uint i = 0; i <= 19; i++)
{
var name = Eisc.StringOutput[JoinMap.SourceNameJoinStart.JoinNumber + i].StringValue;
if (!Eisc.BooleanOutput[JoinMap.UseSourceEnabled.JoinNumber].BoolValue && string.IsNullOrEmpty(name))
{
this.LogDebug("Source at join {join} does not have a name", JoinMap.SourceNameJoinStart.JoinNumber + i);
break;
}
var icon = Eisc.StringOutput[JoinMap.SourceIconJoinStart.JoinNumber + i].StringValue;
var key = Eisc.StringOutput[JoinMap.SourceKeyJoinStart.JoinNumber + i].StringValue;
var type = Eisc.StringOutput[JoinMap.SourceTypeJoinStart.JoinNumber + i].StringValue;
var disableShare = Eisc.BooleanOutput[JoinMap.SourceShareDisableJoinStart.JoinNumber + i].BoolValue;
var sourceEnabled = Eisc.BooleanOutput[JoinMap.SourceIsEnabledJoinStart.JoinNumber + i].BoolValue;
var controllable = Eisc.BooleanOutput[JoinMap.SourceIsControllableJoinStart.JoinNumber + i].BoolValue;
var audioSource = Eisc.BooleanOutput[JoinMap.SourceIsAudioSourceJoinStart.JoinNumber + i].BoolValue;
Debug.Console(0, this, "Adding source {0} '{1}'", key, name);
var sourceKey = Eisc.StringOutput[JoinMap.SourceControlDeviceKeyJoinStart.JoinNumber + i].StringValue;
var newSli = new SourceListItem
{
Icon = icon,
Name = name,
Order = (int)i + 10,
SourceKey = string.IsNullOrEmpty(sourceKey) ? key : sourceKey, // Use the value from the join if defined
Type = eSourceListItemType.Route,
DisableCodecSharing = disableShare,
IncludeInSourceList = !useSourceEnabled || sourceEnabled,
IsControllable = controllable,
IsAudioSource = audioSource
};
newSl.Add(key, newSli);
var existingSourceDevice = co.GetDeviceForKey(newSli.SourceKey);
var syntheticDevice = GetSyntheticSourceDevice(newSli, type, i);
// Look to see if this is a device that already exists in Essentials and get it
if (existingSourceDevice != null)
{
Debug.Console(0, this, "Found device with key: {0} in Essentials.", key);
if (existingSourceDevice.Properties.Value<bool>(_syntheticDeviceKey))
{
Debug.Console(0, this, "Updating previous device config with new values");
existingSourceDevice = syntheticDevice;
}
else
{
Debug.Console(0, this, "Using existing Essentials device (non synthetic)");
}
}
else
{
co.Devices.Add(syntheticDevice);
}
}
co.SourceLists.Add("default", newSl);
if (Eisc.BooleanOutput[JoinMap.SupportsAdvancedSharing.JoinNumber].BoolValue)
{
if (co.DestinationLists == null)
{
co.DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>();
}
CreateDestinationList(co);
}
// Build "audioCodec" config if we need
if (!string.IsNullOrEmpty(rmProps.AudioCodecKey))
{
var acFavs = new List<CodecActiveCallItem>();
for (uint i = 0; i < 4; i++)
{
if (!Eisc.GetBool(JoinMap.SpeedDialVisibleStartJoin.JoinNumber + i))
{
break;
}
acFavs.Add(new CodecActiveCallItem
{
Name = Eisc.GetString(JoinMap.SpeedDialNameStartJoin.JoinNumber + i),
Number = Eisc.GetString(JoinMap.SpeedDialNumberStartJoin.JoinNumber + i),
Type = eCodecCallType.Audio
});
}
var acProps = new
{
favorites = acFavs
};
const string acStr = "audioCodec";
var acConf = new DeviceConfig
{
Group = acStr,
Key = acStr,
Name = acStr,
Type = acStr,
Properties = JToken.FromObject(acProps)
};
co.Devices.Add(acConf);
}
// Build Video codec config
if (!string.IsNullOrEmpty(rmProps.VideoCodecKey))
{
// No favorites, for now?
var favs = new List<CodecActiveCallItem>();
// cameras
var camsProps = new List<object>();
for (uint i = 0; i < 9; i++)
{
var name = Eisc.GetString(i + JoinMap.CameraNearNameStart.JoinNumber);
if (!string.IsNullOrEmpty(name))
{
camsProps.Add(new
{
name,
selector = "camera" + (i + 1),
});
}
}
var farName = Eisc.GetString(JoinMap.CameraFarName.JoinNumber);
if (!string.IsNullOrEmpty(farName))
{
camsProps.Add(new
{
name = farName,
selector = "cameraFar",
});
}
var props = new
{
favorites = favs,
cameras = camsProps,
};
const string str = "videoCodec";
var conf = new DeviceConfig
{
Group = str,
Key = str,
Name = str,
Type = str,
Properties = JToken.FromObject(props)
};
co.Devices.Add(conf);
}
SetupDeviceMessengers();
Debug.Console(0, this, "******* CONFIG FROM SIMPL: \r{0}",
JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));
ConfigurationIsReady?.Invoke(this, new EventArgs());
ConfigIsLoaded = true;
}
private DeviceConfig GetSyntheticDestinationDevice(string key, string name)
{
// If not, synthesize the device config
var devConf = new DeviceConfig
{
Group = "genericdestination",
Key = key,
Name = name,
Type = "genericdestination",
Properties = new JObject(new JProperty(_syntheticDeviceKey, true)),
};
return devConf;
}
private void CreateDestinationList(BasicConfig co)
{
var useDestEnable = Eisc.BooleanOutput[JoinMap.UseDestinationEnable.JoinNumber].BoolValue;
var newDl = new Dictionary<string, DestinationListItem>();
for (uint i = 0; i < SupportedDisplayCount; i++)
{
var name = Eisc.StringOutput[JoinMap.DestinationNameJoinStart.JoinNumber + i].StringValue;
var routeType = Eisc.StringOutput[JoinMap.DestinationTypeJoinStart.JoinNumber + i].StringValue;
var key = Eisc.StringOutput[JoinMap.DestinationDeviceKeyJoinStart.JoinNumber + i].StringValue;
//var order = Eisc.UShortOutput[JoinMap.DestinationOrderJoinStart.JoinNumber + i].UShortValue;
var enabled = Eisc.BooleanOutput[JoinMap.DestinationIsEnabledJoinStart.JoinNumber + i].BoolValue;
if (useDestEnable && !enabled)
{
continue;
}
if (string.IsNullOrEmpty(key))
{
continue;
}
Debug.Console(0, this, "Adding destination {0} - {1}", key, name);
eRoutingSignalType parsedType;
try
{
parsedType = (eRoutingSignalType)Enum.Parse(typeof(eRoutingSignalType), routeType, true);
}
catch
{
Debug.Console(0, this, "Error parsing destination type: {0}", routeType);
parsedType = eRoutingSignalType.AudioVideo;
}
var newDli = new DestinationListItem
{
Name = name,
Order = (int)i,
SinkKey = key,
SinkType = parsedType,
};
if (!newDl.ContainsKey(key))
{
newDl.Add(key, newDli);
}
else
{
newDl[key] = newDli;
}
if (!_directRouteMessenger.DestinationList.ContainsKey(newDli.SinkKey))
{
//add same DestinationListItem to dictionary for messenger in order to allow for correlation by index
_directRouteMessenger.DestinationList.Add(key, newDli);
}
else
{
_directRouteMessenger.DestinationList[key] = newDli;
}
var existingDev = co.GetDeviceForKey(key);
var syntheticDisplay = GetSyntheticDestinationDevice(key, name);
if (existingDev != null)
{
Debug.Console(0, this, "Found device with key: {0} in Essentials.", key);
if (existingDev.Properties.Value<bool>(_syntheticDeviceKey))
{
Debug.Console(0, this, "Updating previous device config with new values");
}
else
{
Debug.Console(0, this, "Using existing Essentials device (non synthetic)");
}
}
else
{
co.Devices.Add(syntheticDisplay);
}
}
if (!co.DestinationLists.ContainsKey("default"))
{
co.DestinationLists.Add("default", newDl);
}
else
{
co.DestinationLists["default"] = newDl;
}
_directRouteMessenger.RegisterForDestinationPaths();
}
/// <summary>
/// Iterates device config and adds messengers as neede for each device type
/// </summary>
private void SetupDeviceMessengers()
{
DeviceMessengers = new Dictionary<string, MessengerBase>();
try
{
foreach (var device in ConfigReader.ConfigObject.Devices)
{
if (device.Group.Equals("simplmessenger"))
{
var props =
JsonConvert.DeserializeObject<SimplMessengerPropertiesConfig>(device.Properties.ToString());
var messengerKey = string.Format("device-{0}-{1}", Key, Key);
if (DeviceManager.GetDeviceForKey(messengerKey) != null)
{
Debug.Console(2, this, "Messenger with key: {0} already exists. Skipping...", messengerKey);
continue;
}
var dev = ConfigReader.ConfigObject.GetDeviceForKey(props.DeviceKey);
if (dev == null)
{
Debug.Console(1, this, "Unable to find device config for key: '{0}'", props.DeviceKey);
continue;
}
var type = device.Type.ToLower();
MessengerBase messenger = null;
if (type.Equals("simplcameramessenger"))
{
Debug.Console(2, this, "Adding SIMPLCameraMessenger for: '{0}'", props.DeviceKey);
messenger = new SIMPLCameraMessenger(messengerKey, Eisc, "/device/" + props.DeviceKey,
props.JoinStart);
}
else if (type.Equals("simplroutemessenger"))
{
Debug.Console(2, this, "Adding SIMPLRouteMessenger for: '{0}'", props.DeviceKey);
messenger = new SIMPLRouteMessenger(messengerKey, Eisc, "/device/" + props.DeviceKey,
props.JoinStart);
}
if (messenger != null)
{
DeviceManager.AddDevice(messenger);
DeviceMessengers.Add(device.Key, messenger);
messenger.RegisterWithAppServer(Parent);
}
else
{
Debug.Console(2, this, "Unable to add messenger for device: '{0}' of type: '{1}'",
props.DeviceKey, type);
}
}
else
{
var dev = DeviceManager.GetDeviceForKey(device.Key);
if (dev != null)
{
if (dev is CameraBase)
{
var camDevice = dev as CameraBase;
Debug.Console(1, this, "Adding CameraBaseMessenger for device: {0}", dev.Key);
var cameraMessenger = new CameraBaseMessenger(device.Key + "-" + Key, camDevice,
"/device/" + device.Key);
DeviceMessengers.Add(device.Key, cameraMessenger);
DeviceManager.AddDevice(cameraMessenger);
cameraMessenger.RegisterWithAppServer(Parent);
continue;
}
}
}
}
}
catch (Exception e)
{
Debug.Console(2, this, "Error Setting up Device Managers: {0}", e);
}
}
/// <summary>
///
/// </summary>
private void SendFullStatus()
{
if (ConfigIsLoaded)
{
var count = Eisc.UShortOutput[JoinMap.NumberOfAuxFaders.JoinNumber].UShortValue;
Debug.Console(1, this, "The Fader Count is : {0}", count);
// build volumes object, serialize and put in content of method below
// Create auxFaders
var auxFaderDict = new Dictionary<string, Volume>();
var volumeStart = JoinMap.VolumeJoinStart.JoinNumber;
for (var i = volumeStart; i <= count; i++)
{
auxFaderDict.Add("level-" + i,
new Volume("level-" + i,
Eisc.UShortOutput[i].UShortValue,
Eisc.BooleanOutput[i].BoolValue,
Eisc.StringOutput[i].StringValue,
true,
"someting.png"));
}
var volumes = new Volumes
{
Master = new Volume("master",
Eisc.UShortOutput[JoinMap.MasterVolume.JoinNumber].UShortValue,
Eisc.BooleanOutput[JoinMap.MasterVolume.JoinNumber].BoolValue,
Eisc.StringOutput[JoinMap.MasterVolume.JoinNumber].StringValue,
true,
"something.png")
{
HasPrivacyMute = true,
PrivacyMuted = Eisc.BooleanOutput[JoinMap.PrivacyMute.JoinNumber].BoolValue
},
AuxFaders = auxFaderDict,
NumberOfAuxFaders = Eisc.UShortInput[JoinMap.NumberOfAuxFaders.JoinNumber].UShortValue
};
// TODO: Add property to status message to indicate if advanced sharing is supported and if users can change share mode
PostStatus(new
{
activityMode = GetActivityMode(),
isOn = Eisc.BooleanOutput[JoinMap.RoomIsOn.JoinNumber].BoolValue,
selectedSourceKey = Eisc.StringOutput[JoinMap.CurrentSourceKey.JoinNumber].StringValue,
volumes,
supportsAdvancedSharing = Eisc.BooleanOutput[JoinMap.SupportsAdvancedSharing.JoinNumber].BoolValue,
userCanChangeShareMode = Eisc.BooleanOutput[JoinMap.UserCanChangeShareMode.JoinNumber].BoolValue,
});
}
else
{
PostStatus(new
{
error = "systemNotReady"
});
}
}
/// <summary>
/// Returns the activity mode int
/// </summary>
/// <returns></returns>
private int GetActivityMode()
{
if (Eisc.BooleanOutput[JoinMap.ActivityPhoneCall.JoinNumber].BoolValue) return 2;
if (Eisc.BooleanOutput[JoinMap.ActivityShare.JoinNumber].BoolValue) return 1;
return Eisc.BooleanOutput[JoinMap.ActivityVideoCall.JoinNumber].BoolValue ? 3 : 0;
}
/// <summary>
/// Helper for posting status message
/// </summary>
/// <param name="contentObject">The contents of the content object</param>
private void PostStatus(object contentObject)
{
AppServerController.SendMessageObject(new MobileControlMessage
{
Type = "/status/",
Content = JToken.FromObject(contentObject)
});
}
/// <summary>
///
/// </summary>
/// <param name="messageType"></param>
/// <param name="contentObject"></param>
private void PostMessage(string messageType, object contentObject)
{
AppServerController.SendMessageObject(new MobileControlMessage
{
Type = messageType,
Content = JToken.FromObject(contentObject)
});
}
/// <summary>
///
/// </summary>
/// <param name="currentDevice"></param>
/// <param name="args"></param>
private void EISC_SigChange(object currentDevice, SigEventArgs args)
{
if (Debug.Level >= 1)
Debug.Console(1, this, "SIMPL EISC change: {0} {1}={2}", args.Sig.Type, args.Sig.Number,
args.Sig.StringValue);
var uo = args.Sig.UserObject;
if (uo != null)
{
if (uo is Action<bool>)
(uo as Action<bool>)(args.Sig.BoolValue);
else if (uo is Action<ushort>)
(uo as Action<ushort>)(args.Sig.UShortValue);
else if (uo is Action<string>)
(uo as Action<string>)(args.Sig.StringValue);
}
}
/// <summary>
/// Returns the mapping of types to groups, for setting up devices.
/// </summary>
/// <returns></returns>
private Dictionary<string, string> GetSourceGroupDictionary()
{
//type, group
var d = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{"laptop", "pc"},
{"pc", "pc"},
{"wireless", "genericsource"},
{"iptv", "settopbox"},
{"simplcameramessenger", "simplmessenger"},
{"camera", "camera"},
};
return d;
}
/// <summary>
/// updates the usercode from server
/// </summary>
protected override void UserCodeChange()
{
Debug.Console(1, this, "Server user code changed: {0}", UserCode);
var qrUrl = string.Format("{0}/api/rooms/{1}/{3}/qr?x={2}", AppServerController.Host, AppServerController.SystemUuid, new Random().Next(), "room1");
QrCodeUrl = qrUrl;
Debug.Console(1, this, "Server user code changed: {0} - {1}", UserCode, qrUrl);
OnUserCodeChanged();
Eisc.StringInput[JoinMap.UserCodeToSystem.JoinNumber].StringValue = UserCode;
Eisc.StringInput[JoinMap.ServerUrl.JoinNumber].StringValue = McServerUrl;
Eisc.StringInput[JoinMap.QrCodeUrl.JoinNumber].StringValue = QrCodeUrl;
}
}
}