diff -r 0cb7b9f6a6f8 -r af40a92d480d GUI/SharpDisplayClient.cs --- a/GUI/SharpDisplayClient.cs Mon Sep 22 21:59:11 2014 +0200 +++ b/GUI/SharpDisplayClient.cs Thu Jan 01 23:35:49 2015 +0100 @@ -1,207 +1,64 @@ -/* - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - - Copyright (C) 2009-2012 Michael Möller - -*/ - + using System; using System.Collections.Generic; -using System.Drawing; +using System.Linq; using System.Text; -using System.Diagnostics; +using System.Threading.Tasks; using System.Windows.Forms; -using System.Windows; -using OpenHardwareMonitor.Hardware; -using OpenHardwareMonitor.Utilities; -using System.Runtime.InteropServices; -using UacHelpers; -// +using SharpDisplay; using System.ServiceModel; -using System.Runtime.Serialization; -using SharpDisplay; +using System.ServiceModel.Channels; + namespace SharpDisplay { - - - - /// - /// For client to specify a specific layout. - /// - [DataContract] - public class TableLayout - { - public TableLayout() - { - Columns = new List(); - Rows = new List(); - Cells = new List(); - } - - public TableLayout(int aColumnCount, int aRowCount) - { - Columns = new List(); - Rows = new List(); - - for (int i = 0; i < aColumnCount; i++) - { - Columns.Add(new ColumnStyle(SizeType.Percent, 100 / aColumnCount)); - } - - for (int i = 0; i < aRowCount; i++) - { - Rows.Add(new RowStyle(SizeType.Percent, 100 / aRowCount)); - } - } - - [DataMember] - public List Cells { get; set; } - - [DataMember] - public List Columns { get; set; } - - [DataMember] - public List Rows { get; set; } - - } - /// /// /// - [DataContract] - public class DataField + [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] + public class Callback : ICallback, IDisposable { - [DataMember] - public int Column { get; set; } + private DisplayClient DisplayClient { get; set; } - [DataMember] - public int Row { get; set; } + public Callback(DisplayClient aClient) + { + DisplayClient = aClient; + } - [DataMember] - public int ColumnSpan { get; set; } + public void OnConnected() + { + //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread); + //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId); - [DataMember] - public int RowSpan { get; set; } + MessageBox.Show("OnConnected()", "Client"); + } + + public void OnCloseOrder() + { + DisplayClient.Close(); + //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread); + //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId); + + //MessageBox.Show("OnServerClosing()", "Client"); + //MainForm.CloseConnectionThreadSafe(); + //MainForm.CloseThreadSafe(); + } + + //From IDisposable + public void Dispose() + { + + } } /// - /// TextField can be send to our server to be displayed on the screen. - /// - [DataContract] - public class TextField : DataField - { - public TextField() - { - Index = 0; - Text = ""; - Alignment = ContentAlignment.MiddleLeft; - } - - public TextField(int aIndex, string aText = "", ContentAlignment aAlignment = ContentAlignment.MiddleLeft) - { - Index = aIndex; - Text = aText; - Alignment = aAlignment; - } - - [DataMember] - public int Index { get; set; } - - [DataMember] - public string Text { get; set; } - - [DataMember] - public ContentAlignment Alignment { get; set; } - } - - /// - /// Define our SharpDisplay service. - /// Clients and servers must implement it to communicate with one another. - /// Through this service clients can send requests to a server. - /// Through this service a server session can receive requests from a client. - /// - [ServiceContract(CallbackContract = typeof(ICallback), SessionMode = SessionMode.Required)] - public interface IService - { - /// - /// Set the name of this client. - /// Name is a convenient way to recognize your client. - /// Naming you client is not mandatory. - /// In the absence of a name the session ID is often used instead. - /// - /// - [OperationContract(IsOneWay = true)] - void SetName(string aClientName); - - - /// - /// - /// - [OperationContract(IsOneWay = true)] - void SetLayout(TableLayout aLayout); - - /// - /// Put the given text in the given field on your display. - /// Fields are often just lines of text. - /// - /// - [OperationContract(IsOneWay = true)] - void SetText(TextField aTextField); - - /// - /// Allows a client to set multiple text fields at once. - /// - /// - [OperationContract(IsOneWay = true)] - void SetTexts(System.Collections.Generic.IList aTextFields); - - /// - /// Provides the number of clients currently connected - /// - /// - [OperationContract()] - int ClientCount(); - - } - - /// - /// SharDisplay callback provides a means for a server to notify its clients. - /// - public interface ICallback - { - [OperationContract(IsOneWay = true)] - void OnConnected(); - - /// - /// Tell our client to close its connection. - /// Notably sent when the server is shutting down. - /// - [OperationContract(IsOneWay = true)] - void OnCloseOrder(); - } - - - -} - - - -namespace SharpDisplay -{ - - /// /// /// [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] public class Client : DuplexClientBase { - public string Name { get; set; } public string SessionId { get { return InnerChannel.SessionId; } } public Client(ICallback aCallback) @@ -210,7 +67,6 @@ public void SetName(string aClientName) { - Name = aClientName; Channel.SetName(aClientName); } @@ -219,14 +75,14 @@ Channel.SetLayout(aLayout); } - public void SetText(TextField aTextField) + public void SetField(DataField aField) { - Channel.SetText(aTextField); + Channel.SetField(aField); } - public void SetTexts(System.Collections.Generic.IList aTextFields) + public void SetFields(System.Collections.Generic.IList aFields) { - Channel.SetTexts(aTextFields); + Channel.SetFields(aFields); } public int ClientCount() @@ -236,10 +92,200 @@ public bool IsReady() { - return State == CommunicationState.Opened; + return State == CommunicationState.Opened || State == CommunicationState.Created; } + } + + + /// + /// Handle connection with our Sharp Display Server. + /// If the connection is faulted it will attempt to restart it. + /// + public class DisplayClient + { + private Client iClient; + private Callback iCallback; + private bool resetingConnection = false; + + //private MainForm MainForm { get; set; } + public string SessionId { get { return iClient.SessionId; } } + public string Name { get; private set; } + private TableLayout Layout { get; set; } + private System.Collections.Generic.IList Fields { get; set; } + + + public DisplayClient(/*MainForm aMainForm*/) + { + //MainForm = aMainForm; + Name = ""; + Fields = new DataField[]{}; + } + + /// + /// Initialize our server connection. + /// + public void Open() + { + iCallback = new Callback(this); + iClient = new Client(iCallback); + } + + /// + /// Terminate our server connection. + /// + public void Close() + { + iClient.Close(); + iClient = null; + iCallback.Dispose(); + iCallback = null; + } + + /// + /// Tells whether a server connection is available. + /// + /// True if a server connection is available. False otherwise. + public bool IsReady() + { + return (iClient != null && iCallback != null && iClient.IsReady()); + } + + /// + /// Check if our server connection is available and attempt reset it if it isn't. + /// This is notably dealing with timed out connections. + /// + public void CheckConnection() + { + if (!IsReady() && !resetingConnection) + { + //Try to reconnect + Open(); + + //Avoid stack overflow in case of persisting failure + resetingConnection = true; + + try + { + //On reconnect there is a bunch of properties we need to reset + if (Name != "") + { + iClient.SetName(Name); + } + + SetLayout(Layout); + iClient.SetFields(Fields); + } + finally + { + //Make sure our this state does not get out of sync + resetingConnection = true; + } + } + } + + public void SetName(string aClientName) + { + Name = aClientName; + CheckConnection(); + iClient.SetName(aClientName); + } + + + public void SetLayout(TableLayout aLayout) + { + Layout = aLayout; + CheckConnection(); + iClient.SetLayout(aLayout); + } + + /// + /// Set the specified field. + /// + /// + /// True if the specified field was set client side. False means you need to redefine all your fields using CreateFields. + public bool SetField(DataField aField) + { + int i = 0; + bool fieldFound = false; + foreach (DataField field in Fields) + { + if (field.Index == aField.Index) + { + //Update our field then + Fields[i] = aField; + fieldFound = true; + break; + } + i++; + } + + if (!fieldFound) + { + //Field not found, make to use SetFields with all your fields at least once after setting your layout. + return false; + } + + CheckConnection(); + iClient.SetField(aField); + return true; + } + + /// + /// Use this function when updating existing fields. + /// + /// + public bool SetFields(System.Collections.Generic.IList aFields) + { + int fieldFoundCount = 0; + foreach (DataField fieldUpdate in aFields) + { + int i = 0; + foreach (DataField existingField in Fields) + { + if (existingField.Index == fieldUpdate.Index) + { + //Update our field then + Fields[i] = fieldUpdate; + fieldFoundCount++; + //Move on to the next field + break; + } + i++; + } + } + + // + if (fieldFoundCount!=aFields.Count) + { + //Field not found, make sure to use SetFields with all your fields at least once after setting your layout. + return false; + } + + CheckConnection(); + iClient.SetFields(aFields); + return true; + } + + /// + /// Use this function when creating your fields. + /// + /// + public void CreateFields(System.Collections.Generic.IList aFields) + { + Fields = aFields; + CheckConnection(); + iClient.SetFields(aFields); + } + + public int ClientCount() + { + CheckConnection(); + return iClient.ClientCount(); + } + + } -} \ No newline at end of file +}