Skip to content

Commit

Permalink
Rewrite osc query models
Browse files Browse the repository at this point in the history
  • Loading branch information
LucHeart committed May 16, 2024
1 parent 7651588 commit 82614da
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 88 deletions.
51 changes: 51 additions & 0 deletions ShockOsc/OscQueryLibrary/HostInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Net;
using System.Text.Json.Serialization;
using OpenShock.ShockOsc.Utils;

// ReSharper disable InconsistentNaming

namespace OpenShock.ShockOsc.OscQueryLibrary;

public sealed class HostInfo
{
[JsonPropertyName("NAME")]
public required string Name { get; set; }

[JsonPropertyName("OSC_IP")]
[JsonConverter(typeof(JsonIPAddressConverter))]
public required IPAddress OscIp { get; set; }

[JsonPropertyName("OSC_PORT")]
public required ushort OscPort { get; set; }

[JsonPropertyName("OSC_TRANSPORT")]
[JsonConverter(typeof(JsonStringEnumConverter<OscTransportType>))]
public required OscTransportType OscTransport { get; set; }

[JsonPropertyName("EXTENSIONS")]
public required ExtensionsNode Extensions { get; set; }

public enum OscTransportType
{
TCP,
UDP
}

public sealed class ExtensionsNode
{
[JsonPropertyName("ACCESS")]
public required bool Access { get; set; }

[JsonPropertyName("CLIPMODE")]
public required bool ClipMode { get; set; }

[JsonPropertyName("RANGE")]
public required bool Range { get; set; }

[JsonPropertyName("TYPE")]
public required bool Type { get; set; }

[JsonPropertyName("VALUE")]
public required bool Value { get; set; }
}
}
22 changes: 22 additions & 0 deletions ShockOsc/OscQueryLibrary/Node.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;

namespace OpenShock.ShockOsc.OscQueryLibrary;

// technically every class in the JSON is this "Node" class but that's gross
public class Node
{
[JsonPropertyName("DESCRIPTION")]
public string? Description { get; set; }

[JsonPropertyName("FULL_PATH")]
public required string FullPath { get; set; }

[JsonPropertyName("ACCESS")]
public required int Access { get; set; }
}

public class Node<T> : Node
{
[JsonPropertyName("CONTENTS")]
public T? Contents { get; set; }
}
81 changes: 20 additions & 61 deletions ShockOsc/OscQueryLibrary/OscQueryModels.cs
Original file line number Diff line number Diff line change
@@ -1,73 +1,32 @@
using System.Text.Json.Serialization;

namespace OpenShock.ShockOsc.OscQueryLibrary;

public class OscQueryModels
public sealed class RootNode : Node<RootNode.RootContents>
{
public class RootNode
{
public SubNode? CONTENTS { get; set; }
}

public class SubNode
public sealed class RootContents
{
public Node input { get; set; }
public TrackingRootNode tracking { get; set; }
public Node chatbox { get; set; }
public AvatarRootNode? avatar { get; set; }
[JsonPropertyName("avatar")] public Node<AvatarContents>? Avatar { get; set; }
}
}

public class TrackingRootNode
{
public string FULL_PATH { get; set; }
public int ACCESS { get; set; }
public TrackingNode CONTENTS { get; set; }
}
public sealed class AvatarContents
{
[JsonPropertyName("change")] public required OscParameterNodeEnd<string> Change { get; set; }

public class TrackingNode
{
public Node trackers { get; set; }
public Node eye { get; set; }
public Node vrsystem { get; set; }
}
[JsonPropertyName("parameters")] public Node<IDictionary<string, OscParameterNode>>? Parameters { get; set; }
}

public class AvatarRootNode
{
public string FULL_PATH { get; set; }
public int ACCESS { get; set; }
public AvatarNode CONTENTS { get; set; }
}
public sealed class OscParameterNode : Node<IDictionary<string, OscParameterNode>>
{
[JsonPropertyName("TYPE")] public string? Type { get; set; }

public class AvatarNode
{
public Node change { get; set; }
public Node? parameters { get; set; }
}
[JsonPropertyName("VALUE")] public IEnumerable<object>? Value { get; set; }
}

// technically every class in the JSON is this "Node" class but that's gross
public class Node
{
public string? DESCRIPTION { get; set; }
public string FULL_PATH { get; set; }
public int ACCESS { get; set; }
public Dictionary<string, Node>? CONTENTS { get; set; }
public string? TYPE { get; set; }
public List<object>? VALUE { get; set; }
}
public sealed class OscParameterNodeEnd<T> : Node
{
[JsonPropertyName("TYPE")] public required string Type { get; set; }

public class HostInfo
{
public string NAME { get; set; }
public string OSC_IP { get; set; }
public int OSC_PORT { get; set; }
public string OSC_TRANSPORT { get; set; }
public Extensions EXTENSIONS { get; set; }
}

public class Extensions
{
public bool ACCESS { get; set; }
public bool CLIPMODE { get; set; }
public bool RANGE { get; set; }
public bool TYPE { get; set; }
public bool VALUE { get; set; }
}
[JsonPropertyName("VALUE")] public required IEnumerable<T> Value { get; set; }
}
57 changes: 30 additions & 27 deletions ShockOsc/OscQueryLibrary/OscQueryServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class OscQueryServer : IDisposable
private readonly MulticastService _multicastService;
private readonly ServiceDiscovery _serviceDiscovery;
private readonly string _serviceName;
private OscQueryModels.HostInfo? _hostInfo;
private HostInfo? _hostInfo;
private object? _queryData;

private readonly HashSet<string> FoundServices = new();
Expand Down Expand Up @@ -178,14 +178,14 @@ private async Task FoundNewVrcClient(IPAddress ipAddress, int port)
try
{
response = await Client.GetStringAsync(url);
var rootNode = JsonSerializer.Deserialize<OscQueryModels.HostInfo>(response);
if (rootNode?.OSC_PORT == null)
var rootNode = JsonSerializer.Deserialize<HostInfo>(response);
if (rootNode?.OscPort == null)
{
Logger.Error("OSCQueryHttpClient: Error no OSC port found");
return null;
}

return new IPEndPoint(IPAddress.Parse(rootNode.OSC_IP), (ushort)rootNode.OSC_PORT);
return new IPEndPoint(rootNode.OscIp, rootNode.OscPort);
}
catch (HttpRequestException ex)
{
Expand All @@ -212,50 +212,53 @@ private async Task FetchJsonFromVrc(IPAddress ipAddress, int port)
try
{
response = await Client.GetStringAsync(url);
var rootNode = JsonSerializer.Deserialize<OscQueryModels.RootNode>(response);
if (rootNode?.CONTENTS?.avatar?.CONTENTS?.parameters?.CONTENTS == null)

response = File.ReadAllText("C:/Users/Lucpe/Desktop/RootNode.json");

var rootNode = JsonSerializer.Deserialize<RootNode>(response);
if (rootNode?.Contents?.Avatar?.Contents?.Parameters?.Contents == null)
{
Logger.Debug("OSCQueryHttpClient: Error no parameters found");
return;
}

ParameterList.Clear();
foreach (var node in rootNode.CONTENTS.avatar.CONTENTS.parameters.CONTENTS!.Values)
foreach (var node in rootNode.Contents.Avatar.Contents.Parameters.Contents!)
{
RecursiveParameterLookup(node);
RecursiveParameterLookup(node.Value);
}

avatarId = rootNode.CONTENTS.avatar.CONTENTS.change.VALUE?[0]?.ToString() ?? string.Empty;
avatarId = rootNode.Contents.Avatar.Contents.Change.Value.FirstOrDefault() ?? string.Empty;
if(ParameterUpdate != null) await ParameterUpdate.Raise(ParameterList, avatarId);
}
catch (HttpRequestException ex)
{
_lastVrcHttpServer = null;
ParameterList.Clear();
ParameterUpdate?.Raise(ParameterList, avatarId);
Logger.Error("OSCQueryHttpClient: Error {ExMessage}", ex.Message);
Logger.Error(ex, "HTTP request failed");
}
catch (Exception ex)
{
Logger.Error("OSCQueryHttpClient: Error {ExMessage}\\n{Response}", ex.Message, response);
Logger.Error(ex, "Unexpected exception while receiving parameters via osc query");
}
finally
{
_fetchInProgress = false;
}
}

private void RecursiveParameterLookup(OscQueryModels.Node node)
private void RecursiveParameterLookup(OscParameterNode node)
{
if (node.CONTENTS == null)
if (node.Contents == null)
{
ParameterList.Add(node.FULL_PATH, node.VALUE?[0]);
ParameterList.Add(node.FullPath, node.Value?.FirstOrDefault());
return;
}

foreach (var subNode in node.CONTENTS.Values)
foreach (var subNode in node.Contents)
{
RecursiveParameterLookup(subNode);
RecursiveParameterLookup(subNode.Value);
}
}

Expand Down Expand Up @@ -304,19 +307,19 @@ private void SetupJsonObjects()
}
};

_hostInfo = new OscQueryModels.HostInfo
_hostInfo = new HostInfo
{
NAME = _serviceName,
OSC_PORT = ShockOscReceivePort,
OSC_IP = _ipAddress.ToString(),
OSC_TRANSPORT = "UDP",
EXTENSIONS = new OscQueryModels.Extensions
Name = _serviceName,
OscPort = ShockOscReceivePort,
OscIp = _ipAddress,
OscTransport = HostInfo.OscTransportType.UDP,
Extensions = new HostInfo.ExtensionsNode
{
ACCESS = true,
CLIPMODE = true,
RANGE = true,
TYPE = true,
VALUE = true
Access = true,
ClipMode = true,
Range = true,
Type = true,
Value = true
}
};
}
Expand Down
37 changes: 37 additions & 0 deletions ShockOsc/Utils/JsonIPAddressConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Buffers;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace OpenShock.ShockOsc.Utils;

/// <summary>
/// JSON converter for <see cref="IPAddress"/> that uses the <see cref="IPAddress.TryFormat(Span{char}, out int)"/> method.
/// </summary>
public class JsonIPAddressConverter : JsonConverter<IPAddress>
{
/// <inheritdoc/>
public override IPAddress Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.String) throw new JsonException($"Expected string but got {reader.TokenType}.");

Span<char> charData = stackalloc char[45];

var count = Encoding.UTF8.GetChars(reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan, charData);

return !IPAddress.TryParse(charData[..count], out var value)
? throw new JsonException($"Could not parse IPAddress from [{charData[..count].ToString()}].")
: value;
}

/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, IPAddress value, JsonSerializerOptions options)
{
var data = value.AddressFamily == AddressFamily.InterNetwork ? stackalloc char[15] : stackalloc char[45];
if (!value.TryFormat(data, out var charsWritten)) throw new JsonException($"IPAddress [{value}] could not be written to JSON.");
writer.WriteStringValue(data[..charsWritten]);
}

}

0 comments on commit 82614da

Please sign in to comment.