mirror of
https://github.com/jwetzell/QControlKit.git
synced 2026-07-26 10:38:54 +00:00
* App.xaml:
* App.xaml.cs: * MainPage.xaml: * Info.plist: * MainPage.xaml.cs: * AppDelegate.cs: * CueListPage.xaml.cs: * QControlKitXamDemo.csproj: * QCueViewModel.cs: * QServerViewModel.cs: * QBrowserViewModel.cs: * QWorkspaceViewModel.cs: * QControlKitXamDemo.iOS.csproj: * QControlKitXamDemo.Android.csproj: * materialdesignicons-webfont.ttf: * materialdesignicons-webfont.ttf: Update demo to look a bit closer to real * QClient.cs: * QBrowser.cs: Change log levels * QControlKit.csproj: Update Package Version * QCue.cs: decent fix for bool property fetching * QMessage.cs: add ToString() * QServer.cs: Add Logging * TCPClient.cs: Big Restructure using SuperSimpleTcp and SlipDecode GAH! * QWorkspace.cs: Add ability to address active cues
This commit is contained in:
@@ -70,9 +70,11 @@ namespace QControlKit
|
||||
|
||||
if (found == null)
|
||||
{
|
||||
server.disconnect();
|
||||
servers.Remove(server);
|
||||
Log.Information($"[qbrowser] Lost {server.name} : {server.host} : {server.port}");
|
||||
server.disconnect();
|
||||
Log.Information($"[qbrowser] after server disconnect()");
|
||||
|
||||
servers.Remove(server);
|
||||
OnServerLost(server);
|
||||
}
|
||||
}
|
||||
@@ -121,7 +123,8 @@ namespace QControlKit
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach(var server in servers)
|
||||
Log.Information($"[qbrowser] Close requested");
|
||||
foreach (var server in servers)
|
||||
{
|
||||
server.disconnect();
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace QControlKit
|
||||
|
||||
public void disconnect()
|
||||
{
|
||||
Log.Debug($"[client] disconnecting from {tcpClient.Address}");
|
||||
Log.Information($"[client] disconnecting from {tcpClient.Address}");
|
||||
tcpClient.Close();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace QControlKit
|
||||
public void sendMessage(string address, params object[] args)
|
||||
{
|
||||
tcpClient.QueueForSending(new OscMessage(address, args));
|
||||
Log.Debug($"[client] send message {address} : {args}");
|
||||
Log.Information($"[client] send message {address} : {args}");
|
||||
}
|
||||
|
||||
private void ProcessMessage(object source, MessageEventArgs args)
|
||||
@@ -97,7 +97,7 @@ namespace QControlKit
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[client] unhandled reply from cue: Type: {data.Type} value: {message.response}");
|
||||
Log.Error($"[client] unhandled reply from cue: Type: {data.Type} value: {message.response}");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -118,7 +118,7 @@ namespace QControlKit
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[client] unhandled reply message: {message.address}");
|
||||
Log.Error($"[client] unhandled reply message: {message.address}");
|
||||
}
|
||||
}
|
||||
else if(message.IsUpdate) {
|
||||
@@ -162,12 +162,12 @@ namespace QControlKit
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[client] unhandled update message: {message.address}");
|
||||
Log.Error($"[client] unhandled update message: {message.address}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[client] unhandled message: {message.address}");
|
||||
Log.Error($"[client] unhandled message: {message.address}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<Authors>Joel Wetzell</Authors>
|
||||
<Version>1.0.0-devel</Version>
|
||||
<PackOnBuild>true</PackOnBuild>
|
||||
<PackageVersion>0.0.21</PackageVersion>
|
||||
<PackageVersion>0.0.23</PackageVersion>
|
||||
<PackageId>QControlKit</PackageId>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<Summary>Unofficial C# port of Figure53's QLabKit.objc</Summary>
|
||||
@@ -18,6 +18,7 @@
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Zeroconf" Version="3.5.11" />
|
||||
<PackageReference Include="SuperSimpleTcp" Version="2.6.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Constants\" />
|
||||
@@ -29,5 +30,7 @@
|
||||
<None Remove="SharpOSC" />
|
||||
<None Remove="Zeroconf" />
|
||||
<None Remove="SharpOSC\" />
|
||||
<None Remove="SimpleTCP" />
|
||||
<None Remove="SuperSimpleTcp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
+80
-78
@@ -18,6 +18,7 @@ namespace QControlKit
|
||||
public List<QCue> childCues;
|
||||
public Dictionary<string, QCue> childCuesUIDMap;
|
||||
|
||||
|
||||
public int sortIndex;
|
||||
public int nestLevel;
|
||||
|
||||
@@ -43,32 +44,9 @@ namespace QControlKit
|
||||
{
|
||||
init();
|
||||
this.workspace = workspace;
|
||||
|
||||
//this breaks things???
|
||||
//it definitely is in the original but somehow the recursion is not working as I expect..
|
||||
//but cue population still works without this as child cues are populated later
|
||||
|
||||
/*JToken children = dict[QOSCKey.Cues];
|
||||
if(children != null && children.Type == JTokenType.Array)
|
||||
{
|
||||
Log.Debug($"[cue] new cue being created with {children.Count()} childCues");
|
||||
foreach (var aChildDict in children)
|
||||
{
|
||||
string uid = (string)aChildDict[QOSCKey.UID];
|
||||
//Log.Debug($"[cue] childDict with uid: {uid} being processed");
|
||||
if (uid == null)
|
||||
continue;
|
||||
string name = dict[QOSCKey.UID].ToString();
|
||||
Log.Debug($"[cue] calling new cue from {name}");
|
||||
|
||||
//QCue child = new QCue(aChildDict, workspace);
|
||||
|
||||
*//*childCues.Add(child);
|
||||
childCuesUIDMap.Add(uid, child);*//*
|
||||
}
|
||||
}*/
|
||||
|
||||
updatePropertiesWithDictionary(dict);
|
||||
workspace.fetchBasicPropertiesForCue(this);
|
||||
|
||||
}
|
||||
|
||||
public QCue(JToken dict, QWorkspace workspace, int nestLevel)
|
||||
@@ -76,31 +54,8 @@ namespace QControlKit
|
||||
init();
|
||||
this.workspace = workspace;
|
||||
this.nestLevel = nestLevel + 1;
|
||||
//this breaks things???
|
||||
//it definitely is in the original but somehow the recursion is not working as I expect..
|
||||
//but cue population still works without this as child cues are populated later
|
||||
|
||||
/*JToken children = dict[QOSCKey.Cues];
|
||||
if(children != null && children.Type == JTokenType.Array)
|
||||
{
|
||||
Log.Debug($"[cue] new cue being created with {children.Count()} childCues");
|
||||
foreach (var aChildDict in children)
|
||||
{
|
||||
string uid = (string)aChildDict[QOSCKey.UID];
|
||||
//Log.Debug($"[cue] childDict with uid: {uid} being processed");
|
||||
if (uid == null)
|
||||
continue;
|
||||
string name = dict[QOSCKey.UID].ToString();
|
||||
Log.Debug($"[cue] calling new cue from {name}");
|
||||
|
||||
//QCue child = new QCue(aChildDict, workspace);
|
||||
|
||||
*//*childCues.Add(child);
|
||||
childCuesUIDMap.Add(uid, child);*//*
|
||||
}
|
||||
}*/
|
||||
|
||||
updatePropertiesWithDictionary(dict);
|
||||
workspace.fetchBasicPropertiesForCue(this);
|
||||
}
|
||||
|
||||
public void init()
|
||||
@@ -244,7 +199,14 @@ namespace QControlKit
|
||||
{
|
||||
get
|
||||
{
|
||||
return propertyForKey(QOSCKey.Parent).ToString();
|
||||
if(propertyForKey(QOSCKey.Parent) != null)
|
||||
{
|
||||
return propertyForKey(QOSCKey.Parent).ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
public string playbackPositionID {
|
||||
@@ -301,7 +263,14 @@ namespace QControlKit
|
||||
{
|
||||
get
|
||||
{
|
||||
return propertyForKey(QOSCKey.ListName).ToString();
|
||||
if(propertyForKey(QOSCKey.ListName) == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return propertyForKey(QOSCKey.ListName).ToString();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -353,46 +322,39 @@ namespace QControlKit
|
||||
if (workspace.connectedToQLab3 || workspace.isOlderThanVersion("4.2.0"))
|
||||
return false;
|
||||
|
||||
bool overridden = (bool)propertyForKey(QOSCKey.IsOverridden);
|
||||
|
||||
if (overridden)
|
||||
return true;
|
||||
foreach (var cue in cues)
|
||||
{
|
||||
if (cue.IsOverridden)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return getBoolForKey(QOSCKey.IsOverridden);
|
||||
}
|
||||
}
|
||||
public bool IsBroken
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((bool)propertyForKey(QOSCKey.IsBroken))
|
||||
return true;
|
||||
foreach(var cue in cues)
|
||||
foreach (var cue in cues)
|
||||
{
|
||||
if (cue.IsBroken)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return getBoolForKey(QOSCKey.IsBroken);
|
||||
}
|
||||
}
|
||||
public bool IsRunning
|
||||
{
|
||||
get
|
||||
{
|
||||
bool running = (bool)propertyForKey(QOSCKey.IsRunning);
|
||||
|
||||
if (running)
|
||||
return true;
|
||||
foreach(var cue in cues)
|
||||
foreach (var cue in cues)
|
||||
{
|
||||
if (cue.IsRunning)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return getBoolForKey(QOSCKey.IsRunning);
|
||||
}
|
||||
}
|
||||
public bool IsTailingOut
|
||||
@@ -402,16 +364,13 @@ namespace QControlKit
|
||||
if (workspace.connectedToQLab3)
|
||||
return false;
|
||||
|
||||
bool tailingOut = (bool)propertyForKey(QOSCKey.IsTailingOut);
|
||||
|
||||
if (tailingOut)
|
||||
return true;
|
||||
foreach(var cue in cues)
|
||||
foreach (var cue in cues)
|
||||
{
|
||||
if (cue.IsTailingOut)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return getBoolForKey(QOSCKey.IsTailingOut);
|
||||
}
|
||||
}
|
||||
public bool IsPanicking
|
||||
@@ -421,16 +380,13 @@ namespace QControlKit
|
||||
if (workspace.connectedToQLab3)
|
||||
return false;
|
||||
|
||||
bool panicking = (bool)propertyForKey(QOSCKey.IsPanicking);
|
||||
|
||||
if (panicking)
|
||||
return true;
|
||||
foreach (var cue in cues)
|
||||
{
|
||||
if (cue.IsPanicking)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return getBoolForKey(QOSCKey.IsPanicking);
|
||||
}
|
||||
}
|
||||
public bool IsGroup
|
||||
@@ -571,6 +527,23 @@ namespace QControlKit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public float actionElapsed
|
||||
{
|
||||
get
|
||||
{
|
||||
object secs = propertyForKey(QOSCKey.ActionElapsed);
|
||||
if (secs != null)
|
||||
{
|
||||
return float.Parse(secs.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public QTriggerAction secondTriggerAction
|
||||
{
|
||||
get
|
||||
@@ -827,6 +800,32 @@ namespace QControlKit
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool getBoolForKey(string key)
|
||||
{
|
||||
if (propertyForKey(key) != null)
|
||||
{
|
||||
object prop = propertyForKey(key);
|
||||
//Log.Warning($"{listName} IsBroken: {propertyForKey(QOSCKey.IsBroken).ToString()} {prop.GetType().ToString()}");
|
||||
if(prop.GetType() == typeof(Boolean)){
|
||||
return (bool)prop;
|
||||
}
|
||||
else if (prop.GetType() == typeof(JValue))
|
||||
{
|
||||
JValue value = (JValue)prop;
|
||||
return (bool)value.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public object propertyForKey(string key)
|
||||
{
|
||||
//TODO: implerment special key checks color, surfaceName, patchDescription,
|
||||
@@ -996,7 +995,10 @@ namespace QControlKit
|
||||
#region Event Handling
|
||||
void OnCuePropertiesUpdated(List<string> properties)
|
||||
{
|
||||
Log.Debug($"[cue] <{nonEmptyName}> properties have been updated.");
|
||||
if(parentID != null && parentID != "")
|
||||
{
|
||||
workspace.fetchBasicPropertiesForCue(workspace.cueWithID(parentID));
|
||||
}
|
||||
CuePropertiesUpdated?.Invoke(this, new QCuePropertiesUpdatedArgs { properties = properties });
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -143,5 +143,21 @@ namespace QControlKit
|
||||
}
|
||||
}
|
||||
|
||||
override
|
||||
public string ToString()
|
||||
{
|
||||
string stringified = "";
|
||||
|
||||
stringified += $"\nAddress: {this.address}\nArguments: \n";
|
||||
|
||||
|
||||
for(int i = 0; i<this.arguments.Count; i++)
|
||||
{
|
||||
object argument = this.arguments[i];
|
||||
stringified += $"\targ {i + 1}: {argument.ToString()}\n";
|
||||
}
|
||||
return stringified;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace QControlKit
|
||||
|
||||
public void disconnect()
|
||||
{
|
||||
Log.Information($"[server] disconnect requested for server <{name}>");
|
||||
foreach (var workspace in workspaces)
|
||||
{
|
||||
if (workspace.connected)
|
||||
|
||||
+41
-14
@@ -3,8 +3,6 @@ using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
|
||||
using QControlKit.Events;
|
||||
using QControlKit.Constants;
|
||||
|
||||
@@ -217,15 +215,13 @@ namespace QControlKit
|
||||
return;
|
||||
stopReceivingUpdates();
|
||||
disconnectFromWorkspace();
|
||||
|
||||
connected = false;
|
||||
client.disconnect();
|
||||
|
||||
connected = false;
|
||||
}
|
||||
|
||||
public void temporarilyDisconnect()
|
||||
{
|
||||
//TODO
|
||||
//TODO what does this mean?
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -245,6 +241,7 @@ namespace QControlKit
|
||||
#region Workspace Methods
|
||||
public void disconnectFromWorkspace() { client.sendMessage($"{workspacePrefix}/disconnect"); }
|
||||
public void startReceivingUpdates() { client.sendMessage($"{workspacePrefix}/updates", 1); }
|
||||
public void getPropertyForActive(string prop) { client.sendMessage($"{workspacePrefix}/cue/active/{prop}"); }
|
||||
public void stopReceivingUpdates() { client.sendMessage($"{workspacePrefix}/updates", 0); }
|
||||
public void enableAlwaysReply() { client.sendMessage($"{workspacePrefix}/alwaysReply", 1); }
|
||||
public void disableAlwaysReply() { client.sendMessage($"{workspacePrefix}/alwaysReply", 0); }
|
||||
@@ -320,18 +317,48 @@ namespace QControlKit
|
||||
|
||||
public void fetchDefaultPropertiesForCue(QCue cue)
|
||||
{
|
||||
List<string> keys = new List<string> { QOSCKey.UID, QOSCKey.Number, QOSCKey.Name,
|
||||
QOSCKey.ListName, QOSCKey.Type, QOSCKey.ColorName,
|
||||
QOSCKey.Flagged, QOSCKey.Armed, QOSCKey.Notes };
|
||||
List<string> keys = new List<string> {
|
||||
QOSCKey.UID,
|
||||
QOSCKey.Number,
|
||||
QOSCKey.Name,
|
||||
QOSCKey.ListName,
|
||||
QOSCKey.Type,
|
||||
QOSCKey.ColorName,
|
||||
QOSCKey.Flagged,
|
||||
QOSCKey.Armed,
|
||||
QOSCKey.IsRunning,
|
||||
QOSCKey.IsBroken,
|
||||
QOSCKey.Parent,
|
||||
QOSCKey.Notes
|
||||
};
|
||||
|
||||
fetchPropertiesForCue(cue, keys.ToArray(), false);
|
||||
}
|
||||
|
||||
public void fetchBasicPropertiesForCue(QCue cue)
|
||||
{
|
||||
List<string> keys = new List<string> { QOSCKey.Name, QOSCKey.ListName, 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, QOSCKey.Notes };
|
||||
List<string> keys = new List<string> {
|
||||
QOSCKey.Name,
|
||||
QOSCKey.ListName,
|
||||
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,
|
||||
QOSCKey.IsBroken,
|
||||
QOSCKey.IsRunning,
|
||||
QOSCKey.IsLoaded,
|
||||
QOSCKey.Notes
|
||||
};
|
||||
|
||||
if (cue.IsGroup)
|
||||
keys.Add(QOSCKey.Children);
|
||||
fetchPropertiesForCue(cue, keys.ToArray(), false);
|
||||
@@ -502,7 +529,7 @@ namespace QControlKit
|
||||
cueList.setProperty(args.cueID, QOSCKey.PlaybackPositionId, false);
|
||||
}
|
||||
|
||||
Log.Debug($"[workspace] cue list <{args.cueListID}> playback position changed to <{args.cueID}>");
|
||||
Log.Information($"[workspace] cue list <{args.cueListID}> playback position changed to <{args.cueID}>");
|
||||
CueListChangedPlaybackPosition?.Invoke(this, new QCueListChangedPlaybackPositionArgs { cueListID = args.cueListID, cueID = args.cueID });
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using Serilog;
|
||||
using SuperSimpleTcp;
|
||||
|
||||
namespace SharpOSC
|
||||
{
|
||||
@@ -38,32 +36,64 @@ namespace SharpOSC
|
||||
private Thread sendThread;
|
||||
|
||||
string _address;
|
||||
TcpClient client;
|
||||
|
||||
byte END = 0xc0;
|
||||
byte ESC = 0xdb;
|
||||
byte ESC_END = 0xDC;
|
||||
byte ESC_ESC = 0xDD;
|
||||
|
||||
SimpleTcpClient simpleClient;
|
||||
|
||||
|
||||
public TCPClient(string address, int port)
|
||||
{
|
||||
_port = port;
|
||||
_address = address;
|
||||
|
||||
simpleClient = new SimpleTcpClient(address, port);
|
||||
simpleClient.Events.Connected += Connected;
|
||||
simpleClient.Events.Disconnected += Disconnected;
|
||||
simpleClient.Events.DataReceived += DataReceived;
|
||||
simpleClient.Logger = Log.Debug;
|
||||
}
|
||||
|
||||
public static string ByteArrayToString(byte[] ba)
|
||||
{
|
||||
return BitConverter.ToString(ba).Replace("-", "");
|
||||
}
|
||||
|
||||
private void DataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
List<byte[]> messages = SlipDecode(e.Data);
|
||||
for(int i = 0; i < messages.Count; i++)
|
||||
{
|
||||
OscPacket packet = OscPacket.GetPacket(messages[i]);
|
||||
OscMessage responseMessage = (OscMessage)packet;
|
||||
OnMessageReceived(responseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void Disconnected(object sender, ConnectionEventArgs e)
|
||||
{
|
||||
Log.Debug("[tcpClient] connection to " + e.IpPort + " was disconnected");
|
||||
}
|
||||
|
||||
private void Connected(object sender, ConnectionEventArgs e)
|
||||
{
|
||||
Log.Debug($"[tcpclient] connected to <{e.IpPort}>");
|
||||
}
|
||||
|
||||
public bool Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
client = new TcpClient(Address, Port);
|
||||
receivingThread = new Thread(ReceiveLoop);
|
||||
receivingThread.Start();
|
||||
simpleClient.Connect();
|
||||
|
||||
sendThread = new Thread(SendLoop);
|
||||
sendThread.IsBackground = true;
|
||||
sendThread.Start();
|
||||
|
||||
Log.Debug($"[tcpclient] connected to <{Address}:{Port}>");
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -71,7 +101,6 @@ namespace SharpOSC
|
||||
Log.Error(e.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void QueueForSending(OscPacket packet)
|
||||
@@ -81,7 +110,7 @@ namespace SharpOSC
|
||||
|
||||
private void SendLoop()
|
||||
{
|
||||
while (client != null && client.Connected)
|
||||
while (simpleClient != null && simpleClient.IsConnected)
|
||||
{
|
||||
if(SendQueue.Count > 0)
|
||||
{
|
||||
@@ -94,8 +123,7 @@ namespace SharpOSC
|
||||
public void Send(byte[] message)
|
||||
{
|
||||
byte[] slipData = SlipEncode(message);
|
||||
NetworkStream netStream = client.GetStream();
|
||||
netStream.Write(slipData.ToArray(), 0, slipData.ToArray().Length);
|
||||
simpleClient.Send(slipData);
|
||||
}
|
||||
|
||||
public void Send(OscPacket packet)
|
||||
@@ -108,57 +136,10 @@ namespace SharpOSC
|
||||
{
|
||||
get
|
||||
{
|
||||
if (client == null)
|
||||
if (simpleClient == null)
|
||||
return false;
|
||||
else
|
||||
return client.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReceiveLoop()
|
||||
{
|
||||
while (client != null && client.Connected)
|
||||
{
|
||||
Receive();
|
||||
}
|
||||
//Log.Debug("[tcpclient] - ReceiveLoop has exited");
|
||||
}
|
||||
|
||||
public void Receive()
|
||||
{
|
||||
Random random = new Random();
|
||||
int num = random.Next(1000);
|
||||
try
|
||||
{
|
||||
NetworkStream netStream = client.GetStream();
|
||||
netStream.ReadTimeout = 250;
|
||||
List<byte> responseData = new List<byte>();
|
||||
if (netStream.CanRead)
|
||||
{
|
||||
//var watch = System.Diagnostics.Stopwatch.StartNew();
|
||||
byte[] buffer = new byte[256];
|
||||
|
||||
int bytesRead = 0;
|
||||
int reads = 0;
|
||||
do
|
||||
{
|
||||
bytesRead = netStream.Read(buffer, 0, buffer.Length);
|
||||
responseData.AddRange(buffer);
|
||||
reads += 1;
|
||||
Thread.Sleep(1);
|
||||
//Log.Debug("Thread " + num + ": Bytes read: " + bytesRead + " - " + Encoding.UTF8.GetString(buffer));
|
||||
} while (netStream.DataAvailable);
|
||||
|
||||
//Console.WriteLine("Raw TCP In: " + System.Text.Encoding.UTF8.GetString(responseData.ToArray()));
|
||||
OscPacket packet = OscPacket.GetPacket(responseData.Skip(1).ToArray());
|
||||
OscMessage responseMessage = (OscMessage)packet;
|
||||
//watch.Stop();
|
||||
//Console.WriteLine($"TCPCLient - message receive took {watch.ElapsedMilliseconds}ms and {reads} reads");
|
||||
OnMessageReceived(responseMessage);
|
||||
}
|
||||
} catch(Exception e)
|
||||
{
|
||||
//Console.WriteLine("TCPSENDER - Receive Exception: " + e.ToString());
|
||||
return simpleClient.IsConnected;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,15 +171,35 @@ namespace SharpOSC
|
||||
return slipData.ToArray();
|
||||
}
|
||||
|
||||
public List<byte[]> SlipDecode(byte[] data)
|
||||
{
|
||||
List<byte[]> messages = new List<byte[]>();
|
||||
|
||||
List<byte> buffer = new List<byte>();
|
||||
for(int i = 0; i < data.Length; i++)
|
||||
{
|
||||
if(data[i] == END && buffer.Count > 0)
|
||||
{
|
||||
messages.Add(buffer.ToArray());
|
||||
buffer.Clear();
|
||||
}
|
||||
else if (data[i] != END)
|
||||
{
|
||||
buffer.Add(data[i]);
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (client != null)
|
||||
Log.Information("[tcpclient] Close() was requested");
|
||||
if (simpleClient != null)
|
||||
{
|
||||
if (client.Connected)
|
||||
if (simpleClient.IsConnected)
|
||||
{
|
||||
Log.Debug($"[tcpClient] closing connection to {Address}");
|
||||
client.GetStream().Close();
|
||||
client.Close();
|
||||
Log.Information($"[tcpClient] closing connection to {Address}");
|
||||
simpleClient.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -101,6 +101,9 @@
|
||||
<Name>QControlKitXamDemo</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="Assets\materialdesignicons-webfont.ttf" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace QControlKitXamDemo.iOS
|
||||
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
||||
{
|
||||
global::Xamarin.Forms.Forms.Init();
|
||||
Log.Logger = new LoggerConfiguration().WriteTo.NSLog().MinimumLevel.Debug().CreateLogger();
|
||||
Log.Logger = new LoggerConfiguration().WriteTo.NSLog().MinimumLevel.Information().CreateLogger();
|
||||
LoadApplication(new App());
|
||||
|
||||
return base.FinishedLaunching(app, options);
|
||||
|
||||
@@ -40,5 +40,9 @@
|
||||
</array>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Scan network for QLab instances</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>materialdesignicons-webfont.ttf</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -133,6 +133,9 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2337" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
||||
<PackageReference Include="SuperSimpleTcp">
|
||||
<Version>2.6.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
@@ -141,4 +144,7 @@
|
||||
<Name>QControlKitXamDemo</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BundleResource Include="Resources\materialdesignicons-webfont.ttf" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
@@ -6,6 +6,52 @@
|
||||
mc:Ignorable="d"
|
||||
x:Class="QControlKitXamDemo.App">
|
||||
<Application.Resources>
|
||||
<OnPlatform x:Key="MaterialFontFamily" x:TypeArguments="x:String">
|
||||
<On Platform="iOS">Material Design Icons</On>
|
||||
<On Platform="Android">materialdesignicons-webfont.ttf#Material Design Icons</On>
|
||||
</OnPlatform>
|
||||
|
||||
<Style TargetType="Label"
|
||||
Class="PrimaryText"
|
||||
ApplyToDerivedTypes="true">
|
||||
<Setter Property="TextColor"
|
||||
Value="{DynamicResource PrimaryTextColor}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button"
|
||||
Class="PrimaryButton"
|
||||
ApplyToDerivedTypes="true">
|
||||
<Setter Property="TextColor"
|
||||
Value="{DynamicResource PrimaryTextColor}" />
|
||||
</Style>
|
||||
|
||||
<x:Double x:Key="SmallPadding">4</x:Double>
|
||||
<x:Double x:Key="MediumPadding">8</x:Double>
|
||||
<x:Double x:Key="StandardPadding">16</x:Double>
|
||||
|
||||
<Thickness x:Key="SmallTopThickness" Top="{StaticResource SmallPadding}" />
|
||||
|
||||
<Thickness x:Key="MediumTopThickness" Top="{StaticResource MediumPadding}" />
|
||||
|
||||
<Thickness x:Key="StandardTopThickness" Top="{StaticResource StandardPadding}" />
|
||||
|
||||
<Thickness x:Key="SmallThickness"
|
||||
Bottom="{StaticResource SmallPadding}"
|
||||
Left="{StaticResource SmallPadding}"
|
||||
Right="{StaticResource SmallPadding}"
|
||||
Top="{StaticResource SmallPadding}" />
|
||||
|
||||
<Thickness x:Key="MediumThickness"
|
||||
Bottom="{StaticResource MediumPadding}"
|
||||
Left="{StaticResource MediumPadding}"
|
||||
Right="{StaticResource MediumPadding}"
|
||||
Top="{StaticResource MediumPadding}" />
|
||||
|
||||
<Thickness x:Key="StandardThickness"
|
||||
Bottom="{StaticResource StandardPadding}"
|
||||
Left="{StaticResource StandardPadding}"
|
||||
Right="{StaticResource StandardPadding}"
|
||||
Top="{StaticResource StandardPadding}" />
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -9,6 +9,8 @@ namespace QControlKitXamDemo
|
||||
InitializeComponent();
|
||||
|
||||
MainPage = new NavigationPage(new MainPage());
|
||||
Current.RequestedThemeChanged += Current_RequestedThemeChanged;
|
||||
SetAppResources();
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
@@ -22,5 +24,80 @@ namespace QControlKitXamDemo
|
||||
protected override void OnResume()
|
||||
{
|
||||
}
|
||||
|
||||
private void Current_RequestedThemeChanged(object sender, AppThemeChangedEventArgs e)
|
||||
{
|
||||
SetAppResources();
|
||||
}
|
||||
|
||||
private void SetAppResources()
|
||||
{
|
||||
|
||||
switch (Current.RequestedTheme)
|
||||
{
|
||||
case OSAppTheme.Light:
|
||||
SetLightResources();
|
||||
break;
|
||||
case OSAppTheme.Dark:
|
||||
SetDarkResources();
|
||||
break;
|
||||
case OSAppTheme.Unspecified:
|
||||
//Default to Light
|
||||
SetLightResources();
|
||||
break;
|
||||
default:
|
||||
SetLightResources();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDarkResources()
|
||||
{
|
||||
|
||||
Resources["NavigationBarColor"] = Color.FromHex("#3f6dab");
|
||||
Resources["PageBackgroundColor"] = Color.FromHex("#525252");
|
||||
|
||||
|
||||
Resources["PrimaryColor"] = Color.WhiteSmoke;
|
||||
Resources["SecondaryColor"] = Color.White;
|
||||
Resources["PrimaryTextColor"] = Color.LightGray;
|
||||
Resources["SecondaryTextColor"] = Color.White;
|
||||
Resources["TertiaryTextColor"] = Color.Gray;
|
||||
Resources["TransparentColor"] = Color.Transparent;
|
||||
|
||||
Resources["IconTextColor"] = Color.WhiteSmoke;
|
||||
|
||||
Resources["ListViewBackgroundColor"] = Color.FromHex("#363636");
|
||||
|
||||
Resources["ServerCellBackgroundColor"] = Color.FromHex("#363636");
|
||||
Resources["WorkspaceCellBackgroundColor"] = Color.FromHex("#737373");
|
||||
|
||||
Resources["SelectedCueCellBackgroundColor"] = Color.FromHex("#737373");
|
||||
|
||||
}
|
||||
|
||||
private void SetLightResources()
|
||||
{
|
||||
Resources["NavigationBarColor"] = Color.FromHex("#3089ff");
|
||||
Resources["PageBackgroundColor"] = Color.LightGray;
|
||||
|
||||
|
||||
Resources["PrimaryColor"] = Color.WhiteSmoke;
|
||||
Resources["SecondaryColor"] = Color.White;
|
||||
Resources["PrimaryTextColor"] = Color.Black;
|
||||
Resources["SecondaryTextColor"] = Color.White;
|
||||
Resources["TertiaryTextColor"] = Color.Gray;
|
||||
Resources["TransparentColor"] = Color.Transparent;
|
||||
|
||||
Resources["IconTextColor"] = Color.Black;
|
||||
|
||||
Resources["ListViewBackgroundColor"] = Color.White;
|
||||
|
||||
Resources["ServerCellBackgroundColor"] = Color.White;
|
||||
Resources["WorkspaceCellBackgroundColor"] = Color.FromHex("#D8D8D8");
|
||||
|
||||
Resources["SelectedCueCellBackgroundColor"] = Color.FromHex("#D8D8D8");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace QControlKitXamDemo
|
||||
HorizontalOptions = LayoutOptions.StartAndExpand,
|
||||
VerticalTextAlignment = TextAlignment.Center,
|
||||
};
|
||||
cueLabel.SetBinding(Label.TextProperty, "name", BindingMode.OneWay);
|
||||
cueLabel.SetBinding(Label.TextProperty, "status", BindingMode.OneWay);
|
||||
var cueBackground = new Frame
|
||||
{
|
||||
BindingContext = qCueViewModel,
|
||||
|
||||
@@ -3,14 +3,121 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Class="QControlKitXamDemo.MainPage">
|
||||
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
|
||||
mc:Ignorable="d"
|
||||
x:Class="QControlKitXamDemo.MainPage"
|
||||
ios:Page.UseSafeArea="true"
|
||||
BackgroundColor="{DynamicResource PageBackgroundColor}">
|
||||
<ContentPage.Content>
|
||||
|
||||
<ListView x:Name="serverListView" GroupDisplayBinding="{Binding name}" IsGroupingEnabled="True">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextCell Text="{Binding name}"/>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
</ContentPage>
|
||||
<StackLayout>
|
||||
<Frame BackgroundColor="{DynamicResource PageBackgroundColor}"
|
||||
CornerRadius="0"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
HasShadow="False">
|
||||
<StackLayout Spacing="0" BackgroundColor="Transparent">
|
||||
<Label Margin="0,10,0,10"
|
||||
Padding="0"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"
|
||||
HorizontalTextAlignment="Center"
|
||||
FontSize="Title"
|
||||
Text="Discovered QLab Instances" />
|
||||
<!--Need to try and remove the group spacing caused by ios:GroupHeaderStyle-->
|
||||
<ListView x:Name="serverListView"
|
||||
ios:ListView.GroupHeaderStyle="Grouped"
|
||||
GroupDisplayBinding="{Binding name}"
|
||||
IsGroupingEnabled="True"
|
||||
d:IsGroupingEnabled="False"
|
||||
BackgroundColor="Transparent"
|
||||
SeparatorVisibility="None"
|
||||
ItemsSource="{Binding ServersGrouped}"
|
||||
Margin="0">
|
||||
<ListView.GroupHeaderTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<Frame BackgroundColor="{DynamicResource ServerCellBackgroundColor}"
|
||||
Margin="0,0,0,0"
|
||||
Padding="10,0,10,0"
|
||||
CornerRadius="0">
|
||||
<Grid BackgroundColor="Transparent" VerticalOptions="FillAndExpand"
|
||||
RowSpacing="0"
|
||||
ColumnSpacing="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0"
|
||||
Text="{Binding GroupName}"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"
|
||||
VerticalTextAlignment="Center"
|
||||
HorizontalTextAlignment="Start"
|
||||
StyleClass="PrimaryText"/>
|
||||
<Label Grid.Column="1"
|
||||
Text="{Binding version}"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"
|
||||
VerticalTextAlignment="Center"
|
||||
HorizontalTextAlignment="End"
|
||||
StyleClass="PrimaryText"/>
|
||||
</Grid>
|
||||
</Frame>
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.GroupHeaderTemplate>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<Frame BackgroundColor="{DynamicResource WorkspaceCellBackgroundColor}"
|
||||
BorderColor="Black"
|
||||
Margin="5,0,5,0"
|
||||
Padding="10,0,10,0"
|
||||
CornerRadius="0"
|
||||
HasShadow="False">
|
||||
<Grid Margin="0"
|
||||
BackgroundColor="Transparent"
|
||||
RowSpacing="0"
|
||||
ColumnSpacing="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Label Grid.Column="0" Text="{Binding Name}"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"
|
||||
VerticalTextAlignment="Center"
|
||||
HorizontalTextAlignment="Start"
|
||||
StyleClass="PrimaryText"/>
|
||||
|
||||
<Label Grid.Column="1" Text="󱆄"
|
||||
FontFamily="{StaticResource MaterialFontFamily}"
|
||||
FontSize="Large"
|
||||
IsVisible="{Binding HasPasscode}"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"
|
||||
VerticalTextAlignment="Center"
|
||||
HorizontalTextAlignment="End"
|
||||
StyleClass="PrimaryText"/>
|
||||
</Grid>
|
||||
</Frame>
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
</Frame>
|
||||
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
||||
@@ -2,150 +2,71 @@
|
||||
using System.ComponentModel;
|
||||
using Xamarin.Forms;
|
||||
using QControlKit;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QControlKit.Events;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using QControlKitXamDemo.ViewModels;
|
||||
using Acr.UserDialogs;
|
||||
|
||||
namespace QControlKitXamDemo
|
||||
{
|
||||
// Learn more about making custom code visible in the Xamarin.Forms previewer
|
||||
// by visiting https://aka.ms/xamarinforms-previewer
|
||||
[DesignTimeVisible(false)]
|
||||
public partial class MainPage : ContentPage
|
||||
{
|
||||
public ObservableCollection<ServerGroup> servers = new ObservableCollection<ServerGroup>();
|
||||
private QBrowserViewModel qBrowserViewModel;
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
serverListView.ItemsSource = servers;
|
||||
QBrowser browser = new QBrowser();
|
||||
browser.ServerLost += Browser_ServerLost;
|
||||
browser.ServerFound += Browser_ServerFound;
|
||||
serverListView.ItemSelected += ServerListView_ItemSelected;
|
||||
|
||||
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
browser.ProbeForQLabInstances();
|
||||
});
|
||||
|
||||
});
|
||||
return true;
|
||||
});
|
||||
qBrowserViewModel = new QBrowserViewModel(new QBrowser());
|
||||
serverListView.BindingContext = qBrowserViewModel;
|
||||
serverListView.ItemSelected += QWorkspaceSelected;
|
||||
|
||||
}
|
||||
|
||||
private void Browser_ServerFound(object source, QServerFoundArgs args)
|
||||
async void QWorkspaceSelected(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
List<ServerGroup> serverGroups = servers.ToList();
|
||||
|
||||
bool serverFound = false;
|
||||
foreach (var serverGroup in serverGroups)
|
||||
if (e.SelectedItem == null)
|
||||
{
|
||||
|
||||
if (serverGroup.host == args.server.host)
|
||||
{
|
||||
serverFound = true;
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!serverFound)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
ServerGroup serverGroup = new ServerGroup(args.server.name);
|
||||
args.server.WorkspaceAdded += Server_WorkspaceAdded;
|
||||
args.server.WorkspaceRemoved += Server_WorkspaceRemoved;
|
||||
serverGroup.host = args.server.host;
|
||||
servers.Add(serverGroup);
|
||||
});
|
||||
}
|
||||
}
|
||||
qBrowserViewModel.autoUpdate = false;
|
||||
QWorkspace selectedWorkspace = (e.SelectedItem as QWorkspaceViewModel).workspace;
|
||||
Log.Debug($"[demo] workspace: {selectedWorkspace.nameWithoutPathExtension} has been selected");
|
||||
((ListView)sender).SelectedItem = null;
|
||||
|
||||
private void Server_WorkspaceRemoved(object source, QServerWorkspaceChangedArgs args)
|
||||
{
|
||||
foreach (var server in servers)
|
||||
if (selectedWorkspace.hasPasscode)
|
||||
{
|
||||
if (server.host == args.server.host)
|
||||
UserDialogs.Instance.Prompt(new PromptConfig
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
InputType = InputType.Number,
|
||||
MaxLength = 4,
|
||||
Title = "Workspace Requires Passcode",
|
||||
OkText = "Connect",
|
||||
IsCancellable = true,
|
||||
OnTextChanged = args =>
|
||||
{
|
||||
server.Remove(args.workspace);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Server_WorkspaceAdded(object source, QServerWorkspaceChangedArgs args)
|
||||
{
|
||||
foreach(var server in servers)
|
||||
{
|
||||
if(server.host == args.server.host)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
args.IsValid = args.Value != null && !args.Value.Equals("") && args.Value.Length == 4;
|
||||
},
|
||||
OnAction = async (resp) =>
|
||||
{
|
||||
server.Add(args.workspace);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_ServerLost(object source, QServerLostArgs args)
|
||||
{
|
||||
|
||||
List<ServerGroup> serverGroups = servers.ToList();
|
||||
foreach(var serverGroup in serverGroups)
|
||||
{
|
||||
if(serverGroup.host == args.server.host)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
servers.Remove(serverGroup);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void ServerListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
if(e.SelectedItem != null)
|
||||
{
|
||||
QWorkspace selectedWorkspace = e.SelectedItem as QWorkspace;
|
||||
Log.Debug($"[demo] Workspace <{selectedWorkspace.nameWithoutPathExtension}> has been selected");
|
||||
if (selectedWorkspace.hasPasscode)
|
||||
{
|
||||
string passcode = await DisplayPromptAsync("Workspace has passcode", "Enter Passcode", maxLength: 4, keyboard: Keyboard.Numeric);
|
||||
if (passcode != null)
|
||||
{
|
||||
|
||||
await Navigation.PushAsync(new CueListPage(selectedWorkspace, passcode));
|
||||
if (resp.Ok)
|
||||
{
|
||||
await Navigation.PushAsync(new CueListPage(selectedWorkspace, resp.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await Navigation.PushAsync(new CueListPage(selectedWorkspace));
|
||||
|
||||
}
|
||||
serverListView.SelectedItem = null;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
await Navigation.PushAsync(new CueListPage(selectedWorkspace));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ServerGroup : ObservableCollection<QWorkspace>{
|
||||
public string name { get; set; }
|
||||
public string host { get; set; }
|
||||
public ServerGroup(string name)
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
this.name = name;
|
||||
base.OnAppearing();
|
||||
qBrowserViewModel.autoUpdate = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2337" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
||||
<PackageReference Include="Acr.UserDialogs" Version="7.2.0.562" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -28,4 +29,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\QControlKit\QControlKit.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Acr.UserDialogs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Xamarin.Forms;
|
||||
|
||||
using QControlKit;
|
||||
using QControlKit.Events;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
|
||||
namespace QControlKitXamDemo.ViewModels
|
||||
{
|
||||
public class QBrowserViewModel : INotifyPropertyChanged
|
||||
{
|
||||
QBrowser browser;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
public bool autoUpdate = false;
|
||||
public ObservableCollection<QServerViewModel> ServersGrouped { get; set; }
|
||||
public QBrowserViewModel(QBrowser browser)
|
||||
{
|
||||
this.browser = browser;
|
||||
ServersGrouped = new ObservableCollection<QServerViewModel>();
|
||||
|
||||
this.browser.ServerFound += OnServerFound;
|
||||
this.browser.ServerLost += OnServerLost;
|
||||
Device.StartTimer(TimeSpan.FromSeconds(4), () =>
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (autoUpdate)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Log.Debug("[QBrowserViewModel] QBrowser probe triggered");
|
||||
this.browser.ProbeForQLabInstances();
|
||||
});
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void OnServerFound(object source, QServerFoundArgs args)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Log.Debug($"[QBrowserViewModel] adding server: {args.server.description}");
|
||||
ServersGrouped.Add(new QServerViewModel(args.server));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void OnServerLost(object source, QServerLostArgs args)
|
||||
{
|
||||
QServerViewModel serverToRemove = null;
|
||||
|
||||
foreach (QServerViewModel serverGroup in ServersGrouped)
|
||||
{
|
||||
if (serverGroup.host.Equals(args.server.host))
|
||||
{
|
||||
serverToRemove = serverGroup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serverToRemove != null)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Log.Debug($"[QBrowserViewModel] removing server: {args.server.description}");
|
||||
ServersGrouped.Remove(serverToRemove);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OnPropertyChanged([CallerMemberName] string name = "")
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
}
|
||||
|
||||
public void InitiateScan()
|
||||
{
|
||||
if (!autoUpdate)
|
||||
{
|
||||
Log.Debug("[QBrowserViewModel] Manual Scan Initiated");
|
||||
browser.ProbeForQLabInstances();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Xamarin.Forms;
|
||||
using Serilog;
|
||||
using QControlKit.Events;
|
||||
using QControlKit.Constants;
|
||||
using System;
|
||||
|
||||
namespace QControlKitXamDemo.ViewModels
|
||||
{
|
||||
@@ -26,13 +27,19 @@ namespace QControlKitXamDemo.ViewModels
|
||||
{
|
||||
foreach(var property in args.properties)
|
||||
{
|
||||
Log.Debug($"[cueviewmodel] property <{property}> has been updated.");
|
||||
Log.Debug($"[cueviewmodel] property <{property}> has been updated for cue {name}.");
|
||||
if (property.Equals(QOSCKey.Name) || property.Equals(QOSCKey.ListName))
|
||||
{
|
||||
OnPropertyChanged("name");
|
||||
}else if (property.Equals(QOSCKey.ColorName))
|
||||
{
|
||||
OnPropertyChanged("color");
|
||||
}else if (property.Equals(QOSCKey.ActionElapsed))
|
||||
{
|
||||
OnPropertyChanged("actionElapsed");
|
||||
}else if (property.Equals(QOSCKey.IsBroken) || property.Equals(QOSCKey.IsRunning))
|
||||
{
|
||||
OnPropertyChanged("status");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,6 +91,34 @@ namespace QControlKitXamDemo.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public string actionElapsed
|
||||
{
|
||||
get
|
||||
{
|
||||
TimeSpan time = TimeSpan.FromSeconds(cue.actionElapsed);
|
||||
|
||||
return time.ToString(@"mm\:ss\:ff");
|
||||
}
|
||||
}
|
||||
|
||||
public string status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cue.IsRunning)
|
||||
{
|
||||
return "running";
|
||||
}else if (cue.IsBroken)
|
||||
{
|
||||
return "broken";
|
||||
}
|
||||
else
|
||||
{
|
||||
return " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSelected {
|
||||
get
|
||||
{
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Xamarin.Forms;
|
||||
|
||||
using QControlKit;
|
||||
using QControlKit.Events;
|
||||
|
||||
namespace QControlKitXamDemo.ViewModels
|
||||
{
|
||||
public class QServerViewModel : ObservableCollection<QWorkspaceViewModel>, INotifyPropertyChanged
|
||||
{
|
||||
QServer server;
|
||||
public new event PropertyChangedEventHandler PropertyChanged;
|
||||
public string name
|
||||
{
|
||||
get => server.name;
|
||||
set => server.name = value;
|
||||
}
|
||||
|
||||
public string host => server.host;
|
||||
|
||||
public string version => server.version == null ? "" : $"v{server.version}";
|
||||
|
||||
public string GroupName => $"{name} ({host})";
|
||||
|
||||
public QServerViewModel(QServer server)
|
||||
{
|
||||
this.server = server;
|
||||
name = this.server.name;
|
||||
foreach (QWorkspace workspace in this.server.workspaces)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Add(new QWorkspaceViewModel(workspace));
|
||||
});
|
||||
}
|
||||
this.server.WorkspaceAdded += Server_WorkspaceAdded;
|
||||
this.server.WorkspaceRemoved += Server_WorkspaceRemoved;
|
||||
this.server.ServerUpdated += OnServerUpdated;
|
||||
}
|
||||
|
||||
private void Server_WorkspaceRemoved(object source, QServerWorkspaceChangedArgs args)
|
||||
{
|
||||
QWorkspaceViewModel workspaceToRemove = null;
|
||||
|
||||
foreach (QWorkspaceViewModel workspaceViewModel in this)
|
||||
{
|
||||
if (workspaceViewModel.workspace.uniqueID == args.workspace.uniqueID)
|
||||
{
|
||||
workspaceToRemove = workspaceViewModel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (workspaceToRemove != null)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Remove(workspaceToRemove);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void Server_WorkspaceAdded(object source, QServerWorkspaceChangedArgs args)
|
||||
{
|
||||
QWorkspaceViewModel workspaceViewModel = new QWorkspaceViewModel(args.workspace);
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Add(workspaceViewModel);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void OnServerUpdated(object source, QServerUpdatedArgs args)
|
||||
{
|
||||
OnPropertyChanged("GroupName");
|
||||
OnPropertyChanged("version");
|
||||
}
|
||||
|
||||
void OnPropertyChanged([CallerMemberName] string name = "")
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using QControlKit;
|
||||
|
||||
namespace QControlKitXamDemo.ViewModels
|
||||
{
|
||||
public class QWorkspaceViewModel
|
||||
{
|
||||
public QWorkspace workspace;
|
||||
public QWorkspaceViewModel(QWorkspace workspace)
|
||||
{
|
||||
this.workspace = workspace;
|
||||
}
|
||||
|
||||
public string Name => workspace.name;
|
||||
|
||||
public bool HasPasscode => workspace.hasPasscode;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user