# HG changeset patch # User sl # Date 1408090801 -7200 # Node ID c375286d1a1c51f7eb47e3d123d52adfbb239935 # Parent c4e03315035c28993976d15c0cdac7cab52ed20a Still trying to setup WCF for us to work nicely. Now using multi threading and reliable session. Implementing thread safe functions where needed. Enforcing session mode. Fixing bug in marquee label as we forgot to reset current position when text is changed. diff -r c4e03315035c -r c375286d1a1c Client/Client.cs --- a/Client/Client.cs Thu Aug 14 18:37:23 2014 +0200 +++ b/Client/Client.cs Fri Aug 15 10:20:01 2014 +0200 @@ -14,7 +14,8 @@ /// /// /// - public partial class Callback : IDisplayServiceCallback, IDisposable + [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] + public class Callback : IDisplayServiceCallback, IDisposable { public void OnConnected() { @@ -46,12 +47,14 @@ /// /// /// - public partial class Client : DuplexClientBase + [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] + public class Client : DuplexClientBase { - private string Name { get; set; } + public string Name { get; set; } + public string SessionId { get { return InnerChannel.SessionId; } } public Client(InstanceContext callbackInstance) - : base(callbackInstance, new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8001/DisplayService")) + : base(callbackInstance, new NetTcpBinding(SecurityMode.None, true), new EndpointAddress("net.tcp://localhost:8001/DisplayService")) { } public void Connect(string aClientName) @@ -77,5 +80,7 @@ Channel.SetTexts(aTexts); } + + } } diff -r c4e03315035c -r c375286d1a1c Client/MainForm.cs --- a/Client/MainForm.cs Thu Aug 14 18:37:23 2014 +0200 +++ b/Client/MainForm.cs Fri Aug 15 10:20:01 2014 +0200 @@ -27,7 +27,7 @@ { //iClient.SetText(0,"Top"); //iClient.SetText(1, "Bottom"); - iClient.SetTexts(new string[] { "Top", "Bottom" }); + iClient.SetTexts(new string[] { iClient.Name, iClient.SessionId }); } private void MainForm_Load(object sender, EventArgs e) @@ -40,7 +40,8 @@ //Connect using unique name string name = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt"); iClient.Connect(name); - Text = Text + ": " + name; + //Text = Text + ": " + name; + Text = Text + ": " + iClient.SessionId; } diff -r c4e03315035c -r c375286d1a1c Interface/Interface.cs --- a/Interface/Interface.cs Thu Aug 14 18:37:23 2014 +0200 +++ b/Interface/Interface.cs Fri Aug 15 10:20:01 2014 +0200 @@ -9,8 +9,9 @@ namespace SharpDisplayInterface { - - [ServiceContract(CallbackContract = typeof(IDisplayServiceCallback))] + + [ServiceContract( CallbackContract = typeof(IDisplayServiceCallback), + SessionMode = SessionMode.Required)] public interface IDisplayService { [OperationContract(IsOneWay = true)] diff -r c4e03315035c -r c375286d1a1c Server/MainForm.Designer.cs --- a/Server/MainForm.Designer.cs Thu Aug 14 18:37:23 2014 +0200 +++ b/Server/MainForm.Designer.cs Fri Aug 15 10:20:01 2014 +0200 @@ -31,6 +31,7 @@ this.components = new System.ComponentModel.Container(); this.tabControl = new System.Windows.Forms.TabControl(); this.tabPageDisplay = new System.Windows.Forms.TabPage(); + this.buttonCloseClients = new System.Windows.Forms.Button(); this.checkBoxFixedPitchFontOnly = new System.Windows.Forms.CheckBox(); this.buttonSuspend = new System.Windows.Forms.Button(); this.buttonStartClient = new System.Windows.Forms.Button(); @@ -38,8 +39,6 @@ this.checkBoxConnectOnStartup = new System.Windows.Forms.CheckBox(); this.panelDisplay = new System.Windows.Forms.Panel(); this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); - this.marqueeLabelTop = new SharpDisplayManager.MarqueeLabel(); - this.marqueeLabelBottom = new SharpDisplayManager.MarqueeLabel(); this.checkBoxShowBorders = new System.Windows.Forms.CheckBox(); this.trackBarBrightness = new System.Windows.Forms.TrackBar(); this.buttonFill = new System.Windows.Forms.Button(); @@ -56,13 +55,17 @@ this.toolStripStatusLabelSpring = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripStatusLabelPower = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripStatusLabelFps = new System.Windows.Forms.ToolStripStatusLabel(); - this.buttonCloseClients = new System.Windows.Forms.Button(); + this.tabPageClients = new System.Windows.Forms.TabPage(); + this.treeViewClients = new System.Windows.Forms.TreeView(); + this.marqueeLabelTop = new SharpDisplayManager.MarqueeLabel(); + this.marqueeLabelBottom = new SharpDisplayManager.MarqueeLabel(); this.tabControl.SuspendLayout(); this.tabPageDisplay.SuspendLayout(); this.panelDisplay.SuspendLayout(); this.tableLayoutPanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.trackBarBrightness)).BeginInit(); this.statusStrip.SuspendLayout(); + this.tabPageClients.SuspendLayout(); this.SuspendLayout(); // // tabControl @@ -72,6 +75,7 @@ | System.Windows.Forms.AnchorStyles.Right))); this.tabControl.Controls.Add(this.tabPageDisplay); this.tabControl.Controls.Add(this.tabPageTests); + this.tabControl.Controls.Add(this.tabPageClients); this.tabControl.Location = new System.Drawing.Point(12, 12); this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; @@ -103,6 +107,16 @@ this.tabPageDisplay.Text = "Display"; this.tabPageDisplay.UseVisualStyleBackColor = true; // + // buttonCloseClients + // + this.buttonCloseClients.Location = new System.Drawing.Point(7, 224); + this.buttonCloseClients.Name = "buttonCloseClients"; + this.buttonCloseClients.Size = new System.Drawing.Size(75, 23); + this.buttonCloseClients.TabIndex = 18; + this.buttonCloseClients.Text = "Close Clients"; + this.buttonCloseClients.UseVisualStyleBackColor = true; + this.buttonCloseClients.Click += new System.EventHandler(this.buttonCloseClients_Click); + // // checkBoxFixedPitchFontOnly // this.checkBoxFixedPitchFontOnly.AutoSize = true; @@ -240,39 +254,6 @@ this.tableLayoutPanel.Size = new System.Drawing.Size(256, 64); this.tableLayoutPanel.TabIndex = 5; // - // marqueeLabelTop - // - this.marqueeLabelTop.AutoEllipsis = true; - this.marqueeLabelTop.BackColor = System.Drawing.Color.Transparent; - this.marqueeLabelTop.Dock = System.Windows.Forms.DockStyle.Fill; - this.marqueeLabelTop.Location = new System.Drawing.Point(1, 1); - this.marqueeLabelTop.Margin = new System.Windows.Forms.Padding(0); - this.marqueeLabelTop.Name = "marqueeLabelTop"; - this.marqueeLabelTop.OwnTimer = false; - this.marqueeLabelTop.PixelsPerSecond = 64; - this.marqueeLabelTop.Separator = "|"; - this.marqueeLabelTop.Size = new System.Drawing.Size(254, 30); - this.marqueeLabelTop.TabIndex = 2; - this.marqueeLabelTop.Text = "ABCDEFGHIJKLMNOPQRST-0123456789"; - this.marqueeLabelTop.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.marqueeLabelTop.UseCompatibleTextRendering = true; - // - // marqueeLabelBottom - // - this.marqueeLabelBottom.AutoEllipsis = true; - this.marqueeLabelBottom.Dock = System.Windows.Forms.DockStyle.Fill; - this.marqueeLabelBottom.Location = new System.Drawing.Point(1, 32); - this.marqueeLabelBottom.Margin = new System.Windows.Forms.Padding(0); - this.marqueeLabelBottom.Name = "marqueeLabelBottom"; - this.marqueeLabelBottom.OwnTimer = false; - this.marqueeLabelBottom.PixelsPerSecond = 64; - this.marqueeLabelBottom.Separator = null; - this.marqueeLabelBottom.Size = new System.Drawing.Size(254, 31); - this.marqueeLabelBottom.TabIndex = 3; - this.marqueeLabelBottom.Text = "abcdefghijklmnopqrst-0123456789"; - this.marqueeLabelBottom.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.marqueeLabelBottom.UseCompatibleTextRendering = true; - // // checkBoxShowBorders // this.checkBoxShowBorders.AutoSize = true; @@ -413,15 +394,57 @@ this.toolStripStatusLabelFps.Size = new System.Drawing.Size(26, 17); this.toolStripStatusLabelFps.Text = "FPS"; // - // buttonCloseClients + // tabPageClients // - this.buttonCloseClients.Location = new System.Drawing.Point(7, 224); - this.buttonCloseClients.Name = "buttonCloseClients"; - this.buttonCloseClients.Size = new System.Drawing.Size(75, 23); - this.buttonCloseClients.TabIndex = 18; - this.buttonCloseClients.Text = "Close Clients"; - this.buttonCloseClients.UseVisualStyleBackColor = true; - this.buttonCloseClients.Click += new System.EventHandler(this.buttonCloseClients_Click); + this.tabPageClients.Controls.Add(this.treeViewClients); + this.tabPageClients.Location = new System.Drawing.Point(4, 22); + this.tabPageClients.Name = "tabPageClients"; + this.tabPageClients.Padding = new System.Windows.Forms.Padding(3); + this.tabPageClients.Size = new System.Drawing.Size(592, 369); + this.tabPageClients.TabIndex = 2; + this.tabPageClients.Text = "Clients"; + this.tabPageClients.UseVisualStyleBackColor = true; + // + // treeViewClients + // + this.treeViewClients.Location = new System.Drawing.Point(6, 6); + this.treeViewClients.Name = "treeViewClients"; + this.treeViewClients.Size = new System.Drawing.Size(439, 357); + this.treeViewClients.TabIndex = 0; + this.treeViewClients.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeViewClients_AfterSelect); + // + // marqueeLabelTop + // + this.marqueeLabelTop.AutoEllipsis = true; + this.marqueeLabelTop.BackColor = System.Drawing.Color.Transparent; + this.marqueeLabelTop.Dock = System.Windows.Forms.DockStyle.Fill; + this.marqueeLabelTop.Location = new System.Drawing.Point(1, 1); + this.marqueeLabelTop.Margin = new System.Windows.Forms.Padding(0); + this.marqueeLabelTop.Name = "marqueeLabelTop"; + this.marqueeLabelTop.OwnTimer = false; + this.marqueeLabelTop.PixelsPerSecond = 64; + this.marqueeLabelTop.Separator = "|"; + this.marqueeLabelTop.Size = new System.Drawing.Size(254, 9); + this.marqueeLabelTop.TabIndex = 2; + this.marqueeLabelTop.Text = "ABCDEFGHIJKLMNOPQRST-0123456789"; + this.marqueeLabelTop.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.marqueeLabelTop.UseCompatibleTextRendering = true; + // + // marqueeLabelBottom + // + this.marqueeLabelBottom.AutoEllipsis = true; + this.marqueeLabelBottom.Dock = System.Windows.Forms.DockStyle.Fill; + this.marqueeLabelBottom.Location = new System.Drawing.Point(1, 21); + this.marqueeLabelBottom.Margin = new System.Windows.Forms.Padding(0); + this.marqueeLabelBottom.Name = "marqueeLabelBottom"; + this.marqueeLabelBottom.OwnTimer = false; + this.marqueeLabelBottom.PixelsPerSecond = 64; + this.marqueeLabelBottom.Separator = null; + this.marqueeLabelBottom.Size = new System.Drawing.Size(254, 20); + this.marqueeLabelBottom.TabIndex = 3; + this.marqueeLabelBottom.Text = "abcdefghijklmnopqrst-0123456789"; + this.marqueeLabelBottom.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.marqueeLabelBottom.UseCompatibleTextRendering = true; // // MainForm // @@ -444,6 +467,7 @@ ((System.ComponentModel.ISupportInitialize)(this.trackBarBrightness)).EndInit(); this.statusStrip.ResumeLayout(false); this.statusStrip.PerformLayout(); + this.tabPageClients.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -471,14 +495,16 @@ private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabelPower; private System.Windows.Forms.Panel panelDisplay; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; - public MarqueeLabel marqueeLabelTop; - public MarqueeLabel marqueeLabelBottom; + private MarqueeLabel marqueeLabelTop; + private MarqueeLabel marqueeLabelBottom; private System.Windows.Forms.CheckBox checkBoxConnectOnStartup; private System.Windows.Forms.CheckBox checkBoxReverseScreen; private System.Windows.Forms.Button buttonStartClient; private System.Windows.Forms.Button buttonSuspend; private System.Windows.Forms.CheckBox checkBoxFixedPitchFontOnly; private System.Windows.Forms.Button buttonCloseClients; + private System.Windows.Forms.TabPage tabPageClients; + private System.Windows.Forms.TreeView treeViewClients; } } diff -r c4e03315035c -r c375286d1a1c Server/MainForm.cs --- a/Server/MainForm.cs Thu Aug 14 18:37:23 2014 +0200 +++ b/Server/MainForm.cs Fri Aug 15 10:20:01 2014 +0200 @@ -366,7 +366,7 @@ new Uri[] { new Uri("net.tcp://localhost:8001/") } ); - iServiceHost.AddServiceEndpoint(typeof(IDisplayService), new NetTcpBinding(), "DisplayService"); + iServiceHost.AddServiceEndpoint(typeof(IDisplayService), new NetTcpBinding(SecurityMode.None,true), "DisplayService"); iServiceHost.Open(); } @@ -407,6 +407,7 @@ foreach (var client in inactiveClients) { iClients.Remove(client); + Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]); } } } @@ -435,7 +436,122 @@ BroadcastCloseEvent(); } + private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e) + { + } + + public delegate void AddClientDelegate(string aSessionId, IDisplayServiceCallback aCallback); + public delegate void RemoveClientDelegate(string aSessionId); + public delegate void SetTextDelegate(int aLineIndex, string aText); + public delegate void SetTextsDelegate(System.Collections.Generic.IList aTexts); + + + /// + /// + /// + /// + /// + public void AddClientThreadSafe(string aSessionId, IDisplayServiceCallback aCallback) + { + if (this.treeViewClients.InvokeRequired) + { + //Not in the proper thread, invoke ourselves + AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe); + this.Invoke(d, new object[] { aSessionId, aCallback }); + } + else + { + //We are in the proper thread + //Add this session to our collection of clients + Program.iMainForm.iClients.Add(aSessionId, aCallback); + //Add this session to our UI + Program.iMainForm.treeViewClients.Nodes.Add(aSessionId, aSessionId); + } + } + + /// + /// + /// + /// + public void RemoveClientThreadSafe(string aSessionId) + { + if (this.treeViewClients.InvokeRequired) + { + //Not in the proper thread, invoke ourselves + RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe); + this.Invoke(d, new object[] { aSessionId }); + } + else + { + //We are in the proper thread + //Remove this session from both client collection and UI tree view + if (Program.iMainForm.iClients.Keys.Contains(aSessionId)) + { + Program.iMainForm.iClients.Remove(aSessionId); + Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]); + } + } + } + + /// + /// + /// + /// + /// + public void SetTextThreadSafe(int aLineIndex, string aText) + { + if (this.treeViewClients.InvokeRequired) + { + //Not in the proper thread, invoke ourselves + SetTextDelegate d = new SetTextDelegate(SetTextThreadSafe); + this.Invoke(d, new object[] { aLineIndex, aText }); + } + else + { + //We are in the proper thread + //Only support two lines for now + if (aLineIndex == 0) + { + Program.iMainForm.marqueeLabelTop.Text = aText; + } + else if (aLineIndex == 1) + { + Program.iMainForm.marqueeLabelBottom.Text = aText; + } + } + } + + /// + /// + /// + /// + public void SetTextsThreadSafe(System.Collections.Generic.IList aTexts) + { + if (this.treeViewClients.InvokeRequired) + { + //Not in the proper thread, invoke ourselves + SetTextsDelegate d = new SetTextsDelegate(SetTextsThreadSafe); + this.Invoke(d, new object[] { aTexts }); + } + else + { + //We are in the proper thread + //Only support two lines for now + for (int i = 0; i < aTexts.Count; i++) + { + if (i == 0) + { + Program.iMainForm.marqueeLabelTop.Text = aTexts[i]; + } + else if (i == 1) + { + Program.iMainForm.marqueeLabelBottom.Text = aTexts[i]; + } + } + } + + } } } diff -r c4e03315035c -r c375286d1a1c Server/MarqueeLabel.cs --- a/Server/MarqueeLabel.cs Thu Aug 14 18:37:23 2014 +0200 +++ b/Server/MarqueeLabel.cs Fri Aug 15 10:20:01 2014 +0200 @@ -203,6 +203,7 @@ private void HandleTextSizeChange() { + CurrentPosition = 0; //For all string measurements and drawing issues refer to the following article: // http://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number //Update text size according to text and font diff -r c4e03315035c -r c375286d1a1c Server/Servers.cs --- a/Server/Servers.cs Thu Aug 14 18:37:23 2014 +0200 +++ b/Server/Servers.cs Fri Aug 15 10:20:01 2014 +0200 @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using SharpDisplayInterface; +using System.Diagnostics; namespace SharpDisplayManager { @@ -12,49 +13,52 @@ /// Implement our display service. /// This class is instantiated anew whenever a client send a request. /// - [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] - class DisplayServer : IDisplayService + [ServiceBehavior( + ConcurrencyMode = ConcurrencyMode.Multiple, + InstanceContextMode = InstanceContextMode.PerSession + )] + class DisplayServer : IDisplayService, IDisposable { + public string SessionId { get; set; } + + DisplayServer() + { + Trace.TraceInformation("Server session opening."); + //First save our session ID. It will be needed in Dispose cause our OperationContxt won't be available then. + SessionId = OperationContext.Current.SessionId; + IDisplayServiceCallback callback = OperationContext.Current.GetCallbackChannel(); + // + Program.iMainForm.AddClientThreadSafe(SessionId,callback); + + } + + public void Dispose() + { + Trace.TraceInformation("Server session closing."); + Program.iMainForm.RemoveClientThreadSafe(SessionId); + } + //From IDisplayService public void SetTexts(System.Collections.Generic.IList aTexts) { - //Only support two lines for now - for (int i=0; i(); - Program.iMainForm.iClients.Add(aClientName, callback); - + //IDisplayServiceCallback callback = OperationContext.Current.GetCallbackChannel(); + //Program.iMainForm.iClients.Add(aClientName, callback); + //Program.iMainForm.treeViewClients.Nodes.Add(aClientName, aClientName); //For some reason MP still hangs on that one //callback.OnConnected(); } @@ -63,13 +67,17 @@ public void Disconnect(string aClientName) { //remove the old client if any + /* if (Program.iMainForm.iClients.Keys.Contains(aClientName)) { Program.iMainForm.iClients.Remove(aClientName); + Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aClientName,false)[0]); } + */ } + }