From 0fd5e46495c60d51a62e8e37b2d7d9d28cb61a49 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 13 Jun 2020 17:30:19 -0500 Subject: [PATCH] Cleanup event calls and continue implementing --- QSharp/QClient.cs | 104 +++++++++++++++--------- QSharp/QServer.cs | 36 ++++++--- QSharp/QWorkspace.cs | 185 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 246 insertions(+), 79 deletions(-) diff --git a/QSharp/QClient.cs b/QSharp/QClient.cs index 39bb977..6403ff8 100644 --- a/QSharp/QClient.cs +++ b/QSharp/QClient.cs @@ -4,6 +4,9 @@ using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SharpOSC; +using Serilog; +using System.Collections.Concurrent; +using System.ComponentModel; namespace QSharp { @@ -32,7 +35,7 @@ namespace QSharp public QClient(string host, int port) { tcpClient = new TCPClient(host, port); - Console.WriteLine($"[QClient] setup connection to: <{host}:{port}>"); + Log.Debug($"[client] setup connection to: <{host}:{port}>"); tcpClient.MessageReceived += ProcessMessage; } @@ -41,7 +44,10 @@ namespace QSharp public bool connect() { - return tcpClient.Connect(); + if (tcpClient == null) + return false; + else + return tcpClient.Connect(); } public void disconnect() @@ -53,7 +59,7 @@ namespace QSharp public void sendMessage(string address, params object[] args) { tcpClient.Send(new OscMessage(address, args)); - // Console.WriteLine($"QClient send message {address}"); + Log.Debug($"[client] send message {address} : {args}"); } private void ProcessMessage(object source, MessageEventArgs args) @@ -64,10 +70,30 @@ namespace QSharp if (message.IsReply) { JToken data = message.response; + //special case, want to update cue properties if (message.IsReplyFromCue) { - //todo check data type? + if(data.Type == JTokenType.Object) + { + OnCueUpdated(message.cueID, data); + } + else if (data.Type == JTokenType.String || data.Type == JTokenType.Integer || data.Type == JTokenType.Float) + { + string property = message.AddressParts.Last(); + if (property == null) + return; + //create object manually since single value replies don't have dictionaries + JObject properties = new JObject(); + properties.Add(property, data); + + OnCueUpdated(message.cueID, properties); + } + else + { + Log.Debug($"[client] unhandled reply from cue: Type: {data.Type} value: {message.response}"); + } + } else if (message.IsReplyFromCueLists) { @@ -86,7 +112,7 @@ namespace QSharp } else { - Console.WriteLine($"[client] unhandled reply message: {message.address}"); + Log.Debug($"[client] unhandled reply message: {message.address}"); } } else if(message.IsUpdate) { @@ -96,7 +122,8 @@ namespace QSharp } else if (message.IsPlaybackPositionUpdate) { - OnCueListChangedPlaybackPosition(message.cueID); + string cueListID = message.AddressParts[4]; + OnCueListChangedPlaybackPosition(cueListID, message.cueID); } else if (message.IsWorkspaceUpdate) { @@ -125,86 +152,81 @@ namespace QSharp } else if (message.IsDisconnect) { - Console.WriteLine($"[client] disconnect message received: {message.address}"); + Log.Debug($"[client] disconnect message received: {message.address}"); OnWorkspaceDisconnected(); } else { - Console.WriteLine($"[client] unhandled update message: {message.address}"); + Log.Debug($"[client] unhandled update message: {message.address}"); } } else { - Console.WriteLine($"[client] unhandled message: {message.address}"); + Log.Debug($"[client] unhandled message: {message.address}"); } } + protected virtual void OnCueUpdated(string cueID, JToken properties) + { + Log.Debug($"[client] cue updated: {cueID}"); + CueUpdated?.Invoke(this, new QCueUpdatedArgs { cueID = cueID, data = properties }); + } protected virtual void OnCueNeedsUpdated(string cueID) { - Console.WriteLine($"[client] cue needs updated: {cueID}"); - if (CueNeedsUpdated != null) - CueNeedsUpdated(this, new QCueNeedsUpdatedArgs { cueID = cueID }); + Log.Debug($"[client] cue needs updated: {cueID}"); + CueNeedsUpdated?.Invoke(this, new QCueNeedsUpdatedArgs { cueID = cueID }); } protected virtual void OnCueListsUpdated(JToken response) { - Console.WriteLine($"[client] Cue Lists Updated"); - if (CueListsUpdated != null) - CueListsUpdated(this, new QCueListsUpdatedArgs { data = response }); + Log.Debug($"[client] Cue Lists Updated"); + CueListsUpdated?.Invoke(this, new QCueListsUpdatedArgs { data = response }); } - protected virtual void OnCueListChangedPlaybackPosition(string cueID) + protected virtual void OnCueListChangedPlaybackPosition(string cueListID, string cueID) { - Console.WriteLine($"[client] Playback Position Changed: {cueID}"); - if (CueListChangedPlaybackPosition != null) - CueListChangedPlaybackPosition(this, new QCueListChangedPlaybackPositionArgs { cueID = cueID }); + Log.Debug($"[client] CueList <{cueListID}> Playback Position Changed to <{cueID}>"); + CueListChangedPlaybackPosition?.Invoke(this, new QCueListChangedPlaybackPositionArgs { cueListID = cueListID, cueID = cueID }); } protected virtual void OnWorkspaceUpdated() { - Console.WriteLine($"[client] Workspace Updated"); - if (WorkspaceUpdated != null) - WorkspaceUpdated(this, new QWorkspaceUpdatedArgs()); + Log.Debug($"[client] Workspace Updated"); + WorkspaceUpdated?.Invoke(this, new QWorkspaceUpdatedArgs()); } protected virtual void OnWorkspaceSettingsUpdated(string settingsType) { - Console.WriteLine($"[client] Workspace Settings Updated"); - if (WorkspaceSettingsUpdated != null) - WorkspaceSettingsUpdated(this, new QWorkspaceSettingsUpdatedArgs { settingsType = settingsType}); + Log.Debug($"[client] Workspace Settings Updated"); + WorkspaceSettingsUpdated?.Invoke(this, new QWorkspaceSettingsUpdatedArgs { settingsType = settingsType }); } protected virtual void OnWorkspaceLightDashboardUpdated() { - Console.WriteLine($"[client] Workspace Light Dashboard Updated"); - if (WorkspaceLightDashboardUpdated != null) - WorkspaceLightDashboardUpdated(this, new QWorkspaceLightDashboardUpdatedArgs()); + Log.Debug($"[client] Workspace Light Dashboard Updated"); + WorkspaceLightDashboardUpdated?.Invoke(this, new QWorkspaceLightDashboardUpdatedArgs()); } protected virtual void OnQLabPreferencesUpdated(string key) { - Console.WriteLine($"[client] QLab Preferences Updated"); - if (QLabPreferencesUpdated != null) - QLabPreferencesUpdated(this, new QQLabPreferencesUpdatedArgs { key = key }); + Log.Debug($"[client] QLab Preferences Updated"); + QLabPreferencesUpdated?.Invoke(this, new QQLabPreferencesUpdatedArgs { key = key }); } protected virtual void OnWorkspaceDisconnected() { - Console.WriteLine($"[client] Workspace Disconnected"); - if (WorkspaceDisconnected != null) - WorkspaceDisconnected(this, new QWorkspaceDisconnectedArgs()); + Log.Debug($"[client] Workspace Disconnected"); + WorkspaceDisconnected?.Invoke(this, new QWorkspaceDisconnectedArgs()); } protected virtual void OnWorkspaceConnected() { - if (WorkspaceConnected != null) - WorkspaceConnected(this, new QWorkspaceConnectedArgs()); + WorkspaceConnected?.Invoke(this, new QWorkspaceConnectedArgs()); } protected virtual void OnWorkspaceConnectionError(string status) { - if (WorkspaceConnectionError != null) - WorkspaceConnectionError(this, new QWorkspaceConnectionErrorArgs { status = status}); + WorkspaceConnectionError?.Invoke(this, new QWorkspaceConnectionErrorArgs { status = status }); } protected virtual void OnWorkspacesUpdated(QMessage message) @@ -221,6 +243,12 @@ namespace QSharp } } + + public void Close() + { + this.tcpClient.Close(); + } + } } diff --git a/QSharp/QServer.cs b/QSharp/QServer.cs index 6e37e8c..569b20d 100644 --- a/QSharp/QServer.cs +++ b/QSharp/QServer.cs @@ -1,4 +1,5 @@ //General information about a QLab Workspace +using Serilog; using System.Collections.Concurrent; using System.Collections.Generic; using Zeroconf; @@ -7,13 +8,15 @@ namespace QSharp { public class QServer { + public event QServerUpdatedHandler ServerUpdated; + private QClient client; public string host; public int port; public string name; public IZeroconfHost zeroconfHost; - List workspaces = new List(); + public List workspaces = new List(); public QServer(string host, int port) { @@ -21,7 +24,12 @@ namespace QSharp this.port = port; client = new QClient(host, port); - client.WorkspacesUpdated += OnWorkspacesUpdated; + client.WorkspacesUpdated += OnServerWorkspacesUpdated; + + if (!client.connect()) + { + Log.Error($"[server] unable to connect to QLab Server: {host}:{port}"); + } } public string description { get { return $"{name} - {host} - {port}"; } } @@ -29,11 +37,6 @@ namespace QSharp public void refreshWorkspaces() { - if (!client.connect()) - { - System.Console.WriteLine($"Error: QServer unable to connect to QLab Server: {host}:{port}"); - return; - } client.sendMessage("/workspaces"); } @@ -48,7 +51,7 @@ namespace QSharp } #region EventHandlers - private void OnWorkspacesUpdated(object source, QWorkspacesUpdatedArgs args) + private void OnServerWorkspacesUpdated(object source, QWorkspacesUpdatedArgs args) { foreach (var workspace in args.Workspaces) { @@ -57,11 +60,26 @@ namespace QSharp { QWorkspace workspaceToAdd = new QWorkspace(workspace, this); workspaces.Add(workspaceToAdd); - workspaceToAdd.connectWithPasscode("1234"); } } + OnServerUpdated(this); + } + + protected virtual void OnServerUpdated(QServer server) + { + ServerUpdated?.Invoke(this, new QServerUpdatedArgs { server = server }); } #endregion + public void Close() + { + foreach (var workspace in workspaces) + { + if(workspace.connected) + workspace.Close(); + } + client.Close(); + } + } } diff --git a/QSharp/QWorkspace.cs b/QSharp/QWorkspace.cs index 96a73d8..33c4b1d 100644 --- a/QSharp/QWorkspace.cs +++ b/QSharp/QWorkspace.cs @@ -1,8 +1,9 @@ //TODO: connect methods, cue property fetch methods, everything else +using Newtonsoft.Json; +using Serilog; using System; using System.Collections.Generic; using System.Linq; -using System.Security.Cryptography; using System.Timers; namespace QSharp @@ -16,7 +17,7 @@ namespace QSharp public string name; public string uniqueID; public bool connected; - private bool hasPasscode; + public bool hasPasscode; private string passcode; private QCue root; @@ -28,6 +29,9 @@ namespace QSharp public string version; + + public event QWorkspaceUpdatedHandler WorkspaceUpdated; + public event QCueListChangedPlaybackPositionHandler CueListChangedPlaybackPosition; private void Init() { name = ""; @@ -63,13 +67,16 @@ namespace QSharp client = new QClient(server.host, server.port); - client.WorkspaceConnected += OnWorkspaceConnection; + client.WorkspaceConnected += OnWorkspaceConnected; client.WorkspaceConnectionError += OnWorkspaceConnectionError; client.WorkspaceDisconnected += OnWorkspaceDisconnected; client.CueListsUpdated += OnCueListsUpdated; + client.CueListChangedPlaybackPosition += OnCueListChangedPlaybackPosition; + client.CueNeedsUpdated += OnCueNeedsUpdated; + client.CueUpdated += OnCueUpdated; this.server = server; - Console.WriteLine($"[workspace] <{name}> initizalied for server: {server.name} "); + Log.Debug($"[workspace] <{name}> initizalied for server: {server.name} "); } //updateWithDictionary @@ -90,7 +97,36 @@ namespace QSharp public string description { get { return $"{name} : {uniqueID}"; } } - public string nameWithoutPathExtension { get { return name; } } //TODO + public bool isOlderThanVersion(string version) + { + var thisVersion = new Version(this.version); + var otherVersion = new Version(version); + return thisVersion.CompareTo(otherVersion) < 0; + } + + public bool isEqualToVersion(string version) + { + var thisVersion = new Version(this.version); + var otherVersion = new Version(version); + return thisVersion.CompareTo(otherVersion) == 0; + } + + public bool isNewerThanVersion(string version) + { + var thisVersion = new Version(this.version); + var otherVersion = new Version(version); + return thisVersion.CompareTo(otherVersion) > 0; + } + + public string nameWithoutPathExtension { + get { + if (name.EndsWith(".cues")) + return name.Substring(0, name.Length - 5); + else if (name.EndsWith(".qlab4")) + return name.Substring(0, name.Length - 6); + return name; + } + } //TODO public string serverName { get { return server.name; } } @@ -98,6 +134,8 @@ namespace QSharp public QCue firstCue { get { return firstCueList.firstCue; } } public QCue firstCueList { get { return root.firstCue; } } + + public List cueLists { get { return root.cues; } } public string fullNameWithCueList(QCue cueList) { return ""; } //TODO other convenience methods public string[] versionParts { get { return version.Split('.'); } } @@ -110,20 +148,20 @@ namespace QSharp //TODO if (!client.connect()) { - System.Console.WriteLine($"[workspace] *** Error: couldn't connect to server client is not connected"); + Log.Error($"[workspace] *** Error: couldn't connect to server client is not connected"); return; } //save password for reuse this.passcode = passcode; - System.Console.WriteLine("[workspace] connecting..."); + Log.Information("[workspace] connecting..."); client.sendMessage($"{workspacePrefix}/connect",passcode); } private void finishConnection() { //TODO - System.Console.WriteLine($"[workspace] connected to <{name}> running on QLab version <{version}>"); + Log.Information($"[workspace] connected to <{name}> running on QLab version <{version}>"); connected = true; startReceivingUpdates(); //fetchQLabVersion(); @@ -143,7 +181,7 @@ namespace QSharp public void disconnect() { //TODO - Console.WriteLine($"[workspace] disconnect: {name}"); + Log.Information($"[workspace] disconnect: {name}"); stopHeartbeat(); stopReceivingUpdates(); disconnectFromWorkspace(); @@ -163,7 +201,7 @@ namespace QSharp #endregion #region Cues - public QCue cueWithId(string uid) + public QCue cueWithID(string uid) { return root.cueWithID(uid); } @@ -249,14 +287,19 @@ namespace QSharp #region Cue Getters/Setters public void valueForKey(QCue cue, string key) { client.sendMessage(addressForCue(cue, key)); } + + public void valuesForKeys(QCue cue, string[] keys) + { + string keyString = JsonConvert.SerializeObject(keys); + client.sendMessage(addressForCue(cue, "valuesForKeys"), keyString); + } + + public void updatePropertySend(QCue cue, object value, string key) { + client.sendMessage(addressForCue(cue, key), value); + } + + public void updatePropertiesSend(QCue cue, object[] values, string key) { client.sendMessage(addressForCue(cue, key), values); } - //TODO - public void valuesForKeys(QCue cue, string[] keys) { } - //TODO - public void updatePropertySend(QCue cue, object value, string key) { } - //TODO - public void updatePropertiesSend(QCue cue, object[] values, string key) { } - //TODO public void updateAllCueProperties() { root.sendAllPropertiesToQLab(); @@ -270,9 +313,34 @@ namespace QSharp #region Property Fetching //TODO - public void fetchPropertiesForCue(QCue cue, string[] keys) - { + public void fetchDefaultPropertiesForCue(QCue cue) + { + string[] keys = new string[] { QOSCKey.UID, QOSCKey.Number, QOSCKey.Name, + QOSCKey.ListName, QOSCKey.Type, QOSCKey.ColorName, + QOSCKey.Flagged, QOSCKey.Armed, QOSCKey.Notes }; + fetchPropertiesForCue(cue, keys, false); + } + + public void fetchBasicPropertiesForCue(QCue cue) + { + string[] keys = new string[] { QOSCKey.Name, QOSCKey.Number, QOSCKey.FileTarget, QOSCKey.CueTargetNumber, + QOSCKey.HasFileTargets, QOSCKey.HasCueTargets, QOSCKey.Armed, QOSCKey.ColorName, + QOSCKey.ContinueMode, QOSCKey.Flagged, QOSCKey.PreWait, QOSCKey.PostWait, + QOSCKey.Duration, QOSCKey.AllowsEditingDuration }; + fetchPropertiesForCue(cue, keys, false); + } + + public void fetchPropertiesForCue(QCue cue, string[] keys, bool includeChildren) + { + valuesForKeys(cue, keys); + + if (!includeChildren) + return; + foreach (var childCue in cue.cues) + { + fetchPropertiesForCue(cue, keys, includeChildren); + } } #endregion @@ -280,12 +348,14 @@ namespace QSharp public void sendMessage(string address, params object[] args) { //check for workspace prefix? + if (!address.StartsWith(workspacePrefix)) + address = workspacePrefix + address; client.sendMessage(address, args); } private string addressForCue(QCue cue, string action) { - return $"{workspacePrefix}/cue_id/{cue.propertyForKey("uniqueId")}/{action}"; + return $"{workspacePrefix}/cue_id/{cue.propertyForKey(QOSCKey.UID)}/{action}"; } private string addressForWildcardNumber(string number, string action) { @@ -314,23 +384,23 @@ namespace QSharp { //clear passcode if there was one set in the connect() method this.passcode = ""; - Console.WriteLine($"[workspace] *** Error: Password for workspace {name} was incorrect!"); + Log.Error($"[workspace] *** Password for workspace {name} was incorrect!"); } else - Console.WriteLine($"[workspace] *** Error: Unable to connect to workspace: {name} on server: {server.name}"); + Log.Error($"[workspace] *** Unable to connect to workspace: {name} on server: {server.name}"); } - private void OnWorkspaceConnection(object source, QWorkspaceConnectedArgs args) + private void OnWorkspaceConnected(object source, QWorkspaceConnectedArgs args) { + Log.Debug($"[workspace] Connection being finished."); finishConnection(); - Console.WriteLine($"[workspace] Connection finished this is the first cue in this workspace {root.firstCue.displayName}"); } private void OnWorkspaceDisconnected(object source, QWorkspaceDisconnectedArgs args) { //this might not be called with TCP? - Console.WriteLine($"[workspace] *** Workspace has indicated it is disconnecting"); + Log.Warning($"[workspace] *** Workspace has indicated it is disconnecting"); } private void OnCueListsUpdated(object source, QCueListsUpdatedArgs args) @@ -399,31 +469,82 @@ namespace QSharp //add Event handled? use CueUpdated one? } - Console.WriteLine($"[workspace] cueLists finished processing. root updated? {rootCueUpdated}"); + Log.Debug($"[workspace] cueLists finished processing. root updated? {rootCueUpdated}"); - Print(); + + OnWorkspaceUpdated(); } + private void OnCueListChangedPlaybackPosition(object source, QCueListChangedPlaybackPositionArgs args) + { + QCue cueList = cueWithID(args.cueListID); + + if (cueList == null) + return; + if (cueList.ignoreUpdates) + return; + + cueList.setProperty(args.cueID, QOSCKey.PlaybackPositionId, false); + + QCue selectedCue = cueWithID(args.cueID); + if (selectedCue != null) + { + Log.Information($"[workspace] cue list <{cueList.displayName}> playback position changed to <{selectedCue.displayName}>"); + CueListChangedPlaybackPosition?.Invoke(this, new QCueListChangedPlaybackPositionArgs { cueListID = cueList.uid, cueID = selectedCue.uid }); + + } + + + } //OnWorkspaceUpdated //OnWorkspaceSettingsUpdated //LightDashboardUpdated //PreferencesUpdated - //CueNeedsUpdated - //CueUpdated - //ClientShouldDisconnect + + private void OnCueNeedsUpdated(object source, QCueNeedsUpdatedArgs args) + { + QCue cueToUpdate = cueWithID(args.cueID); + if (cueToUpdate == null) + return; + + fetchBasicPropertiesForCue(cueToUpdate); + } + + private void OnCueUpdated(object source, QCueUpdatedArgs args) + { + QCue cue = cueWithID(args.cueID); + + if (cue == null) + return; + if (cue.ignoreUpdates) + return; + + cue.updatePropertiesWithDictionary(args.data); + + } + + private void OnWorkspaceUpdated() + { + WorkspaceUpdated?.Invoke(this, new QWorkspaceUpdatedArgs()); + } + #endregion + public void Close() + { + client.Close(); + } #region Printing public void Print() { - Console.WriteLine($"[workspace] {name}"); + Log.Information($"[workspace] {name}"); foreach (var cueList in root.cues) { cueList.Print(); } - Console.WriteLine("[workspace] End of Workspace"); + Log.Information("[workspace] End of Workspace"); } #endregion }