Create a simple chat with Sinapse

In this web tutorial we will create a simple chat using Sinapse. We will see some basic Sinapse concepts: server side configuration, viewer side configuration, scripting and styling Gui objects.

Let’s start!

Start Configurator (or click on Project -> New configuration):
new-project
Next click Tools -> Server designer to begin server side configuration:
server-designer-empty
Let’s add SinapseUserPropertyClient plugin, so click + on the left and select SinapseUserPropertyClient in combo box:
plugin-properties

SinapseUserPropertyClient plugin allows us to define properties used in our application.

Change Entity from NotDefined to ChatProperties, this is the our instance plugin “name”:
plugin-properties-entity
Open section parameters and add a property LastMessage with PropertyType System.String and check IsWritable option.
plugin-properties-lastmessage
Click OK to save plugin configuration and close Property window.
server-designer-userpropertyclient
Now let’s configure our chat graphic interface. Close Server designer window and add ChatProperies plugin just configured in viewer canvas clicking on the plugin name in tab Plugins in the right side:
viewer-designer1
Now add a SinapseGuiObjectText calling it Messages without change other object parameters:
viewer-designer-add-guiobjecttext
Press OK to add it on viewer canvas and use Move and Resize tools to set its position and size.
viewer-designer-add-guiobjecttext-resize
Add a SinapseGuiObjectTextButton calling it MessageToSend and changing PropertyNameToShow parameter:
viewer-designer-add-guiobjecttextbox
Press OK to save object configuration and add it on viewer canvas:
viewer-designer-add-guiobjecttextbutton-resize
Let’s changing Button content; right click on added object and select Plugin menu -> Edit resources. Change ButtonContent resource to SEND:
Click OK to see the new button content:
viewer-designer-add-guiobjecttextbutton-resources2
Let’s connecting GUI objects to related properties already defined in UserPropertyClient plugin; select “Hide/show GUI object arrows” button in toolbar to see object connectors and  “Tool to connect objects with arrows” to create links:
viewer-designer-connecting-properties1
Now click on Messages object left connector (blue), drag and drop on UserPropertyClient plugin and you will see the Exported properties window:
Click on + to create a connection between LastMessage plugin property and TextValue Gui object property:
viewer-designer-connecting-properties2

Green arrow is the just created connection. Repeat the same operation with SinapseGuiObjectTextButton selecting again LastMessage property:

In Exported properties window now you can see “1 subscribers”, added in the previous step.
viewer-designer-connecting-properties3
Let’s write the application logic. Right click on SinapseGuiObjectText Messages -> Plugin menu -> Enable/Disable Editor and again right click on SinapseGuiObjectText Messages -> Plugin menu -> Initialization Script Editor, editor window will appear:
script-editor1

Let’s write code to append messages in SinapseGuiTextBox, so let’s add the following C# script:

using MASES.Sinapse.BaseServices;
using MASES.Sinapse.BaseGui;
using System.Windows;
using System.Windows.Controls;
using System;
using Opc.Ua;
using MASES.Sinapse.GuiObjectToolkit;
using MASES.Sinapse.GuiObjects.Base;
using System.Windows.Media;
using System.Runtime.Serialization;


namespace Sinapse
{
    public class ChatViewer : SinapseConfigurableContext
    {
        string chatMessages = string.Empty;
        public override bool PropertyChanged(object sender, string propertyName, Opc.Ua.DataValue propertyValue)
        {
            if (propertyName == "LastMessage")
            {
                if (propertyValue != null)
                {
                    chatMessages += propertyValue.ToString() + Environment.NewLine;
                    propertyValue.Value = chatMessages;
                }
            }
            return true;
        }
    }
}

Let’s study previous code: PropertyChanged; the function is called when the value of a subscribed property changes, in our example we manage LastMessage value changes.

In particular, at first we filter notifications using subscibed plugin property name:

if (propertyName=="LastMessage")

Next we concatenate last received message with previous chat messages:


if (propertyValue!=null)
{
 chatMessages += propertyValue.ToString()+Environment.NewLine;
 propertyValue.Value = chatMessages;
}

And finally the script returns true to permit PropertyChanged event propagation to Gui Object.

This is a very simple script but allows to understand some Sinapse base concepts.
Another way to call PropertyChanged of Gui Object is to use the following code that call PropertyChanged explicitly:

using MASES.Sinapse.BaseServices;
using MASES.Sinapse.BaseGui;
using System.Windows;
using System.Windows.Controls;
using System;
using Opc.Ua;
using MASES.Sinapse.GuiObjectToolkit;
using MASES.Sinapse.GuiObjects.Base;
using System.Windows.Media;
using System.Runtime.Serialization;


namespace Sinapse
{
	public class ChatViewer: SinapseConfigurableContext
	{
		string chatMessages= string.Empty;
		public override bool PropertyChanged(object sender, string propertyName, Opc.Ua.DataValue propertyValue)
		{
			EventWrite.EventWriteWarning(null, "PropertyName="+propertyName);
			//System.Diagnostics.Debugger.Break();
			if (propertyName=="LastMessage")
			{
				SinapseGuiObjectText goText = TargetObject as SinapseGuiObjectText ;
				if (goText ==null) throw new Exception("TargetObject not SinapseGuiObjectText ");
				if (propertyValue!=null) chatMessages += propertyValue.ToString()+Environment.NewLine;
				Opc.Ua.DataValue newVal = new Opc.Ua.DataValue(chatMessages);
				EventWrite.EventWriteWarning(null, "chatMessages="+chatMessages);
				goText.PropertyChanged((IPublisher)sender, propertyName, newVal);
				return false;
			}
			return true;
		}
	}
}

So, using

goText.PropertyChanged((IPublisher)sender, propertyName, newVal);

we can call the Gui Object Text PropertyChanged method and the following instruction stops the event propagation.

return false;
Before compile and apply this script, let’s fill Class name with Sinapse.ChatViewer and select CSharp in Language combo box.
script-editor2
Now we can compile script and apply it. Till now we created and configured project in client side, so we compile script using Target Local:
script-editor3
and, if we get Compile success, we can apply script in client side and next we upload configuration to server.
script-editor4
If we apply script successfully we can connect to server (I suppose that the server is already running and the configurator has the right connection configuration parameters), by clicking Connect.
configurator-connect1
Next click No in the first message box:
configurator-connect2
and Yes in the second one:
configurator-connect3
Let’s restart server to apply uploaded configuration, click on Server menu -> Restart server. After server has been restarted, Configurator reconnects to it automatically:
configurator-connect4
Let’s download server configuration (click on File -> Download  configuration  from server) and then we can start to use our application by sending the first message:
chat-use1
After first message has been sent, we see the string in the chat object Text but it is aligned center vertically, also we need to align top to see all messages and also we need scrollbars. Let’s configure Gui object Text style. Right click on Gui object Text left connector (blue) and select Plugin menu -> Enable/Disable Object Style; again right click on Gui object Text left connector (blue) and then click on Edit resources.
editor-style

Add HorizontalScrollbarVisibility, VerticalScrollBarVisibility, TextWrapping, AcceptsTab and AcceptsReturn setters in TextBox section, after TextElement.Foreground setter:

<Style TargetType="masessgob:SinapseGuiObjectText" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:masessgob="clr-namespace:MASES.Sinapse.GuiObjects.Base;assembly=SinapseGuiObjectBase" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib">
	<Style.Resources>
		...
		<Style TargetType="TextBox" x:Key="{x:Type TextBox}">
			...
			<Setter Property="Control.VerticalContentAlignment">
			        <Setter.Value>
			                <x:Static Member="VerticalAlignment.Top" />
			        </Setter.Value>
			</Setter>
			<Setter Property="ScrollViewer.HorizontalScrollBarVisibility">
				<Setter.Value>
					<x:Static Member="ScrollBarVisibility.Auto" />
				</Setter.Value>
			</Setter>
			<Setter Property="ScrollViewer.VerticalScrollBarVisibility">
				<Setter.Value>
					<x:Static Member="ScrollBarVisibility.Auto" />
				</Setter.Value>
			</Setter>
			<Setter Property="TextBlock.TextWrapping">
				<Setter.Value>
					<x:Static Member="TextWrapping.Wrap" />
				</Setter.Value>
			</Setter>
			<Setter Property="TextBoxBase.AcceptsTab" Value="True"/>
			<Setter Property="KeyboardNavigation.AcceptsReturn" Value="True"/>
		</Style>
	</Style.Resources>
	...
</Style>

Also change TextElement.Foreground from #FFFFFFFF to #FFFFFFAA and click OK.
chat-use2

Next add a script in Gui object TextButton, so right click on TextButton blue connector->Plugin menu -> Enable/Disable Editor and again TextButton blue connector->Plugin menu -> Initialize script to open script editor. Then add the following script:

using MASES.Sinapse.BaseServices;
using MASES.Sinapse.BaseServices;
using MASES.Sinapse.BaseGui;
using System.Windows;
using System.Windows.Controls;
using System;
using Opc.Ua;
using MASES.Sinapse.GuiObjectToolkit;
using MASES.Sinapse.GuiObjects.Base;
using System.Windows.Media;
using System.Runtime.Serialization;


namespace Sinapse
{
    public class ChatSender : SinapseConfigurableContext
    {
        string chatMessages = string.Empty;
        public override bool PropertyChanged(object sender, string propertyName, Opc.Ua.DataValue propertyValue)
        {
            if (propertyName == "LastMessage")
            {
                if (propertyValue != null)
                {
                    propertyValue.Value = string.Empty;
                }
            }
            return true;
        }
    }
}
This is really similar to previous script, it cleans text box after message has been sent. To compile and apply it, set Class name as Sinapse.ChatSender and Language as CSharp and then compile and apply it using the related buttons:
chat-use3
Let’s open Client Runtime to execute another chat client. You will see messages in both applications, Client runtime and Configurator.
chat-use4

You can open new ClientRuntime or Configurator instances to use chat with multiple clients.