Client/Client.cs
author StephaneLenclud
Fri, 19 Jun 2015 17:12:06 +0200
changeset 141 6f1da2b5c2ec
parent 123 0df386e37e29
permissions -rw-r--r--
Prevents switching clients too often.
     1 //
     2 // Copyright (C) 2014-2015 Stéphane Lenclud.
     3 //
     4 // This file is part of SharpDisplayManager.
     5 //
     6 // SharpDisplayManager is free software: you can redistribute it and/or modify
     7 // it under the terms of the GNU General Public License as published by
     8 // the Free Software Foundation, either version 3 of the License, or
     9 // (at your option) any later version.
    10 //
    11 // SharpDisplayManager is distributed in the hope that it will be useful,
    12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14 // GNU General Public License for more details.
    15 //
    16 // You should have received a copy of the GNU General Public License
    17 // along with SharpDisplayManager.  If not, see <http://www.gnu.org/licenses/>.
    18 //
    19 
    20 using System;
    21 using System.Collections.Generic;
    22 using System.Linq;
    23 using System.Text;
    24 using System.Threading.Tasks;
    25 using System.Windows.Forms;
    26 using SharpDisplay;
    27 using System.ServiceModel;
    28 using System.ServiceModel.Channels;
    29 
    30 
    31 namespace SharpDisplayClient
    32 {
    33     /// <summary>
    34     /// Client side Sharp Display callback implementation.
    35     /// </summary>
    36     [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
    37     public class Callback : ICallback, IDisposable
    38     {
    39         private MainForm MainForm { get; set; }
    40 
    41         public Callback(MainForm aMainForm)
    42         {
    43             MainForm = aMainForm;
    44         }
    45 
    46         /// <summary>
    47         /// Not used I believe.
    48         /// </summary>
    49         public void OnConnected()
    50         {
    51             //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
    52             //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId);
    53 
    54             MessageBox.Show("OnConnected()", "Client");
    55         }
    56 
    57 
    58         public void OnCloseOrder()
    59         {
    60             //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
    61             //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId);
    62 
    63             //MessageBox.Show("OnServerClosing()", "Client");
    64             MainForm.CloseConnectionThreadSafe();
    65             MainForm.CloseThreadSafe();
    66         }
    67 
    68         //From IDisposable
    69         public void Dispose()
    70         {
    71 
    72         }
    73     }
    74 
    75 
    76     /// <summary>
    77     /// Client side implementation of our Sharp Display Service.
    78     /// </summary>
    79     [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
    80     public class Client : DuplexClientBase<IService>
    81     {
    82         public string SessionId { get { return InnerChannel.SessionId; } }
    83 
    84         public Client(ICallback aCallback)
    85             : base(new InstanceContext(aCallback), new NetTcpBinding(SecurityMode.None, true), new EndpointAddress("net.tcp://localhost:8001/DisplayService"))
    86         { }
    87 
    88         public void SetName(string aClientName)
    89         {
    90             Channel.SetName(aClientName);
    91         }
    92 
    93         public void SetLayout(TableLayout aLayout)
    94         {
    95             Channel.SetLayout(aLayout);
    96         }
    97 
    98         public void SetField(DataField aField)
    99         {
   100             Channel.SetField(aField);
   101         }
   102 
   103         public void SetFields(System.Collections.Generic.IList<DataField> aFields)
   104         {
   105             Channel.SetFields(aFields);
   106         }
   107 
   108         public int ClientCount()
   109         {
   110             return Channel.ClientCount();
   111         }
   112 
   113         public bool IsReady()
   114         {
   115             return State == CommunicationState.Opened || State == CommunicationState.Created;
   116         }
   117     }
   118 
   119 
   120     /// <summary>
   121     /// Handle connection with our Sharp Display Server.
   122     /// If the connection is faulted it will attempt to restart it.
   123     /// </summary>
   124     public class DisplayClient
   125     {
   126         private Client iClient;
   127         private Callback iCallback;
   128         private bool resetingConnection = false;
   129 
   130         private MainForm MainForm { get; set; }
   131         public string SessionId { get { return iClient.SessionId; } }
   132         public string Name { get; private set; }
   133         private TableLayout Layout { get; set; }
   134         private System.Collections.Generic.IList<DataField> Fields { get; set; }
   135 
   136 
   137         public DisplayClient(MainForm aMainForm)
   138         {
   139             MainForm = aMainForm;
   140             Name = "";
   141             Fields = new DataField[]{};
   142         }
   143 
   144         /// <summary>
   145         /// Initialize our server connection.
   146         /// </summary>
   147         public void Open()
   148         {
   149             iCallback = new Callback(MainForm);
   150             iClient = new Client(iCallback);
   151         }
   152 
   153         /// <summary>
   154         /// Terminate our server connection.
   155         /// </summary>
   156         public void Close()
   157         {
   158             iClient.Close();
   159             iClient = null;
   160             iCallback.Dispose();
   161             iCallback = null;
   162         }
   163 
   164         /// <summary>
   165         /// Tells whether a server connection is available.
   166         /// </summary>
   167         /// <returns>True if a server connection is available. False otherwise.</returns>
   168         public bool IsReady()
   169         {
   170             return (iClient != null && iCallback != null && iClient.IsReady());
   171         }
   172 
   173         /// <summary>
   174         /// Check if our server connection is available and attempt reset it if it isn't.
   175         /// This is notably dealing with timed out connections.
   176         /// </summary>
   177         public void CheckConnection()
   178         {
   179             if (!IsReady() && !resetingConnection)
   180             {
   181                 //Try to reconnect
   182                 Open();
   183 
   184                 //Avoid stack overflow in case of persisting failure
   185                 resetingConnection = true;
   186 
   187                 try
   188                 {
   189                     //On reconnect there is a bunch of properties we need to reset
   190                     if (Name != "")
   191                     {
   192                         iClient.SetName(Name);
   193                     }
   194 
   195                     SetLayout(Layout);
   196                     iClient.SetFields(Fields);
   197                 }
   198                 finally
   199                 {
   200                     //Make sure our this state does not get out of sync
   201                     resetingConnection = true;
   202                 }
   203             }
   204         }
   205 
   206         /// <summary>
   207         /// Set our client's name.
   208         /// Client's name is typically user friendly.
   209         /// It does not have to be unique.
   210         /// </summary>
   211         /// <param name="aClientName">Our client name.</param>
   212         public void SetName(string aClientName)
   213         {
   214             Name = aClientName;
   215             CheckConnection();
   216             iClient.SetName(aClientName);
   217         }
   218 
   219         /// <summary>
   220         /// Set your client fields' layout.
   221         /// </summary>
   222         /// <param name="aLayout">The layout to apply for this client.</param>
   223         public void SetLayout(TableLayout aLayout)
   224         {
   225             Layout = aLayout;
   226             CheckConnection();
   227             iClient.SetLayout(aLayout);
   228         }
   229 
   230         /// <summary>
   231         /// Set the specified field.
   232         /// </summary>
   233         /// <param name="aField"></param>
   234         /// <returns>True if the specified field was set client side. False means you need to redefine all your fields using CreateFields.</returns>
   235         public bool SetField(DataField aField)
   236         {
   237             int i = 0;
   238             bool fieldFound = false;
   239             foreach (DataField field in Fields)
   240             {
   241                 if (field.Index == aField.Index)
   242                 {
   243                     //Update our field then
   244                     Fields[i] = aField;
   245                     fieldFound = true;
   246                     break;
   247                 }
   248                 i++;
   249             }
   250 
   251             if (!fieldFound)
   252             {
   253                 //Field not found, make sure to use CreateFields first after setting your layout.
   254                 return false;
   255             }
   256 
   257             CheckConnection();
   258             iClient.SetField(aField);
   259             return true;
   260         }
   261 
   262         /// <summary>
   263         /// Use this function when updating existing fields.
   264         /// </summary>
   265         /// <param name="aFields"></param>
   266         public bool SetFields(System.Collections.Generic.IList<DataField> aFields)
   267         {
   268             int fieldFoundCount = 0;
   269             foreach (DataField fieldUpdate in aFields)
   270             {
   271                 int i = 0;
   272                 foreach (DataField existingField in Fields)
   273                 {
   274                     if (existingField.Index == fieldUpdate.Index)
   275                     {
   276                         //Update our field then
   277                         Fields[i] = fieldUpdate;
   278                         fieldFoundCount++;
   279                         //Move on to the next field
   280                         break;
   281                     }
   282                     i++;
   283                 }
   284             }
   285 
   286             //
   287             if (fieldFoundCount!=aFields.Count)
   288             {
   289                 //Field not found, make sure to use CreateFields first after setting your layout.
   290                 return false;
   291             }
   292 
   293             CheckConnection();
   294             iClient.SetFields(aFields);
   295             return true;
   296         }
   297 
   298         /// <summary>
   299         /// Use this function when creating your fields.
   300         /// This must be done at least once after setting your layout.
   301         /// </summary>
   302         /// <param name="aFields"></param>
   303         public void CreateFields(System.Collections.Generic.IList<DataField> aFields)
   304         {
   305             Fields = aFields;
   306             CheckConnection();
   307             iClient.SetFields(aFields);
   308         }
   309 
   310         /// <summary>
   311         /// Provide the number of clients currently connected to our server.
   312         /// </summary>
   313         /// <returns>Number of clients currently connected to our server.</returns>
   314         public int ClientCount()
   315         {
   316             CheckConnection();
   317             return iClient.ClientCount();
   318         }
   319 
   320 
   321 
   322     }
   323 
   324 
   325 }