StephaneLenclud@123: //
StephaneLenclud@123: // Copyright (C) 2014-2015 Stéphane Lenclud.
StephaneLenclud@123: //
StephaneLenclud@123: // This file is part of SharpDisplayManager.
StephaneLenclud@123: //
StephaneLenclud@123: // SharpDisplayManager is free software: you can redistribute it and/or modify
StephaneLenclud@123: // it under the terms of the GNU General Public License as published by
StephaneLenclud@123: // the Free Software Foundation, either version 3 of the License, or
StephaneLenclud@123: // (at your option) any later version.
StephaneLenclud@123: //
StephaneLenclud@123: // SharpDisplayManager is distributed in the hope that it will be useful,
StephaneLenclud@123: // but WITHOUT ANY WARRANTY; without even the implied warranty of
StephaneLenclud@123: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
StephaneLenclud@123: // GNU General Public License for more details.
StephaneLenclud@123: //
StephaneLenclud@123: // You should have received a copy of the GNU General Public License
StephaneLenclud@123: // along with SharpDisplayManager. If not, see .
StephaneLenclud@123: //
StephaneLenclud@123:
StephaneLenclud@123: using System;
sl@20: using System.Collections.Generic;
sl@20: using System.Linq;
sl@20: using System.Text;
sl@20: using System.Threading.Tasks;
sl@20: using System.Windows.Forms;
sl@55: using SharpDisplay;
sl@20: using System.ServiceModel;
sl@20: using System.ServiceModel.Channels;
sl@20:
sl@20:
sl@20: namespace SharpDisplayClient
sl@20: {
sl@25: ///
StephaneLenclud@138: /// Client side Sharp Display callback implementation.
sl@25: ///
sl@31: [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
sl@55: public class Callback : ICallback, IDisposable
sl@20: {
sl@31: private MainForm MainForm { get; set; }
sl@31:
sl@31: public Callback(MainForm aMainForm)
sl@31: {
sl@31: MainForm = aMainForm;
sl@31: }
sl@31:
StephaneLenclud@138: ///
StephaneLenclud@138: /// Not used I believe.
StephaneLenclud@138: ///
sl@20: public void OnConnected()
sl@20: {
sl@20: //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
sl@20: //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId);
sl@20:
sl@20: MessageBox.Show("OnConnected()", "Client");
sl@20: }
sl@20:
sl@20:
sl@32: public void OnCloseOrder()
sl@20: {
sl@20: //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
sl@20: //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId);
sl@20:
sl@21: //MessageBox.Show("OnServerClosing()", "Client");
sl@31: MainForm.CloseConnectionThreadSafe();
sl@31: MainForm.CloseThreadSafe();
sl@20: }
sl@25:
sl@25: //From IDisposable
sl@25: public void Dispose()
sl@25: {
sl@25:
sl@25: }
sl@20: }
sl@20:
sl@20:
sl@25: ///
StephaneLenclud@138: /// Client side implementation of our Sharp Display Service.
sl@25: ///
sl@31: [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
sl@55: public class Client : DuplexClientBase
sl@20: {
sl@30: public string SessionId { get { return InnerChannel.SessionId; } }
sl@26:
sl@57: public Client(ICallback aCallback)
sl@57: : base(new InstanceContext(aCallback), new NetTcpBinding(SecurityMode.None, true), new EndpointAddress("net.tcp://localhost:8001/DisplayService"))
sl@20: { }
sl@20:
sl@32: public void SetName(string aClientName)
sl@20: {
sl@32: Channel.SetName(aClientName);
sl@26: }
sl@26:
sl@62: public void SetLayout(TableLayout aLayout)
sl@62: {
sl@62: Channel.SetLayout(aLayout);
sl@62: }
sl@62:
sl@74: public void SetField(DataField aField)
sl@20: {
sl@74: Channel.SetField(aField);
sl@20: }
sl@20:
sl@74: public void SetFields(System.Collections.Generic.IList aFields)
sl@20: {
sl@74: Channel.SetFields(aFields);
sl@67: }
sl@67:
sl@32: public int ClientCount()
sl@32: {
sl@32: return Channel.ClientCount();
sl@32: }
sl@57:
sl@57: public bool IsReady()
sl@57: {
sl@74: return State == CommunicationState.Opened || State == CommunicationState.Created;
sl@57: }
sl@20: }
sl@73:
sl@73:
sl@73: ///
sl@78: /// Handle connection with our Sharp Display Server.
sl@78: /// If the connection is faulted it will attempt to restart it.
sl@73: ///
sl@73: public class DisplayClient
sl@73: {
sl@78: private Client iClient;
sl@78: private Callback iCallback;
sl@78: private bool resetingConnection = false;
sl@78:
sl@73: private MainForm MainForm { get; set; }
sl@73: public string SessionId { get { return iClient.SessionId; } }
sl@74: public string Name { get; private set; }
sl@74: private TableLayout Layout { get; set; }
sl@74: private System.Collections.Generic.IList Fields { get; set; }
sl@74:
sl@73:
sl@73: public DisplayClient(MainForm aMainForm)
sl@73: {
sl@73: MainForm = aMainForm;
sl@73: Name = "";
sl@74: Fields = new DataField[]{};
sl@73: }
sl@73:
sl@78: ///
sl@78: /// Initialize our server connection.
sl@78: ///
sl@73: public void Open()
sl@73: {
sl@73: iCallback = new Callback(MainForm);
sl@73: iClient = new Client(iCallback);
sl@73: }
sl@73:
sl@78: ///
sl@78: /// Terminate our server connection.
sl@78: ///
sl@73: public void Close()
sl@73: {
sl@73: iClient.Close();
sl@73: iClient = null;
sl@73: iCallback.Dispose();
sl@73: iCallback = null;
sl@73: }
sl@73:
sl@78: ///
sl@78: /// Tells whether a server connection is available.
sl@78: ///
sl@78: /// True if a server connection is available. False otherwise.
sl@73: public bool IsReady()
sl@73: {
sl@73: return (iClient != null && iCallback != null && iClient.IsReady());
sl@73: }
sl@73:
sl@78: ///
sl@78: /// Check if our server connection is available and attempt reset it if it isn't.
sl@78: /// This is notably dealing with timed out connections.
sl@78: ///
sl@73: public void CheckConnection()
sl@73: {
sl@78: if (!IsReady() && !resetingConnection)
sl@73: {
sl@74: //Try to reconnect
sl@73: Open();
sl@74:
sl@78: //Avoid stack overflow in case of persisting failure
sl@78: resetingConnection = true;
sl@78:
sl@78: try
sl@74: {
sl@78: //On reconnect there is a bunch of properties we need to reset
sl@78: if (Name != "")
sl@78: {
sl@78: iClient.SetName(Name);
sl@78: }
sl@78:
sl@78: SetLayout(Layout);
sl@80: iClient.SetFields(Fields);
sl@74: }
sl@78: finally
sl@78: {
sl@78: //Make sure our this state does not get out of sync
sl@78: resetingConnection = true;
sl@78: }
sl@73: }
sl@73: }
sl@73:
StephaneLenclud@138: ///
StephaneLenclud@138: /// Set our client's name.
StephaneLenclud@138: /// Client's name is typically user friendly.
StephaneLenclud@138: /// It does not have to be unique.
StephaneLenclud@138: ///
StephaneLenclud@138: /// Our client name.
sl@73: public void SetName(string aClientName)
sl@73: {
sl@73: Name = aClientName;
sl@73: CheckConnection();
sl@73: iClient.SetName(aClientName);
sl@73: }
sl@73:
StephaneLenclud@138: ///
StephaneLenclud@138: /// Set your client fields' layout.
StephaneLenclud@138: ///
StephaneLenclud@138: /// The layout to apply for this client.
sl@73: public void SetLayout(TableLayout aLayout)
sl@73: {
sl@74: Layout = aLayout;
sl@73: CheckConnection();
sl@73: iClient.SetLayout(aLayout);
sl@73: }
sl@73:
sl@80: ///
sl@80: /// Set the specified field.
sl@80: ///
sl@80: ///
sl@80: /// True if the specified field was set client side. False means you need to redefine all your fields using CreateFields.
sl@80: public bool SetField(DataField aField)
sl@73: {
sl@74: int i = 0;
sl@80: bool fieldFound = false;
sl@74: foreach (DataField field in Fields)
sl@74: {
sl@74: if (field.Index == aField.Index)
sl@74: {
sl@74: //Update our field then
sl@74: Fields[i] = aField;
sl@80: fieldFound = true;
sl@74: break;
sl@74: }
sl@74: i++;
sl@74: }
sl@74:
sl@80: if (!fieldFound)
sl@80: {
StephaneLenclud@138: //Field not found, make sure to use CreateFields first after setting your layout.
sl@80: return false;
sl@80: }
sl@80:
sl@73: CheckConnection();
sl@74: iClient.SetField(aField);
sl@80: return true;
sl@73: }
sl@73:
sl@80: ///
sl@80: /// Use this function when updating existing fields.
sl@80: ///
sl@80: ///
sl@80: public bool SetFields(System.Collections.Generic.IList aFields)
sl@80: {
sl@80: int fieldFoundCount = 0;
sl@80: foreach (DataField fieldUpdate in aFields)
sl@80: {
sl@80: int i = 0;
sl@80: foreach (DataField existingField in Fields)
sl@80: {
sl@80: if (existingField.Index == fieldUpdate.Index)
sl@80: {
sl@80: //Update our field then
sl@80: Fields[i] = fieldUpdate;
sl@80: fieldFoundCount++;
sl@80: //Move on to the next field
sl@80: break;
sl@80: }
sl@80: i++;
sl@80: }
sl@80: }
sl@80:
sl@80: //
sl@80: if (fieldFoundCount!=aFields.Count)
sl@80: {
StephaneLenclud@138: //Field not found, make sure to use CreateFields first after setting your layout.
sl@80: return false;
sl@80: }
sl@80:
sl@80: CheckConnection();
sl@80: iClient.SetFields(aFields);
sl@80: return true;
sl@80: }
sl@80:
sl@80: ///
sl@80: /// Use this function when creating your fields.
StephaneLenclud@138: /// This must be done at least once after setting your layout.
sl@80: ///
sl@80: ///
sl@80: public void CreateFields(System.Collections.Generic.IList aFields)
sl@73: {
sl@74: Fields = aFields;
sl@73: CheckConnection();
sl@74: iClient.SetFields(aFields);
sl@73: }
sl@73:
StephaneLenclud@138: ///
StephaneLenclud@138: /// Provide the number of clients currently connected to our server.
StephaneLenclud@138: ///
StephaneLenclud@138: /// Number of clients currently connected to our server.
sl@73: public int ClientCount()
sl@73: {
sl@73: CheckConnection();
sl@73: return iClient.ClientCount();
sl@73: }
sl@73:
sl@73:
sl@73:
sl@73: }
sl@73:
sl@73:
sl@20: }