Client/Client.cs
author StephaneLenclud
Thu, 24 Sep 2015 14:35:50 +0200
changeset 158 e22bf44c4300
parent 123 0df386e37e29
permissions -rw-r--r--
Fixing issues where layout change would not be detected beccause of partial
field similarity between new and older layout.
Therefore we now clear our fields whenever our layout is changed.
Now resetting our create bitmap flag, hoping it will fix our rather large memory
usage when minimized.
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
}