Basic workspace connection and Cue Lists fetching working

This commit is contained in:
Joel Wetzell
2020-06-09 19:03:33 -05:00
parent 830e905300
commit 130366acad
14 changed files with 1716 additions and 147 deletions
+41 -4
View File
@@ -1,23 +1,60 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QSharp", "QSharp\QSharp.csproj", "{51CD2579-79C9-49A9-B68D-93CBE878BAA1}"
# Visual Studio Version 16
VisualStudioVersion = 16.0.30128.74
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QSharp", "QSharp\QSharp.csproj", "{51CD2579-79C9-49A9-B68D-93CBE878BAA1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QSharpDemo", "QSharpDemo\QSharpDemo.csproj", "{8518D042-D511-4DBB-8406-5B11B2777ED2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QSharpDemo", "QSharpDemo\QSharpDemo.csproj", "{8518D042-D511-4DBB-8406-5B11B2777ED2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Debug|ARM.ActiveCfg = Debug|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Debug|ARM.Build.0 = Debug|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Debug|x64.ActiveCfg = Debug|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Debug|x64.Build.0 = Debug|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Debug|x86.ActiveCfg = Debug|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Debug|x86.Build.0 = Debug|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Release|Any CPU.Build.0 = Release|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Release|ARM.ActiveCfg = Release|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Release|ARM.Build.0 = Release|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Release|x64.ActiveCfg = Release|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Release|x64.Build.0 = Release|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Release|x86.ActiveCfg = Release|Any CPU
{51CD2579-79C9-49A9-B68D-93CBE878BAA1}.Release|x86.Build.0 = Release|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Debug|ARM.ActiveCfg = Debug|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Debug|ARM.Build.0 = Debug|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Debug|x64.ActiveCfg = Debug|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Debug|x64.Build.0 = Debug|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Debug|x86.ActiveCfg = Debug|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Debug|x86.Build.0 = Debug|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Release|Any CPU.Build.0 = Release|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Release|ARM.ActiveCfg = Release|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Release|ARM.Build.0 = Release|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Release|x64.ActiveCfg = Release|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Release|x64.Build.0 = Release|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Release|x86.ActiveCfg = Release|Any CPU
{8518D042-D511-4DBB-8406-5B11B2777ED2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E5BF7535-FB56-4373-89F2-B0ABBD010092}
EndGlobalSection
EndGlobal
+72
View File
@@ -0,0 +1,72 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
namespace QSharp
{
public class QServerFoundArgs : EventArgs
{
public QServer server { get; set; }
}
public class QWorkspacesUpdatedArgs : EventArgs
{
public List<QWorkspaceInfo> Workspaces { get; set; }
}
public class QWorkspaceUpdatedArgs : EventArgs
{
}
public class QWorkspaceSettingsUpdatedArgs : EventArgs
{
public string settingsType { get; set; }
}
public class QWorkspaceLightDashboardUpdatedArgs : EventArgs
{
}
public class QWorkspaceConnectedArgs : EventArgs
{
}
public class QWorkspaceDisconnectedArgs : EventArgs
{
}
public class QWorkspaceConnectionErrorArgs : EventArgs
{
public string status { get; set; }
}
public class QQLabPreferencesUpdatedArgs : EventArgs
{
public string key { get; set; }
}
public class QCueUpdatedArgs : EventArgs
{
}
public class QCueListsUpdatedArgs : EventArgs
{
public JToken data { get; set; }
}
public class QCueNeedsUpdatedArgs : EventArgs
{
public string cueID { get; set; }
}
public class QCueListChangedPlaybackPositionArgs : EventArgs
{
public string cueID { get; set; }
}
}
+21
View File
@@ -0,0 +1,21 @@
namespace QSharp
{
public delegate void QServerFoundHandler(object source, QServerFoundArgs args);
public delegate void QWorkspacesUpdatedHandler(object source, QWorkspacesUpdatedArgs args);
public delegate void QWorkspaceUpdatedHandler(object source, QWorkspaceUpdatedArgs args);
public delegate void QWorkspaceSettingsUpdatedHandler(object source, QWorkspaceSettingsUpdatedArgs args);
public delegate void QWorkspaceLightDashboardUpdatedHandler(object source, QWorkspaceLightDashboardUpdatedArgs args);
public delegate void QWorkspaceConnectedHandler(object source, QWorkspaceConnectedArgs args);
public delegate void QWorkspaceDisconnectedHandler(object source, QWorkspaceDisconnectedArgs args);
public delegate void QWorkspaceConnectionErrorHandler(object source, QWorkspaceConnectionErrorArgs args);
public delegate void QQLabPreferencesUpdatedHandler(object source, QQLabPreferencesUpdatedArgs args);
public delegate void QCueUpdatedHandler(object source, QCueUpdatedArgs args);
public delegate void QCueListsUpdatesHandler(object source, QCueListsUpdatedArgs args);
public delegate void QCueNeedsUpdatedHandler(object source, QCueNeedsUpdatedArgs args);
public delegate void QCueListChangedPlaybackPositionHandler(object source, QCueListChangedPlaybackPositionArgs args);
}
+37 -17
View File
@@ -6,49 +6,50 @@ namespace QSharp
{
public class QBrowser
{
const string UDPServiceType = "_qlab._udp.local.";
const string TCPServiceType = "_qlab._tcp.local.";
const int udpPort = 53000;
ZeroconfResolver.ResolverListener zeroconfTCPBrowser;
IObservable<IZeroconfHost> netServices;
ZeroconfResolver.ResolverListener netServiceTCPBrowser;
public ObservableCollection<QServer> servers = new ObservableCollection<QServer>();
public event QServerFoundHandler ServerFound;
ObservableCollection<QServer> servers = new ObservableCollection<QServer>();
public QBrowser()
{
Console.WriteLine("QBrowser Init");
netServiceTCPBrowser = ZeroconfResolver.CreateListener(TCPServiceType);
netServiceTCPBrowser.ServiceFound += ServerFound;
netServiceTCPBrowser.ServiceLost += ServerLost;
zeroconfTCPBrowser = ZeroconfResolver.CreateListener(QBonjour.TCPService);
zeroconfTCPBrowser.ServiceFound += ZeroconfHostFound;
zeroconfTCPBrowser.ServiceLost += ZeroconfHostLost;
}
private void ServerLost(object sender, IZeroconfHost e)
private void ZeroconfHostLost(object sender, IZeroconfHost e)
{
Console.WriteLine($"Lost {e.DisplayName} : {e.IPAddress}");
}
private void ServerFound(object sender, IZeroconfHost e)
private void ZeroconfHostFound(object sender, IZeroconfHost e)
{
foreach(var service in e.Services)
{
if (service.Key.Equals(TCPServiceType))
if (service.Key.Equals(QBonjour.TCPService))
{
Console.WriteLine($"Found {e.DisplayName} : {e.IPAddress} : {service.Value.Port}");
QServer server = getServerFromAddress(e.IPAddress);
QServer server = serverForAddress(e.IPAddress);
if(server == null)
{
Console.WriteLine("New Server Found so adding");
QServer serverToAdd = new QServer(e.IPAddress, service.Value.Port, e.DisplayName);
QServer serverToAdd = new QServer(e.IPAddress, service.Value.Port);
serverToAdd.name = e.DisplayName;
serverToAdd.zeroconfHost = e;
servers.Add(serverToAdd);
serverToAdd.refreshWorkspaces();
OnServerFound(serverToAdd);
}
else
{
@@ -60,9 +61,8 @@ namespace QSharp
}
public QServer getServerFromAddress(string address)
public QServer serverForAddress(string address)
{
QServer foundServer = null;
foreach(var server in servers)
{
if (server.host.Equals(address))
@@ -71,7 +71,27 @@ namespace QSharp
}
}
return foundServer;
return null;
}
public QServer serverForIZeroconfHost(IZeroconfHost zeroconfHost)
{
foreach (var server in servers)
{
if (server.zeroconfHost == zeroconfHost)
{
return server;
}
}
return null;
}
protected virtual void OnServerFound(QServer server)
{
if (ServerFound != null)
ServerFound(this, new QServerFoundArgs { server = server });
}
}
}
+179 -31
View File
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SharpOSC;
namespace QSharp
@@ -9,62 +11,208 @@ namespace QSharp
{
TCPClient tcpClient;
public class WorkspacesUpdatedArgs : EventArgs
{
public List<QWorkspaceInfo> Workspaces { get; set; }
}
public event QWorkspacesUpdatedHandler WorkspacesUpdated;
public event QCueUpdatedHandler CueUpdated;
public event QCueListsUpdatesHandler CueListsUpdated;
public event QCueNeedsUpdatedHandler CueNeedsUpdated;
public event QCueListChangedPlaybackPositionHandler CueListChangedPlaybackPosition;
public delegate void WorkspacesUpdatedHandler(object source, WorkspacesUpdatedArgs args);
public event WorkspacesUpdatedHandler WorkspacesUpdated;
public event QWorkspaceUpdatedHandler WorkspaceUpdated;
public event QWorkspaceSettingsUpdatedHandler WorkspaceSettingsUpdated;
public event QWorkspaceLightDashboardUpdatedHandler WorkspaceLightDashboardUpdated;
public event QQLabPreferencesUpdatedHandler QLabPreferencesUpdated;
public event QWorkspaceDisconnectedHandler WorkspaceDisconnected;
public event QWorkspaceConnectedHandler WorkspaceConnected;
public event QWorkspaceConnectionErrorHandler WorkspaceConnectionError;
public QClient(string host, int port)
{
tcpClient = new TCPClient(host, port);
tcpClient.Connect();
Console.WriteLine($"QClient setup {host}:{port}");
tcpClient.MessageReceived += ResponseReceived;
Console.WriteLine($"[QClient] setup connection to: <{host}:{port}>");
tcpClient.MessageReceived += ProcessMessage;
}
private void ResponseReceived(object source, MessageEventArgs args)
public bool IsConnnected { get { return tcpClient.IsConnected; } }
public bool connect()
{
OscMessage msg = args.Message;
foreach (var obj in msg.Arguments)
{
QResponse response = JsonConvert.DeserializeObject<QResponse>(obj.ToString());
switch (response.getReplyType())
{
case "WORKSPACES":
Console.WriteLine("WORKSPACES RECEIVED");
OnWorkspacesUpdated(response);
break;
default:
break;
}
}
return tcpClient.Connect();
}
public void disconnect()
{
tcpClient.Close();
}
public void sendMessage(string address, params object[] args)
{
tcpClient.Send(new OscMessage(address, args));
Console.WriteLine($"QClient send message {address}");
// Console.WriteLine($"QClient send message {address}");
}
protected virtual void OnWorkspacesUpdated(QResponse response)
private void ProcessMessage(object source, MessageEventArgs args)
{
QMessage message = new QMessage(args.Message);
if (message.IsReply)
{
JToken data = message.response;
//special case, want to update cue properties
if (message.IsReplyFromCue)
{
//todo check data type?
}
else if (message.IsReplyFromCueLists)
{
OnCueListsUpdated(message.response);
}
else if (message.IsWorkspacesInfo)
{
OnWorkspacesUpdated(message);
}
else if (message.IsConnect)
{
if (message.response.ToString() == "ok")
OnWorkspaceConnected();
else
OnWorkspaceConnectionError(message.response.ToString());
}
else
{
Console.WriteLine($"[client] unhandled reply message: {message.address}");
}
}
else if(message.IsUpdate) {
if (message.IsCueUpdate)
{
OnCueNeedsUpdated(message.cueID);
}
else if (message.IsPlaybackPositionUpdate)
{
OnCueListChangedPlaybackPosition(message.cueID);
}
else if (message.IsWorkspaceUpdate)
{
OnWorkspaceUpdated();
}
else if (message.IsWorkspaceSettingsUpdate)
{
string settingsType = message.AddressParts.Last();
if (settingsType == null)
return;
OnWorkspaceSettingsUpdated(settingsType);
}
else if (message.IsLightDashboardUpdate)
{
//need to do checks for 4.2 or newer
OnWorkspaceLightDashboardUpdated();
}
else if ( message.IsPreferencesUpdate)
{
//need to do checks for 4.2 or newer
string key = message.AddressParts.Last();
if (key == null)
return;
OnQLabPreferencesUpdated(key);
}
else if (message.IsDisconnect)
{
Console.WriteLine($"[client] disconnect message received: {message.address}");
OnWorkspaceDisconnected();
}
else
{
Console.WriteLine($"[client] unhandled update message: {message.address}");
}
}
else
{
Console.WriteLine($"[client] unhandled message: {message.address}");
}
}
protected virtual void OnCueNeedsUpdated(string cueID)
{
if (CueNeedsUpdated != null)
CueNeedsUpdated(this, new QCueNeedsUpdatedArgs { cueID = cueID });
}
protected virtual void OnCueListsUpdated(JToken response)
{
if (CueListsUpdated != null)
CueListsUpdated(this, new QCueListsUpdatedArgs { data = response });
}
protected virtual void OnCueListChangedPlaybackPosition(string cueID)
{
if (CueListChangedPlaybackPosition != null)
CueListChangedPlaybackPosition(this, new QCueListChangedPlaybackPositionArgs { cueID = cueID });
}
protected virtual void OnWorkspaceUpdated()
{
if (WorkspaceUpdated != null)
WorkspaceUpdated(this, new QWorkspaceUpdatedArgs());
}
protected virtual void OnWorkspaceSettingsUpdated(string settingsType)
{
if (WorkspaceSettingsUpdated != null)
WorkspaceSettingsUpdated(this, new QWorkspaceSettingsUpdatedArgs { settingsType = settingsType});
}
protected virtual void OnWorkspaceLightDashboardUpdated()
{
if (WorkspaceLightDashboardUpdated != null)
WorkspaceLightDashboardUpdated(this, new QWorkspaceLightDashboardUpdatedArgs());
}
protected virtual void OnQLabPreferencesUpdated(string key)
{
if (QLabPreferencesUpdated != null)
QLabPreferencesUpdated(this, new QQLabPreferencesUpdatedArgs { key = key });
}
protected virtual void OnWorkspaceDisconnected()
{
if (WorkspaceDisconnected != null)
WorkspaceDisconnected(this, new QWorkspaceDisconnectedArgs());
}
protected virtual void OnWorkspaceConnected()
{
if (WorkspaceConnected != null)
WorkspaceConnected(this, new QWorkspaceConnectedArgs());
}
protected virtual void OnWorkspaceConnectionError(string status)
{
if (WorkspaceConnectionError != null)
WorkspaceConnectionError(this, new QWorkspaceConnectionErrorArgs { status = status});
}
protected virtual void OnWorkspacesUpdated(QMessage message)
{
if(WorkspacesUpdated != null)
{
List<QWorkspaceInfo> workspaces = new List<QWorkspaceInfo>();
foreach (var item in response.data)
foreach (var item in message.response)
{
QWorkspaceInfo workspacefound = JsonConvert.DeserializeObject<QWorkspaceInfo>(item.ToString());
workspaces.Add(workspacefound);
Console.WriteLine(workspacefound.displayName);
}
WorkspacesUpdated(this, new WorkspacesUpdatedArgs { Workspaces = workspaces });
WorkspacesUpdated(this, new QWorkspacesUpdatedArgs { Workspaces = workspaces });
}
}
}
}
+668 -5
View File
@@ -1,19 +1,682 @@
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace QSharp
{
public class QCue
{
Dictionary<string,object> cueData = new Dictionary<string,object>();
public QWorkspace workspace;
public Dictionary<string, object> cueData;
public List<QCue> childCues;
public Dictionary<string, QCue> childCuesUIDMap;
public int sortIndex;
private bool needsSortChildCues;//?
private bool needsNotifyCueUpdated;//?
public bool ignoreUpdates;
public QCue()
{
init();
}
public object propertyForKey(string key)
public QCue(QWorkspace workspace)
{
if (cueData.ContainsKey(key))
return cueData[key];
init();
this.workspace = workspace;
}
public QCue(JToken dict, QWorkspace workspace)
{
init();
this.workspace = workspace;
JToken children = dict[QOSCKey.Cues];
if(children != null && children.Type == JTokenType.Array)
{
//Console.WriteLine($"[cue] new cue being created with {children.Count()} childCues");
foreach (var aChildDict in children)
{
string uid = (string)aChildDict[QOSCKey.UID];
//Console.WriteLine($"[cue] childDict with uid: {uid} being processed");
if (uid == null)
continue;
QCue child = new QCue(aChildDict, workspace);
childCues.Add(child);
childCuesUIDMap.Add(uid, child);
}
}
updatePropertiesWithDictionary(dict);
}
public void init()
{
cueData = new Dictionary<string, object> {
{ QOSCKey.Flagged, false },
{ QOSCKey.Armed, false },
{ QOSCKey.PreWait, false },
{ QOSCKey.PercentPreWaitElapsed, false },
{ QOSCKey.PercentActionElapsed, false },
{ QOSCKey.PostWait, false },
{ QOSCKey.PercentPostWaitElapsed, false },
{ QOSCKey.IsPanicking, false },
{ QOSCKey.IsTailingOut, false },
{ QOSCKey.IsRunning, false },
{ QOSCKey.IsLoaded, false },
{ QOSCKey.IsPaused, false },
{ QOSCKey.IsBroken, false },
{ QOSCKey.IsOverridden, false },
{ QOSCKey.ContinueMode, false }
};
//TODO init cueData with some keys
childCues = new List<QCue>();
childCuesUIDMap = new Dictionary<string, QCue>();
}
public string description
{
get
{
return $"(Cue: {this}) name: {name} [id:{uid} number:{number} type: {type}";
}
}
//isEqual?
//hash?
//compare?
public bool isEqualToCue(QCue cue) { throw new NotImplementedException(); }
public void setWorkspace(QWorkspace workspace)
{
if(this.workspace != workspace)
{
this.workspace = workspace;
}
}
public void addChildCue(QCue cue)
{
string uid = propertyForKey(QOSCKey.UID).ToString();
addChildCue(cue, uid);
}
public void addChildCue(QCue cue, string uid)
{
if (uid.Length == 0)
return;
childCues.Add(cue);
childCuesUIDMap.Add(uid, cue);
//some sorting of the childCues needs to be done?
//reset sorting index
}
public void removeChildCue(QCue cue) { throw new NotImplementedException(); }
public void removeAllChildCues() { throw new NotImplementedException(); }
public void removeChildCuesWithIDs(List<string> uids) { throw new NotImplementedException(); }
//copied: yes
//implemented: no
#region Class Methods
public string iconForType(string type) { throw new NotImplementedException(); }
public bool cueTypeIsAudio(string type) { throw new NotImplementedException(); }
public bool cueTypeIsVideo(string type) { throw new NotImplementedException(); }
public bool cueTypeIsGroup(string type) { throw new NotImplementedException(); }
public bool cueTypeIsCueList(string type) { throw new NotImplementedException(); }
public List<string> fadeModeTitles() { throw new NotImplementedException(); }
#endregion
//KVC Compliance... do I need this?
//copied: yes
//implemented: not all
#region Convenience Accessors
public List<QCue> cues
{
get
{
List<QCue> cues = (List<QCue>)propertyForKey(QOSCKey.Cues);
if (cues == null)
return new List<QCue>();
return cues;
}
}
public string parentID
{
get
{
return propertyForKey(QOSCKey.Parent).ToString();
}
}
public string playbackPositionID() { throw new NotImplementedException(); }
public string name
{
get
{
return propertyForKey(QOSCKey.Name).ToString();
}
}
public string number
{
get
{
return propertyForKey(QOSCKey.Number).ToString();
}
}
public string uid
{
get
{
return propertyForKey(QOSCKey.UID).ToString();
}
}
public string listName
{
get
{
return propertyForKey(QOSCKey.ListName).ToString();
}
}
public string type
{
get
{
return propertyForKey(QOSCKey.Type).ToString();
}
}
public string notes
{
get
{
return propertyForKey(QOSCKey.Notes).ToString();
}
}
public bool IsFlagged
{
get
{
return (bool)propertyForKey(QOSCKey.Flagged);
}
}
public bool IsOverridden
{
get
{
return (bool)propertyForKey("OSCNameKey");
}
}
public bool IsBroken
{
get
{
return (bool)propertyForKey("OSCNameKey");
}
}
public bool IsRunning
{
get
{
return (bool)propertyForKey("OSCNameKey");
}
}
public bool IsTailingOut
{
get
{
return (bool)propertyForKey("OSCNameKey");
}
}
public bool IsPanicking
{
get
{
return (bool)propertyForKey("OSCNameKey");
}
}
public bool IsGroup
{
get
{
if (type.Equals(QCueType.Group))
return true;
if (type.Equals(QCueType.CueList))
return true;
if (type.Equals(QCueType.Cart))
return true;
return false;
}
}
public bool IsCueList
{
get
{
if ( type.Equals(QCueType.CueList))
return true;
return false;
}
}
public string displayName
{
get
{
string number = this.number;
if (number.Length > 0)
return $"{number} \u00b7 {nonEmptyName}";
else
return nonEmptyName;
}
}
public string nonEmptyName
{
get
{
//TODO
string nonEmptyName;
if (name != null && !name.Equals(""))
nonEmptyName = name;
else if (listName != null && !listName.Equals(""))
nonEmptyName = listName;
else
nonEmptyName = $"Untitled {type} Cue";
return nonEmptyName;
}
}
public string workspaceName{ get { return workspace.name; } }
public double currentDuration
{
get
{
//try v4 current duration key first
if (propertyForKey(QOSCKey.CurrentDuration) != null)
return (double)propertyForKey(QOSCKey.CurrentDuration);
else
return (double)propertyForKey(QOSCKey.Duration);
}
}
public string audioFadeModeName
{
get
{
//TODO
if (type.Equals(QCueType.Fade))
return "";
else
return null;
}
}
public string geoFadeModeName
{
get
{
//TODO
if (type.Equals(QCueType.Fade))
return "";
else
return null;
}
}
public string surfaceName{ get { return propertyForKey("surfaceName").ToString(); } }
public string patchName
{
get
{
//TODO
return (string)propertyForKey("patchDecription");
}
}
//TODO: THIS IS WHERE THINGS START TO GET TRICKY!
//TODO add object color method
public string color { get { return propertyForKey(QOSCKey.ColorName).ToString(); } }
//TODO add quaternion property and setter
public Size surfaceSize() { throw new NotImplementedException(); }
public Size cueSize() { throw new NotImplementedException(); }
public List<string> availableSurfaceName() { throw new NotImplementedException(); }
public List<string> propertyKeys
{
get
{
//TODO add null check?
return cueData.Keys.ToList();
}
}
public bool hasChildren
{
get
{
return cues.Count > 0;
}
}
public QCue firstCue
{
get
{
return cues.First();
}
}
public QCue lastCue
{
get
{
return cues.Last();
}
}
public void setIgnoreUpdates(bool ignoreUpdates)
{
if(this.ignoreUpdates != ignoreUpdates)
{
this.ignoreUpdates = ignoreUpdates;
if (!this.ignoreUpdates)
{
//TODO: update local with QLab
}
}
}
#endregion
//Copied: Yes
//Implemented: Not all
#region Mutators
public void setCues(List<QCue> cues)
{
}
public void setName() { }
public void setNumber() { }
public void setUid() { }
public void setListName() { }
public void setType() { }
public void setFlagged() { }
#endregion
//Copied: Yes
//Implemented: Not all
#region Update Methods
public bool updatePropertiesWithDictionary(JToken dict)
{
return updatePropertiesWithDictionary(dict, true);
}
public bool updatePropertiesWithDictionary(JToken dict, bool notify)
{
bool cueUpdated = false;
//TODO
JObject dictObj = (JObject)dict;
foreach (var obj in dictObj)
{
//Console.WriteLine($"[cue] found property {obj.Key} ");
JToken value = obj.Value;
if (obj.Key.Equals(QOSCKey.Cues))
{
if (value.Type != JTokenType.Array)
continue;
updateChildCuesWithPropertiesArray(value, false);
}
else
{
bool didSetProptery = setProperty(value, obj.Key, false);
if (didSetProptery)
cueUpdated = true;
}
}
if (!cueUpdated)
return false;
if (notify)
Console.WriteLine("should notify cue updated.");
//something about notifying cueUpdated ? OnCueUpdated event handler maybe?
return true;
}
//enqueue updated notification?
public void updateChildCuesWithPropertiesArray(JToken value, bool removeUnused)
{
if (!workspace.connected)
return;
List<string> previousUids = null;
if (removeUnused)
previousUids = allChildCueUids();
int index = 0;
foreach (var dict in value)
{
string uid = (string)dict[QOSCKey.UID];
if (uid == null)
continue;
if (removeUnused)
previousUids.Remove(uid);
QCue child = cueWithID(uid, false);
if (child != null)
{
bool didUpdateProperties = updatePropertiesWithDictionary(dict);
if (didUpdateProperties)
needsNotifyCueUpdated = true;
if (child.sortIndex != index)
{
child.sortIndex = index;
needsSortChildCues = true;
needsNotifyCueUpdated = true;
}
}
else
{
child = new QCue(dict, workspace);
child.sortIndex = index;
addChildCue(child, uid);
needsNotifyCueUpdated = true;
}
index++;
}
//TODO: TEST?!
}
#endregion
//Methods Copied: Yes
//Methods Implemented: Not all
#region Children Cues
public List<string> allChildCueUids()
{
//TODO
return new List<string>();
}
public QCue cueAtIndex(int index)
{
//TODO
return new QCue();
}
public QCue cueWithID(string uid)
{
return cueWithID(uid, true);
}
public QCue cueWithID(string uid, bool includeChildren)
{
//Console.WriteLine($"[cue] searching for cue with id: {uid} - includeChildren: {includeChildren}");
if(childCuesUIDMap.ContainsKey(uid))
return childCuesUIDMap[uid];
if (!includeChildren)
return null;
foreach(var cue in cues)
{
if (!cue.IsGroup)
continue;
QCue childCue = cue.cueWithID(uid, includeChildren);
if (childCue != null)
return childCue;
}
return null;
}
public QCue cueWithNumber(string number)
{
//TODO
return new QCue();
}
public object propertyForKey(string key)
{
//TODO: implerment special key checks color, surfaceName, patchDescription,
if (key.Equals(QOSCKey.Cues))
{
return childCues;
}
if (cueData.ContainsKey(key))
{
return cueData[key];
}
return null;
}
public bool setProperty(object value, string key)
{
return setProperty( value, key, workspace.defaultSendUpdatesOSC);
}
public bool setProperty(object value, string key, bool osc)
{
//TODO:
object existingValue = null;
if(cueData.ContainsKey(key))
existingValue = cueData[key];
if(existingValue != null)
{
if (existingValue == value || existingValue.Equals(value))
{
//Console.WriteLine($"[cue] skipping set property: {key} with type: {value.GetType()} and value: {value}");
return false;
}
}
//Console.WriteLine($"[cue] attempting set property: {key} with type: {value.GetType()} and value: {value}");
if (workspace.connectedToQLab3 && key.Equals(QOSCKey.Type) && existingValue.Equals(QCueType.CueList) && value.Equals(QCueType.Group))
{
return false;
}
if (key.Equals(QOSCKey.Cues))
{
if (value.GetType() != typeof(List<QCue>))
return false;
Dictionary<string,QCue> newChildCuesUIDMap = new Dictionary<string, QCue>();
int index = 0;
string uid = null;
foreach(var aCue in (List<QCue>)value)
{
uid = aCue.uid;
if (uid == null)
continue;
aCue.sortIndex = index;
newChildCuesUIDMap.Add(uid, aCue);
index++;
}
childCues = (List<QCue>)value;
childCuesUIDMap = newChildCuesUIDMap;
}
else if(key.Equals(QOSCKey.PlaybackPositionId))
{
if (!IsCueList)
return false;
if (value != null && value.GetType() != typeof(string))
return false;
if (value.Equals("none"))
value = null;
if (value != null)
cueData[key] = value;
else
cueData.Remove(key);
}
else
{
if (value != null)
cueData[key] = value;
else
cueData.Remove(key);
}
//Console.WriteLine($"[cue] set property: {key} with type: {value.GetType()} and value: {value}");
//check for type key and update icon? do I want to do this here?
return true;
}
#endregion
//Methods Copied: Yes
//Methods Implemented: Yes
#region Actions
public void start() { workspace.startCue(this); }
public void stop() { workspace.stopCue(this); }
public void pause() { workspace.pauseCue(this); }
public void reset() { workspace.resetCue(this); }
public void load() { workspace.loadCue(this); }
public void resume() { workspace.resumeCue(this); }
public void hardStop() { workspace.hardStopCue(this); }
public void hardPause() { workspace.hardPauseCue(this); }
public void togglePause() { workspace.togglePauseCue(this); }
public void preview() { workspace.previewCue(this); }
public void panic() { workspace.panicCue(this); }
#endregion
#region Printing
public void Print()
{
if (IsCueList)
{
if(cues != null && cues.Count() > 0)
{
Console.WriteLine(displayName);
foreach(var cue in cues)
{
cue.Print();
}
}
}
else
{
Console.WriteLine($"{displayName} : {color}");
}
}
#endregion
}
}
+141
View File
@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace QSharp
{
static class QBonjour
{
public const string TCPService = "_qlab._tcp.local.";
public const string UDPService = "_qlab._udp.local.";
}
static class QCueType
{
public const string Cue = "Cue";
public const string CueList = "Cue List";
public const string Cart = "Cart";
public const string Group = "Group";
public const string Audio = "";
public const string Mic = "Mic";
public const string Video = "Video";
public const string Camera = "Camera";
public const string Text = "Text";
public const string Light = "Light";
public const string Fade = "Fade";
public const string Network = "Network";
public const string MIDI = "MIDI";
public const string MIDIFile = "MIDI File";
public const string Timecode = "Timecode";
public const string Start = "Start";
public const string Stop = "Stop";
public const string Pause = "Paus";
public const string Load = "Load";
public const string Reset = "Reset";
public const string Devamp = "Devamp";
public const string Goto = "GoTo";
public const string Target = "Target";
public const string Arm = "Arm";
public const string Disarm = "Disarm";
public const string Wait = "Wait";
public const string Memo = "Memo";
public const string Script = "Script";
public const string Stagetracker = "Stagetracker";
//v3
public const string OSC = "OSC";
public const string Titles = "Titles";
}
static class QOSCKey
{
public const string UID = "uniqueID";
public const string Type = "type";
public const string Parent = "parent";
public const string Name = "name";
public const string Number = "number";
public const string Notes = "notes";
public const string FileTarget = "fileTarget";
public const string CueTargetNumber = "cueTargetNumber";
public const string CurrentCueTarget = "currentCueTarget";
public const string ColorName = "colorName";
public const string Flagged = "flagged";
public const string Armed = "armed";
public const string ContinueMode = "continueMode";
public const string PreWait = "preWait";
public const string PostWait = "postWait";
public const string CurrentDuration = "currentDuration";
public const string PercentPreWaitElapsed = "percentPreWaitElapsed";
public const string PercentPostWaitElapsed = "percentPostWaitElapsed";
public const string PercentActionElapsed = "percentActionElapsed";
public const string PreWaitElapsed = "preWaitElapsed";
public const string PostWaitElapsed = "postWaitElapsed";
public const string ActionElapsed = "actionElapsed";
public const string GroupMode = "mode";
public const string CartPosition = "cartPosition";
public const string CartRows = "cartRows";
public const string CartColumns = "cartColumns";
public const string HasFileTargets = "hasFileTargets";
public const string HasCueTargets = "hasCueTargets";
public const string AllowsEditingDuration = "allowsEditingDuration";
public const string IsPanicking = "isPanicking";
public const string IsTailingOut = "isTailingOut";
public const string IsRunning = "isRunning";
public const string IsLoaded = "isLoaded";
public const string IsPaused = "isPaused";
public const string IsBroken = "isBroken";
public const string IsOverridden = "isOverridden";
public const string TranslationX = "translationX";
public const string TranslationY = "translationY";
public const string ScaleX = "scaleX";
public const string ScaleY = "scaleY";
public const string OriginX = "originX";
public const string OriginY = "originY";
public const string Quaternion = "quaternion";
public const string SurfaceSize = "surfaceSize";
public const string CueSize = "cueSize";
public const string PreserveAspectRatio = "preserveAspectRatio";
public const string Layer = "layer";
public const string Patch = "patch";
public const string PatchList = "patchList";
public const string SurfaceList = "surfaceList";
public const string Cues = "cues";
public const string ListName = "listName";
public const string SurfaceID = "surfaceID";
public const string FullSurface = "fullSurface";
public const string Opacity = "opacity";
public const string RotationZ = "rotationZ";
public const string RotationY = "rotationY";
public const string RotationX = "rotationX";
public const string PlaybackPositionId = "playbackPositionId";
public const string StartNextCueWhenSliceEnds = "startNextCueWhenSliceEnds";
public const string StopTargetWhenSliceEnds = "stopTargetWhenSliceEnds";
public const string SliderLevel = "sliderLevel";
// v3
public const string Duration = "duration";
public const string FullScreen = "fullScreen";
}
static class QIdentifiers
{
// Identifiers for "fake" cues
public const string RootCue = "__root__";
public const string ActiveCues = "__active__";
}
//these should really be enums
static class QFadeMode
{
public const int Absolute = 0;
public const int Relative = 1;
}
static class QContinueMode
{
public const int NoContinue = 0;
public const int AutoContinue = 1;
public const int AutoFollow = 2;
}
}
+171
View File
@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using SharpOSC;
namespace QSharp
{
public class QMessage
{
private OscMessage OSCMessage;
public QMessage(OscMessage oscMessage)
{
OSCMessage = oscMessage;
}
public string description
{
get
{
//string join probably won't work might need to cast argument objects to strings first
return $"address: {address}, arguments: {string.Join(" - ", arguments)}";
}
}
#region Message Type Checks
public bool IsReply { get { return OSCMessage.Address.StartsWith("/reply"); } }
// /reply/cue_id/1/action
public bool IsReplyFromCue { get{ return OSCMessage.Address.StartsWith("/reply/cue_id"); } }
public bool IsReplyFromCueLists { get { return IsReply && OSCMessage.Address.EndsWith("cueLists"); } }
public bool IsUpdate { get{ return OSCMessage.Address.StartsWith("/update"); } }
public bool IsWorkspaceUpdate {
get {
// /update/workspace/{workspace_id}
string[] parts = AddressParts;
return (parts.Length == 3 && parts[1].Equals("workspace"));
}
}
public bool IsWorkspacesInfo {
get {
// /workspaces
string[] parts = AddressParts;
return (parts.Length == 2 && parts[1] == "workspaces");
}
}
public bool IsWorkspaceSettingsUpdate {
get {
// /update/workspace/{workspace_id}/settings/{settings_controller}
string[] parts = AddressParts;
return (parts.Length == 5 && parts[1].Equals("workspace") && parts[3].Equals("settings"));
}
}
public bool IsLightDashboardUpdate {
get {
// /update/workspace/{workspace_id}/dashboard
string[] parts = AddressParts;
return (parts.Length == 4 && parts[1].Equals("workspace") && parts[3].Equals("dashboard"));
}
}
public bool IsCueUpdate {
get {
// /update/workspace/{workspace_id}/cue_id/{cue_id}
string[] parts = AddressParts;
return (parts.Length == 5 && parts[1].Equals("workspace") && parts[3].Equals("cue_id"));
}
}
public bool IsPlaybackPositionUpdate {
get {
// /update/workspace/{workspace_id}/cueList/{cue_list_id}/playbackPosition {cue_id}
string[] parts = AddressParts;
return (parts.Length == 6 && address.EndsWith("/playbakPosition"));
}
}
public bool IsPreferencesUpdate {
get {
string[] parts = AddressParts;
return (parts.Length == 4 && parts[3].Equals("preferences"));
}
}
public bool IsDisconnect {
get {
string[] parts = AddressParts;
return (parts.Length == 4 && parts[3].Equals("disconnect"));
}
}
public bool IsConnect
{
get
{
string[] parts = AddressParts;
return (parts.Length == 4 && parts[3].Equals("connect"));
}
}
#endregion
public string address { get { return OSCMessage.Address; } }
public string replyAddress { get{ return IsReply ? address.Substring("/reply".Length) : address; } }
public string addressWithoutWorkspace
{
get
{
//TODO
return "";
}
}
public string[] AddressParts { get { return address.Split("/",StringSplitOptions.RemoveEmptyEntries); } }
public JToken response
{
get
{
JObject responseObj = JObject.Parse((string)arguments[0]);
//TODO
return responseObj["data"];
}
}
public List<Object> arguments { get { return OSCMessage.Arguments; } }
public string cueID
{
get
{
if (IsCueUpdate)
{
return AddressParts[4];
}else if (IsPlaybackPositionUpdate)
{
//TODO: check string cast
return arguments.Count > 0 ? (string)arguments[0] : null;
}else if (IsReplyFromCue)
{
return AddressParts[2];
}
else
{
return null;
}
}
}
}
}
-22
View File
@@ -1,22 +0,0 @@
using System;
using System.Text.RegularExpressions;
namespace QSharp
{
public class QResponse
{
private const string WORKSPACES_REGEX = @"/workspaces";
public string status { get; set; }
public string address { get; set; }
public object[] data { get; set; }
public string getReplyType()
{
Console.WriteLine($"QRESPONSE - Address to match: '{address}'");
Match m = Regex.Match(address, WORKSPACES_REGEX);
if (m.Success)
return "WORKSPACES";
return "";
}
}
}
+33 -19
View File
@@ -1,53 +1,67 @@
//General information about a QLab Workspace
using System.Collections.Concurrent;
using System.Collections.Generic;
using Zeroconf;
namespace QSharp
{
public class QServer
{
private QClient qClient;
private QClient client;
public string host;
public int port;
public string name;
public IZeroconfHost zeroconfHost;
List<QWorkspace> workspaces = new List<QWorkspace>();
public QServer(string host, int port, string name)
public QServer(string host, int port)
{
this.host = host;
this.port = port;
this.name = name;
qClient = new QClient(host, port);
qClient.WorkspacesUpdated += OnWorkspacesUpdated;
client = new QClient(host, port);
client.WorkspacesUpdated += OnWorkspacesUpdated;
}
private void OnWorkspacesUpdated(object source, QClient.WorkspacesUpdatedArgs args)
{
foreach(var workspace in args.Workspaces)
{
QWorkspace existingWorkspace = workspaceWithID(workspace.uniqueID);
if(existingWorkspace == null)
{
QWorkspace workspaceToAdd = new QWorkspace(workspace, this);
workspaces.Add(workspaceToAdd);
}
}
}
public string description { get { return $"{name} - {host} - {port}"; } }
public void refreshWorkspaces()
{
qClient.sendMessage("/workspaces");
if (!client.connect())
{
System.Console.WriteLine($"Error: QServer unable to connect to QLab Server: {host}:{port}");
return;
}
client.sendMessage("/workspaces");
}
public QWorkspace workspaceWithID(string uniqueID)
{
foreach(var workspace in workspaces)
{
if (workspace.getUniqueID().Equals(uniqueID))
if (workspace.uniqueID.Equals(uniqueID))
return workspace;
}
return null;
}
#region EventHandlers
private void OnWorkspacesUpdated(object source, QWorkspacesUpdatedArgs args)
{
foreach (var workspace in args.Workspaces)
{
QWorkspace existingWorkspace = workspaceWithID(workspace.uniqueID);
if (existingWorkspace == null)
{
QWorkspace workspaceToAdd = new QWorkspace(workspace, this);
workspaces.Add(workspaceToAdd);
workspaceToAdd.connectWithPasscode("1234");
}
}
}
#endregion
}
}
+324 -40
View File
@@ -1,4 +1,9 @@
//TODO: connect methods, cue property fetch methods, everything else
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
namespace QSharp
{
public class QWorkspace
@@ -7,11 +12,20 @@ namespace QSharp
private QServer server;
private QClient client;
private string name;
private string uniqueID;
private bool connected;
public string name;
public string uniqueID;
public bool connected;
private bool hasPasscode;
private string oscPrefix;
private string passcode;
private QCue root;
private Timer heartbeatTimer;
private int heartbeatAttempts;
public bool defaultSendUpdatesOSC;
public string version;
private void Init()
{
@@ -20,26 +34,42 @@ namespace QSharp
connected = false;
hasPasscode = false;
defaultSendUpdatesOSC = false;
root = new QCue(this);
root.setProperty(QIdentifiers.RootCue, QOSCKey.UID, false);
root.setProperty("Cue Lists", QOSCKey.Name, false);
root.setProperty(QCueType.CueList, QOSCKey.Type, false);
//TODO: add version
version = "3.0.0";
}
public QWorkspace(QWorkspaceInfo workspaceInfo, QServer server)
{
if(workspaceInfo.uniqueID.Length > 0)
{
uniqueID = workspaceInfo.uniqueID;
oscPrefix = workspacePrefix();
}
Init();
if(workspaceInfo.uniqueID.Length > 0)
uniqueID = workspaceInfo.uniqueID;
if (workspaceInfo.version.Length > 0)
version = workspaceInfo.version;
//TODO add version if it has one if no version then it is QLab 3?
updateWithWorkspaceInfo(workspaceInfo);
client = new QClient(server.host, server.port);
this.server = server;
client.WorkspaceConnected += OnWorkspaceConnection;
client.WorkspaceConnectionError += OnWorkspaceConnectionError;
client.WorkspaceDisconnected += OnWorkspaceDisconnected;
client.CueListsUpdated += OnCueListsUpdated;
this.server = server;
Console.WriteLine($"[workspace] <{name}> initizalied for server: {server.name} ");
}
//updateWithDictionary
public bool updateWithWorkspaceInfo(QWorkspaceInfo workspaceInfo) {
bool didUpdate = false;
if(workspaceInfo.displayName.Length > 0 && !workspaceInfo.displayName.Equals(this.name))
@@ -55,61 +85,315 @@ namespace QSharp
return didUpdate;
}
public string description { get { return $"{name} : {uniqueID}"; } }
public string nameWithoutPathExtension { get { return name; } } //TODO
public string getUniqueID()
public string serverName { get { return server.name; } }
public string fullName { get { return $"{name} ({server.name})"; } }
public QCue firstCue { get { return firstCueList.firstCue; } }
public QCue firstCueList { get { return root.firstCue; } }
public string fullNameWithCueList(QCue cueList) { return ""; }
//TODO other convenience methods
public bool connectedToQLab3 { get { return false; } } //TODO implement the version system
#region Connection/reconnection
public void connectWithPasscode(string passcode)
{
return uniqueID;
//TODO
if (!client.connect())
{
System.Console.WriteLine($"[workspace] *** Error: couldn't connect to server");
return;
}
System.Console.WriteLine("[workspace] connecting...");
client.sendMessage($"{workspacePrefix}/connect",passcode);
}
#region OSC address helpers
private string addressForCue(QCue cue, string action)
private void finishConnection()
{
return $"{oscPrefix}/cue_id/{cue.propertyForKey("uniqueId")}/{action}";
//TODO
System.Console.WriteLine($"[workspace] connected to <{name}> running on QLab version <{version}>");
connected = true;
startReceivingUpdates();
//fetchQLabVersion();
fetchCueLists();
}
private string addressForWildcardNumber(string number, string action) {
return $"{oscPrefix}/cue/{number}/{action}";
public void reconnect()
{
if (connected)
return;
connectWithPasscode(passcode);
//todo
}
private string workspacePrefix()
public void disconnect()
{
return $"/workspace/{uniqueID}";
//TODO
Console.WriteLine($"[workspace] disconnect: {name}");
stopHeartbeat();
stopReceivingUpdates();
disconnectFromWorkspace();
connected = false;
client.disconnect();
//root.removeAllChildCues();
}
public void temporarilyDisconnect()
{
//TODO
}
#endregion
#region Cues
public QCue cueWithId(string uid)
{
return root.cueWithID(uid);
}
public QCue cueWithNumber(string number)
{
return root.cueWithNumber(number);
}
#endregion
#region Workspace Methods
public void disconnectFromWorkspace() { client.sendMessage($"{oscPrefix}/disconnect"); }
public void startReceivingUpdates() { client.sendMessage($"{oscPrefix}/updates", 1); }
public void stopReceivingUpdates() { client.sendMessage($"{oscPrefix}/updates", 0); }
public void enableAlwaysReply() { client.sendMessage($"{oscPrefix}/alwaysReply", 1); }
public void disableAlwaysReply() { client.sendMessage($"{oscPrefix}/alwaysReply", 0); }
public void disconnectFromWorkspace() { client.sendMessage($"{workspacePrefix}/disconnect"); }
public void startReceivingUpdates() { client.sendMessage($"{workspacePrefix}/updates", 1); }
public void stopReceivingUpdates() { client.sendMessage($"{workspacePrefix}/updates", 0); }
public void enableAlwaysReply() { client.sendMessage($"{workspacePrefix}/alwaysReply", 1); }
public void disableAlwaysReply() { client.sendMessage($"{workspacePrefix}/alwaysReply", 0); }
public void fetchQLabVersion() { client.sendMessage($"{workspacePrefix}/version"); } //TODO: EventHandler for this
public void fetchCueLists() { client.sendMessage($"{workspacePrefix}/cueLists"); } //TODO: EventHandler for CueListUpdated
public void fetchPlaybackPositionForCue(QCue cue) { client.sendMessage(addressForCue(cue, QOSCKey.PlaybackPositionId)); } //EventHandler for this? can I use the CueListPlaybackPosition one?
public void go() { client.sendMessage($"{workspacePrefix}/go"); }
public void save() { client.sendMessage($"{workspacePrefix}/save"); }
public void undo() { client.sendMessage($"{workspacePrefix}/undo"); }
public void redo() { client.sendMessage($"{workspacePrefix}/redo"); }
public void resetAll() { client.sendMessage($"{workspacePrefix}/reset"); }
public void pauseAll() { client.sendMessage($"{workspacePrefix}/pause"); }
public void resumeAll() { client.sendMessage($"{workspacePrefix}/resume"); }
public void stopAll() { client.sendMessage($"{workspacePrefix}/stop"); }
public void panicAll() { client.sendMessage($"{workspacePrefix}/panic"); }
#endregion
public void fetchQLabVersion() {
#region Heartbeat
//TODO
public void startHeartbeat()
{
clearHeartbeatTimeout();
sendHeartbeat();
}
public void stopHeartbeat()
{
clearHeartbeatTimeout();
heartbeatAttempts = -1;
}
public void clearHeartbeatTimeout()
{
heartbeatTimer.Stop();
heartbeatTimer = null;
heartbeatAttempts = 0;
//TODO
}
public void fetchCueLists()
public void sendHeartbeat()
{
client.sendMessage("/thump");
//TODO
}
public void heartbeatTimeout(object sender, ElapsedEventArgs e)
{
//TODO
}
public void fetchPlaybackPositionForCue()
#endregion
#region Cue Actions
public void startCue(QCue cue) { client.sendMessage(addressForCue(cue, "start")); }
public void stopCue(QCue cue) { client.sendMessage(addressForCue(cue, "stop")); }
public void pauseCue(QCue cue) { client.sendMessage(addressForCue(cue, "pause")); } //TODO: immediately update local for snappier whatever
public void loadCue(QCue cue) { client.sendMessage(addressForCue(cue, "load")); }
public void resetCue(QCue cue) { client.sendMessage(addressForCue(cue, "reset")); }
public void deleteCue(QCue cue) { client.sendMessage(addressForCue(cue, "")); }
public void resumeCue(QCue cue) { client.sendMessage(addressForCue(cue, "resume")); }
public void hardStopCue(QCue cue) { client.sendMessage(addressForCue(cue, "hardStop")); }
public void hardPauseCue(QCue cue) { client.sendMessage(addressForCue(cue, "hardPause")); } //TODO: immediately update local for snappier whatever
public void togglePauseCue(QCue cue) { client.sendMessage(addressForCue(cue, "togglePause")); }
public void previewCue(QCue cue) { client.sendMessage(addressForCue(cue, "preview")); }
public void panicCue(QCue cue) { client.sendMessage(addressForCue(cue, "panic")); } //TODO: immediately update local for snappier whatever
#endregion
#region Cue Getters/Setters
//TODO
#endregion
#region Property Fetching
//TODO
#endregion
#region OSC address helpers
public void sendMessage(string address, params object[] args)
{
//TODO
//check for workspace prefix?
client.sendMessage(address, args);
}
public void go() { client.sendMessage($"{oscPrefix}/go"); }
public void save() { client.sendMessage($"{oscPrefix}/save"); }
public void undo() { client.sendMessage($"{oscPrefix}/undo"); }
public void redo() { client.sendMessage($"{oscPrefix}/redo"); }
public void resetAll() { client.sendMessage($"{oscPrefix}/reset"); }
public void pauseAll() { client.sendMessage($"{oscPrefix}/pause"); }
public void resumeAll() { client.sendMessage($"{oscPrefix}/resume"); }
public void stopAll() { client.sendMessage($"{oscPrefix}/stop"); }
public void panicAll() { client.sendMessage($"{oscPrefix}/panic"); }
private string addressForCue(QCue cue, string action)
{
return $"{workspacePrefix}/cue_id/{cue.propertyForKey("uniqueId")}/{action}";
}
private string addressForWildcardNumber(string number, string action) {
return $"{workspacePrefix}/cue/{number}/{action}";
}
private string workspacePrefix
{
get { return $"/workspace/{uniqueID}"; }
}
#endregion
public string workspaceID
{
get
{
return uniqueID;
}
}
#region Event Handling
private void OnWorkspaceConnectionError(object source, QWorkspaceConnectionErrorArgs args)
{
if (args.status.Equals("badpass"))
Console.WriteLine($"[workspace] *** Error: Password for workspace {name} was incorrect!");
else
Console.WriteLine($"[workspace] *** Error: Unable to connect to workspace: {name} on server: {server.name}");
}
private void OnWorkspaceConnection(object source, QWorkspaceConnectedArgs args)
{
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");
}
private void OnCueListsUpdated(object source, QCueListsUpdatedArgs args)
{
bool rootCueUpdated = false;
List<QCue> currentCueLists = new List<QCue>(args.data.Count());
int index = 0;
foreach (var aCueList in args.data)
{
string uid = aCueList[QOSCKey.UID].ToString();
if (uid == null)
continue;
QCue cueList = root.cueWithID(uid, false);
if(cueList != null)
{
if(cueList.sortIndex != index)
{
cueList.sortIndex = index;
rootCueUpdated = true;
}
}
else
{
cueList = new QCue(aCueList, this);
cueList.sortIndex = index;
if (connectedToQLab3)
cueList.setProperty(QCueType.CueList, QOSCKey.Type);
rootCueUpdated = true;
}
currentCueLists.Add(cueList);
index++;
}
QCue activeCuesList = root.cueWithID(QIdentifiers.ActiveCues, false);
if(activeCuesList != null)
{
if (activeCuesList.sortIndex != index)
{
activeCuesList.setProperty(QIdentifiers.ActiveCues, QOSCKey.UID, false);
activeCuesList.setProperty("Active Cues", QOSCKey.Name, false);
activeCuesList.setProperty(QCueType.CueList, QOSCKey.Type, false);
rootCueUpdated = true;
}
}
else
{
activeCuesList = new QCue(this);
activeCuesList.setProperty(QIdentifiers.ActiveCues, QOSCKey.UID, false);
activeCuesList.setProperty("Active Cues", QOSCKey.Name, false);
activeCuesList.setProperty(QCueType.CueList, QOSCKey.Type, false);
rootCueUpdated = true;
}
currentCueLists.Add(activeCuesList);
if (root.cues.Count() != currentCueLists.Count())
rootCueUpdated = true;
root.setProperty(currentCueLists, QOSCKey.Cues);
if (rootCueUpdated)
{
//add Event handled? use CueUpdated one?
}
Console.WriteLine($"[workspace] cueLists finished processing. root updated? {rootCueUpdated}");
Print();
}
//OnWorkspaceUpdated
//OnWorkspaceSettingsUpdated
//LightDashboardUpdated
//PreferencesUpdated
//CueNeedsUpdated
//CueUpdated
//ClientShouldDisconnect
#endregion
#region Printing
public void Print()
{
foreach(var cueList in root.cues)
{
cueList.Print();
}
}
#endregion
}
}
+24 -6
View File
@@ -46,12 +46,22 @@ namespace SharpOSC
_address = address;
}
public void Connect()
public bool Connect()
{
client = new TcpClient(Address, Port);
Console.WriteLine($"TCPClient - connect called for <{Address}:{Port}>");
Thread receivingThread = new Thread(ReceiveLoop);
receivingThread.Start();
try
{
client = new TcpClient(Address, Port);
Thread receivingThread = new Thread(ReceiveLoop);
receivingThread.Start();
//Console.WriteLine($"TCPClient - connected to <{Address}:{Port}>");
return true;
}
catch (Exception e)
{
//Console.WriteLine(e.Message);
return false;
}
}
public void Send(byte[] message)
@@ -67,6 +77,14 @@ namespace SharpOSC
Send(data);
}
public bool IsConnected
{
get
{
return client.Connected;
}
}
public void ReceiveLoop()
{
while (client.Connected)
@@ -109,7 +127,7 @@ namespace SharpOSC
}
} catch(Exception e)
{
//Log.Debug("TCPSENDER - Receive Exception: " + e.ToString());
//Console.WriteLine("TCPSENDER - Receive Exception: " + e.ToString());
}
}
+1 -3
View File
@@ -8,11 +8,9 @@ namespace qControlDemo
{
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine("qControlDemo Started");
QBrowser browser = new QBrowser();
Thread.Sleep(100000000);
}
}
+4
View File
@@ -5,6 +5,10 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Terminal.Gui" Version="0.81.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QSharp\QSharp.csproj" />
</ItemGroup>