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