Send messages to the client

This commit is contained in:
Frank A. Krueger 2017-06-12 20:31:47 -07:00
parent 9ee271559f
commit 5daecdd445
5 changed files with 87 additions and 11 deletions

View File

@ -12,5 +12,5 @@ socket.addEventListener('open', function (event) {
// Listen for messages // Listen for messages
socket.addEventListener('message', function (event) { socket.addEventListener('message', function (event) {
console.log('Message from server', event.data); console.log('Message from server', JSON.parse (event.data));
}); });

View File

@ -1,13 +1,26 @@
using System; using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Ooui namespace Ooui
{ {
public class Message public class Message
{ {
[JsonProperty("t")]
[JsonConverter (typeof (MillisecondEpochConverter))]
public DateTime CreatedTime = DateTime.UtcNow; public DateTime CreatedTime = DateTime.UtcNow;
[JsonProperty("m")]
[JsonConverter (typeof (StringEnumConverter))]
public MessageType MessageType = MessageType.Nop; public MessageType MessageType = MessageType.Nop;
[JsonProperty("id")]
public string TargetId = ""; public string TargetId = "";
public string Member = "";
[JsonProperty("k")]
public string Key = "";
[JsonProperty("v")]
public string Value = ""; public string Value = "";
public void SetValue (object value) public void SetValue (object value)
@ -20,7 +33,7 @@ namespace Ooui
Value = EncodeString (s); Value = EncodeString (s);
break; break;
default: default:
Value = String.Format (System.Globalization.CultureInfo.InvariantCulture, "{0}", value); Value = JsonConvert.SerializeObject (value);
break; break;
} }
} }
@ -42,4 +55,20 @@ namespace Ooui
Set, Set,
Call, Call,
} }
class MillisecondEpochConverter : DateTimeConverterBase
{
private static readonly DateTime epoch = new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteRawValue (((DateTime)value - epoch).TotalMilliseconds.ToString (System.Globalization.CultureInfo.InvariantCulture));
}
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value == null) return null;
return epoch.AddMilliseconds ((double)reader.Value);
}
}
} }

View File

@ -14,6 +14,13 @@ namespace Ooui
readonly List<Message> messages = new List<Message> (); readonly List<Message> messages = new List<Message> ();
readonly List<Message> stateMessages = new List<Message> (); readonly List<Message> stateMessages = new List<Message> ();
public event Action<Message> MessageLogged;
public IEnumerable<Message> AllMessages =>
messages
.Concat (from c in children from m in c.AllMessages select m)
.OrderBy (x => x.CreatedTime);
public Node () public Node ()
{ {
Mapping = Mapping.Get (GetType ()); Mapping = Mapping.Get (GetType ());
@ -66,7 +73,7 @@ namespace Ooui
{ {
var old = stateMessages.FirstOrDefault ( var old = stateMessages.FirstOrDefault (
x => x.MessageType == MessageType.Set && x => x.MessageType == MessageType.Set &&
x.Member == message.Member); x.Key == message.Key);
if (old != null) { if (old != null) {
stateMessages.Remove (old); stateMessages.Remove (old);
} }
@ -74,6 +81,8 @@ namespace Ooui
} }
break; break;
} }
MessageLogged?.Invoke (message);
} }
protected void LogCreate () protected void LogCreate ()
@ -90,7 +99,7 @@ namespace Ooui
Log (new Message { Log (new Message {
MessageType = MessageType.Call, MessageType = MessageType.Call,
TargetId = Id, TargetId = Id,
Member = methodName, Key = methodName,
}); });
} }
@ -99,13 +108,13 @@ namespace Ooui
var m = new Message { var m = new Message {
MessageType = MessageType.Set, MessageType = MessageType.Set,
TargetId = Id, TargetId = Id,
Member = Mapping.GetMemberPath (propertyName), Key = Mapping.GetMemberPath (propertyName),
}; };
m.SetValue (value); m.SetValue (value);
Log (m); Log (m);
} }
protected bool SetProperty<T> (ref T backingStore, T newValue, string propertyName = "") protected bool SetProperty<T> (ref T backingStore, T newValue, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{ {
if (!backingStore.Equals (newValue)) { if (!backingStore.Equals (newValue)) {
backingStore = newValue; backingStore = newValue;

View File

@ -129,6 +129,9 @@ namespace Ooui
async void ProcessWebSocketRequest (HttpListenerContext listenerContext, CancellationToken token) async void ProcessWebSocketRequest (HttpListenerContext listenerContext, CancellationToken token)
{ {
//
// Find the element
//
var url = listenerContext.Request.Url; var url = listenerContext.Request.Url;
var path = url.LocalPath; var path = url.LocalPath;
@ -150,9 +153,14 @@ namespace Ooui
return; return;
} }
//
// Connect the web socket
//
WebSocketContext webSocketContext = null; WebSocketContext webSocketContext = null;
WebSocket webSocket = null;
try { try {
webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: "ooui-1.0").ConfigureAwait (false); webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: "ooui-1.0").ConfigureAwait (false);
webSocket = webSocketContext.WebSocket;
Console.WriteLine ("WEBSOCKET {0}", listenerContext.Request.Url.LocalPath); Console.WriteLine ("WEBSOCKET {0}", listenerContext.Request.Url.LocalPath);
} }
catch (Exception ex) { catch (Exception ex) {
@ -162,9 +170,30 @@ namespace Ooui
return; return;
} }
WebSocket webSocket = null;
//
// Preparse handlers for the element
//
Action<Message> onElementMessage = async m => {
if (webSocket == null) return;
try { try {
webSocket = webSocketContext.WebSocket; await SendMessageAsync (webSocket, m, token);
}
catch (Exception ex) {
Error ("Failed to handled element message", ex);
}
};
//
// Communicate!
//
try {
foreach (var m in element.AllMessages) {
if (webSocket.State == WebSocketState.Open) {
await SendMessageAsync (webSocket, m, token);
}
}
element.MessageLogged += onElementMessage;
var receiveBuffer = new byte[1024]; var receiveBuffer = new byte[1024];
@ -190,8 +219,8 @@ namespace Ooui
var receivedString = Encoding.UTF8.GetString (receiveBuffer, 0, size); var receivedString = Encoding.UTF8.GetString (receiveBuffer, 0, size);
Console.WriteLine ("RECEIVED: {0}", receivedString); Console.WriteLine ("RECEIVED: {0}", receivedString);
var outputBuffer = new ArraySegment<byte> (Encoding.UTF8.GetBytes ($"You said: {receivedString}")); // var outputBuffer = new ArraySegment<byte> (Encoding.UTF8.GetBytes ($"You said: {receivedString}"));
await webSocket.SendAsync (outputBuffer, WebSocketMessageType.Text, true, token).ConfigureAwait (false); // await webSocket.SendAsync (outputBuffer, WebSocketMessageType.Text, true, token).ConfigureAwait (false);
} }
} }
} }
@ -202,10 +231,18 @@ namespace Ooui
Error ("Failed to process web socket", ex); Error ("Failed to process web socket", ex);
} }
finally { finally {
element.MessageLogged -= onElementMessage;
webSocket?.Dispose (); webSocket?.Dispose ();
} }
} }
Task SendMessageAsync (WebSocket webSocket, Message message, CancellationToken token)
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject (message);
var outputBuffer = new ArraySegment<byte> (Encoding.UTF8.GetBytes (json));
return webSocket.SendAsync (outputBuffer, WebSocketMessageType.Text, true, token);
}
void Error (string message, Exception ex) void Error (string message, Exception ex)
{ {
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;

View File

@ -9,6 +9,7 @@ namespace Samples
{ {
var server = new Server (); var server = new Server ();
var button = new Button(); var button = new Button();
button.Name = "TestButton";
server.Publish ("/button", button); server.Publish ("/button", button);
server.RunAsync ("http://*:8080/").Wait (); server.RunAsync ("http://*:8080/").Wait ();
return 0; return 0;