diff options
author | Indrajith K L | 2021-05-16 15:41:53 +0530 |
---|---|---|
committer | Indrajith K L | 2021-05-16 15:41:53 +0530 |
commit | 05a44ebcfce184b1ee89f0c983623ff0e17059a9 (patch) | |
tree | 62aa25f91dccbe7dc4fbac9c4cd413008f043709 /SensorClientAppW | |
download | SensorClientXMPP-05a44ebcfce184b1ee89f0c983623ff0e17059a9.tar.gz SensorClientXMPP-05a44ebcfce184b1ee89f0c983623ff0e17059a9.tar.bz2 SensorClientXMPP-05a44ebcfce184b1ee89f0c983623ff0e17059a9.zip |
:tada: Working Sample for Sensor Client
Diffstat (limited to 'SensorClientAppW')
-rw-r--r-- | SensorClientAppW/Form1.Designer.cs | 230 | ||||
-rw-r--r-- | SensorClientAppW/Form1.cs | 78 | ||||
-rw-r--r-- | SensorClientAppW/Form1.resx | 60 | ||||
-rw-r--r-- | SensorClientAppW/Program.cs | 23 | ||||
-rw-r--r-- | SensorClientAppW/Properties/Resources.Designer.cs | 83 | ||||
-rw-r--r-- | SensorClientAppW/Properties/Resources.resx | 127 | ||||
-rw-r--r-- | SensorClientAppW/Resources/loupe.png | bin | 0 -> 25107 bytes | |||
-rw-r--r-- | SensorClientAppW/Resources/sicon.png | bin | 0 -> 337 bytes | |||
-rw-r--r-- | SensorClientAppW/SensorClientAppW.csproj | 35 | ||||
-rw-r--r-- | SensorClientAppW/TheClientV.cs | 654 | ||||
-rw-r--r-- | SensorClientAppW/WeatherData.cs | 14 |
11 files changed, 1304 insertions, 0 deletions
diff --git a/SensorClientAppW/Form1.Designer.cs b/SensorClientAppW/Form1.Designer.cs new file mode 100644 index 0000000..6752a07 --- /dev/null +++ b/SensorClientAppW/Form1.Designer.cs @@ -0,0 +1,230 @@ + +namespace SensorClientAppW +{ + partial class Form1 + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.btnLogin = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.txtBoxPassword = new System.Windows.Forms.TextBox(); + this.txtBoxPort = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtBoxHost = new System.Windows.Forms.TextBox(); + this.txtBoxUserName = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.btnFindDevice = new System.Windows.Forms.Button(); + this.subscriptionDataGrid = new System.Windows.Forms.DataGridView(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.lblConnectedStatus = new System.Windows.Forms.Label(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.subscriptionDataGrid)).BeginInit(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.btnLogin); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.txtBoxPassword); + this.groupBox1.Controls.Add(this.txtBoxPort); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.txtBoxHost); + this.groupBox1.Controls.Add(this.txtBoxUserName); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Location = new System.Drawing.Point(13, 13); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(713, 126); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "XMPP Setup"; + // + // btnLogin + // + this.btnLogin.Location = new System.Drawing.Point(590, 87); + this.btnLogin.Name = "btnLogin"; + this.btnLogin.Size = new System.Drawing.Size(75, 23); + this.btnLogin.TabIndex = 4; + this.btnLogin.Text = "Login"; + this.btnLogin.UseVisualStyleBackColor = true; + this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(509, 23); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(57, 15); + this.label4.TabIndex = 2; + this.label4.Text = "Password"; + // + // txtBoxPassword + // + this.txtBoxPassword.Location = new System.Drawing.Point(509, 41); + this.txtBoxPassword.Name = "txtBoxPassword"; + this.txtBoxPassword.PasswordChar = '*'; + this.txtBoxPassword.Size = new System.Drawing.Size(156, 23); + this.txtBoxPassword.TabIndex = 3; + this.txtBoxPassword.Text = "123456"; + // + // txtBoxPort + // + this.txtBoxPort.Location = new System.Drawing.Point(208, 41); + this.txtBoxPort.Name = "txtBoxPort"; + this.txtBoxPort.Size = new System.Drawing.Size(100, 23); + this.txtBoxPort.TabIndex = 3; + this.txtBoxPort.Text = "5222"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(208, 23); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(29, 15); + this.label2.TabIndex = 2; + this.label2.Text = "Port"; + // + // txtBoxHost + // + this.txtBoxHost.Location = new System.Drawing.Point(7, 42); + this.txtBoxHost.Name = "txtBoxHost"; + this.txtBoxHost.Size = new System.Drawing.Size(176, 23); + this.txtBoxHost.TabIndex = 1; + // + // txtBoxUserName + // + this.txtBoxUserName.Location = new System.Drawing.Point(331, 41); + this.txtBoxUserName.Name = "txtBoxUserName"; + this.txtBoxUserName.Size = new System.Drawing.Size(155, 23); + this.txtBoxUserName.TabIndex = 1; + this.txtBoxUserName.Text = "demoControlApp2"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(331, 23); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(65, 15); + this.label3.TabIndex = 0; + this.label3.Text = "User Name"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(8, 23); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(71, 15); + this.label1.TabIndex = 0; + this.label1.Text = "Host/Broker"; + // + // btnFindDevice + // + this.btnFindDevice.Image = global::SensorClientAppW.Properties.Resources.sicon; + this.btnFindDevice.Location = new System.Drawing.Point(760, 45); + this.btnFindDevice.Name = "btnFindDevice"; + this.btnFindDevice.Size = new System.Drawing.Size(123, 41); + this.btnFindDevice.TabIndex = 1; + this.btnFindDevice.Text = "Find Device"; + this.btnFindDevice.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.btnFindDevice.UseVisualStyleBackColor = true; + this.btnFindDevice.Visible = false; + this.btnFindDevice.Click += new System.EventHandler(this.btnFindDevice_Click); + // + // subscriptionDataGrid + // + this.subscriptionDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.subscriptionDataGrid.Location = new System.Drawing.Point(13, 142); + this.subscriptionDataGrid.Name = "subscriptionDataGrid"; + this.subscriptionDataGrid.RowTemplate.Height = 25; + this.subscriptionDataGrid.Size = new System.Drawing.Size(713, 283); + this.subscriptionDataGrid.TabIndex = 2; + // + // statusStrip1 + // + this.statusStrip1.Location = new System.Drawing.Point(0, 428); + this.statusStrip1.Name = "statusStrip1"; + this.statusStrip1.Size = new System.Drawing.Size(733, 22); + this.statusStrip1.Stretch = false; + this.statusStrip1.TabIndex = 3; + this.statusStrip1.Text = "statusStrip1"; + // + // lblConnectedStatus + // + this.lblConnectedStatus.AutoSize = true; + this.lblConnectedStatus.Font = new System.Drawing.Font("Segoe UI Semibold", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point); + this.lblConnectedStatus.ForeColor = System.Drawing.Color.Red; + this.lblConnectedStatus.Location = new System.Drawing.Point(13, 432); + this.lblConnectedStatus.Name = "lblConnectedStatus"; + this.lblConnectedStatus.Size = new System.Drawing.Size(83, 13); + this.lblConnectedStatus.TabIndex = 4; + this.lblConnectedStatus.Text = "Not Connected"; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(733, 450); + this.Controls.Add(this.lblConnectedStatus); + this.Controls.Add(this.statusStrip1); + this.Controls.Add(this.subscriptionDataGrid); + this.Controls.Add(this.btnFindDevice); + this.Controls.Add(this.groupBox1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "Form1"; + this.Text = "Sensor Control Demo"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); + this.Load += new System.EventHandler(this.Form1_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.subscriptionDataGrid)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox txtBoxPort; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox txtBoxHost; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtBoxPassword; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox txtBoxUserName; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button btnLogin; + private System.Windows.Forms.Button btnFindDevice; + private System.Windows.Forms.DataGridView subscriptionDataGrid; + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.Label lblConnectedStatus; + } +} + diff --git a/SensorClientAppW/Form1.cs b/SensorClientAppW/Form1.cs new file mode 100644 index 0000000..3e8de9b --- /dev/null +++ b/SensorClientAppW/Form1.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace SensorClientAppW +{ + public partial class Form1 : Form + { + TheClientV theClient = null; + public List<WeatherData> weatherData = new List<WeatherData>(); + public Form1() + { + InitializeComponent(); + } + + private void Form1_Load(object sender, EventArgs e) + { + Console.WriteLine("Hello"); + this.subscriptionDataGrid.DataSource = weatherData; + } + + private void btnLogin_Click(object sender, EventArgs e) + { + try + { + string Host = this.txtBoxHost.Text; + int Port = Int32.Parse(this.txtBoxPort.Text); + string UserName = this.txtBoxUserName.Text; + string Password = this.txtBoxPassword.Text; + this.theClient = new TheClientV(Host, Port, UserName, Password, this); + } catch(Exception ex) + { + MessageBox.Show(ex.ToString(), "Something Went Wrong"); + } + } + + public void disableFormGroup() + { + this.groupBox1.Enabled = false; + } + + public void setConnectionStatus(bool status) + { + this.lblConnectedStatus.Text = status ? "Connected" : "Connection Failed"; + this.lblConnectedStatus.ForeColor = status ? Color.YellowGreen: Color.DarkRed; + } + + private void btnFindDevice_Click(object sender, EventArgs e) + { + //this.theClient.searchDevices(); + } + + private void Form1_FormClosing(object sender, FormClosingEventArgs e) + { + if (this.theClient!=null) + { + this.theClient.Shutdown(); + } + } + + public void UpdateDataGrid(WeatherData data) + { + this.subscriptionDataGrid.DataSource = null; + this.weatherData.Add(data); + this.subscriptionDataGrid.DataSource = weatherData; + this.subscriptionDataGrid.Update(); + this.subscriptionDataGrid.Refresh(); + + + } + } +} diff --git a/SensorClientAppW/Form1.resx b/SensorClientAppW/Form1.resx new file mode 100644 index 0000000..f298a7b --- /dev/null +++ b/SensorClientAppW/Form1.resx @@ -0,0 +1,60 @@ +<root> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root>
\ No newline at end of file diff --git a/SensorClientAppW/Program.cs b/SensorClientAppW/Program.cs new file mode 100644 index 0000000..c1a471c --- /dev/null +++ b/SensorClientAppW/Program.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace SensorClientAppW +{ + static class Program + { + /// <summary> + /// The main entry point for the application. + /// </summary> + [STAThread] + static void Main() + { + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/SensorClientAppW/Properties/Resources.Designer.cs b/SensorClientAppW/Properties/Resources.Designer.cs new file mode 100644 index 0000000..511c07d --- /dev/null +++ b/SensorClientAppW/Properties/Resources.Designer.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace SensorClientAppW.Properties { + using System; + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SensorClientAppW.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap loupe { + get { + object obj = ResourceManager.GetObject("loupe", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap sicon { + get { + object obj = ResourceManager.GetObject("sicon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/SensorClientAppW/Properties/Resources.resx b/SensorClientAppW/Properties/Resources.resx new file mode 100644 index 0000000..1cd3b50 --- /dev/null +++ b/SensorClientAppW/Properties/Resources.resx @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="loupe" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\loupe.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="sicon" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\sicon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> +</root>
\ No newline at end of file diff --git a/SensorClientAppW/Resources/loupe.png b/SensorClientAppW/Resources/loupe.png Binary files differnew file mode 100644 index 0000000..e9a225e --- /dev/null +++ b/SensorClientAppW/Resources/loupe.png diff --git a/SensorClientAppW/Resources/sicon.png b/SensorClientAppW/Resources/sicon.png Binary files differnew file mode 100644 index 0000000..9c60908 --- /dev/null +++ b/SensorClientAppW/Resources/sicon.png diff --git a/SensorClientAppW/SensorClientAppW.csproj b/SensorClientAppW/SensorClientAppW.csproj new file mode 100644 index 0000000..b3b67ff --- /dev/null +++ b/SensorClientAppW/SensorClientAppW.csproj @@ -0,0 +1,35 @@ +<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net5.0-windows</TargetFramework> + <UseWindowsForms>true</UseWindowsForms> + <DisableWinExeOutputInference>true</DisableWinExeOutputInference> + <StartupObject></StartupObject> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Waher.Content" Version="1.1.1" /> + <PackageReference Include="Waher.Content.Markdown" Version="1.2.0" /> + <PackageReference Include="Waher.Networking.XMPP.Sensor" Version="1.1.1" /> + <PackageReference Include="Waher.Persistence.Files" Version="1.3.0" /> + <PackageReference Include="Waher.Script" Version="1.0.55" /> + <PackageReference Include="Waher.Script.Persistence" Version="1.0.28" /> + </ItemGroup> + + <ItemGroup> + <Compile Update="Properties\Resources.Designer.cs"> + <DesignTime>True</DesignTime> + <AutoGen>True</AutoGen> + <DependentUpon>Resources.resx</DependentUpon> + </Compile> + </ItemGroup> + + <ItemGroup> + <EmbeddedResource Update="Properties\Resources.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>Resources.Designer.cs</LastGenOutput> + </EmbeddedResource> + </ItemGroup> + +</Project>
\ No newline at end of file diff --git a/SensorClientAppW/TheClientV.cs b/SensorClientAppW/TheClientV.cs new file mode 100644 index 0000000..f74d5ac --- /dev/null +++ b/SensorClientAppW/TheClientV.cs @@ -0,0 +1,654 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Waher.Networking.XMPP.Sensor; +using Waher.Networking.XMPP; +using Waher.Things; +using Waher.Networking.XMPP.ServiceDiscovery; +using Waher.Networking.XMPP.Provisioning; +using System.Windows.Forms; +using Waher.Things.SensorData; +using Waher.Runtime.Settings; +using Waher.Persistence.Files; +using Waher.Persistence; +using System.IO; +using Waher.Runtime.Inventory; +using System.Reflection; +using Waher.Persistence.Serialization; +using Waher.Content.Xml; +using Timer = System.Threading.Timer; +using Waher.Networking.XMPP.Provisioning.SearchOperators; + +namespace SensorClientAppW +{ + class TheClientV + { + string Key = "Your_Broker_Key"; + string Secret = "Your_Broker_Secret"; + private XmppClient xmppClient = null; + string Host = "1451.ieeehyd.org"; + int Port = 5222; + string UserName = "demoControllApp2"; + string PasswordHash = "123456"; + private Form1 formW = null; + private SensorClient sensorClient; + private string sensorJid = null; + private ThingReference sensor = null; + private SensorDataSubscriptionRequest subscription = null; + private double? pressure = null; + private double? humidity = null; + private string deviceId = null; + private FilesProvider db = null; + private ThingRegistryClient registryClient = null; + private bool Location = false; + private string COUNTRY = "IN"; + private string REGION = "TVM"; + private string CITY = "TVM"; + private string AREA = "area"; + private string STREET = "Street"; + private string STREETNR = "StreetNr"; + private string BLD = "Third"; + private string APT = "1"; + private string ROOM = "room1"; + private string NAME = "Kochi"; + private DateTime lastFindFriends = DateTime.MinValue; + public TheClientV(string Host, int Port, string UserName, string Password, Form1 formW) + { + this.Host = Host; + this.Port = Port; + this.UserName = UserName; + this.PasswordHash = Password; + this.formW = formW; + this.init(); + } + + async void init() + { + Types.Initialize( + typeof(FilesProvider).GetTypeInfo().Assembly, + typeof(ObjectSerializer).GetTypeInfo().Assembly, + typeof(RuntimeSettings).GetTypeInfo().Assembly, + typeof(Waher.Content.IContentEncoder).GetTypeInfo().Assembly, + typeof(XmppClient).GetTypeInfo().Assembly, + typeof(Waher.Content.Markdown.MarkdownDocument).GetTypeInfo().Assembly, + typeof(XML).GetTypeInfo().Assembly, + typeof(Waher.Script.Expression).GetTypeInfo().Assembly, + typeof(Waher.Script.Graphs.Graph).GetTypeInfo().Assembly, + typeof(Waher.Script.Persistence.SQL.Select).GetTypeInfo().Assembly, + typeof(TheClientV).Assembly); + db = await FilesProvider.CreateAsync(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + + Path.DirectorySeparatorChar + "IoT-DEMO-APP", "Default", 8192, 1000, 8192, Encoding.UTF8, 10000); + Database.Register(db); + + await db.RepairIfInproperShutdown(null); + await db.Start(); + this.deviceId = await RuntimeSettings.GetAsync("DeviceId", string.Empty); + if (string.IsNullOrEmpty(this.deviceId)) + { + this.deviceId = Guid.NewGuid().ToString().Replace("-", string.Empty); + await RuntimeSettings.SetAsync("DeviceId", this.deviceId); + } + + Console.WriteLine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + + Path.DirectorySeparatorChar + "IoT-DEMO-APP"); + Console.WriteLine(this.deviceId); + + this.xmppClient = new XmppClient(this.Host, this.Port, this.UserName, this.PasswordHash, "en", typeof(TheClientV).Assembly); + this.xmppClient.AllowRegistration(Key, Secret); + this.xmppClient.OnConnectionError += this.ConnectionError; + this.xmppClient.OnStateChanged += this.OnStateChanged; + this.xmppClient.OnRosterItemAdded += OnRosterItemAdded; + this.xmppClient.OnRosterItemUpdated += OnRosterItemUpdated; + this.xmppClient.OnRosterItemRemoved += OnRosterItemRemoved; + this.xmppClient.Connect(); + } + + private Task ConnectionError(object _, Exception ex) + { + Console.WriteLine(ex); + return Task.CompletedTask; + } + + private async Task OnStateChanged(object Sender, XmppState State) + { + switch (State) + { + case XmppState.Connected: + Console.WriteLine("Connected"); + this.formW.disableFormGroup(); + this.formW.setConnectionStatus(true); + this.AddFeatures(); + await this.RegisterDevice(); + //this.formW.su + //this.FindSensors(); + break; + case XmppState.Error: + this.formW.setConnectionStatus(false); + Console.WriteLine("Connection Error"); + break; + case XmppState.Offline: + this.formW.setConnectionStatus(false); + Console.WriteLine("Connection Error"); + break; + } + } + + private Task OnRosterItemAdded(object obj, RosterItem Item) + { + Console.Write("Roster Item Added " + Item.BareJid); + if (Item.IsInGroup("Sensor")) + if (Item.IsInGroup("Sensor")) + { + Console.WriteLine("Requesting presence subscription."+Item.BareJid); + this.xmppClient.RequestPresenceSubscription(Item.BareJid); + } + return Task.CompletedTask; + } + + private Task OnRosterItemUpdated(object obj, RosterItem Item) + { + Console.Write("Roster Item Updated " + Item.BareJid); + bool IsSensor; + if ((IsSensor = (this.sensorJid != null && string.Compare(Item.BareJid, this.sensorJid, true) == 0)) && + (Item.State == SubscriptionState.None || Item.State == SubscriptionState.From) && + Item.PendingSubscription != PendingSubscription.Subscribe) + { + this.FriendshipLost(Item); + } + else if (IsSensor) + this.SubscribeToSensorData(); + return Task.CompletedTask; + } + private void FriendshipLost(RosterItem Item) + { + bool UpdateRegistration = false; + + if (string.Compare(Item.BareJid, this.sensorJid, true) == 0) + { + this.sensorJid = null; + this.sensor = null; + UpdateRegistration = true; + } + + if (UpdateRegistration) + Task.Run(this.RegisterDevice); + } + + private Task OnRosterItemRemoved(object obj, RosterItem Item) + { + Console.Write("Roster Item Removed " + Item.BareJid); + this.FriendshipLost(Item); + return Task.CompletedTask; + } + + private async Task RegisterDevice() + { + string ThingRegistryJid = await RuntimeSettings.GetAsync("ThingRegistry.JID", string.Empty); + + if (!string.IsNullOrEmpty(ThingRegistryJid)) + { + Console.WriteLine("Things Already Registered"); + await this.RegisterDevice(ThingRegistryJid); + } + else + { + Console.Write("Searching for Thing Registry."); + + this.xmppClient.SendServiceItemsDiscoveryRequest(this.xmppClient.Domain, (sender, e) => + { + foreach (Item Item in e.Items) + { + this.xmppClient.SendServiceDiscoveryRequest(Item.JID, async (sender2, e2) => + { + try + { + Item Item2 = (Item)e2.State; + + if (e2.HasFeature(ThingRegistryClient.NamespaceDiscovery)) + { + Console.WriteLine("Thing registry found.: " + Item2.JID); + + await RuntimeSettings.SetAsync("ThingRegistry.JID", Item2.JID); + await this.RegisterDevice(Item2.JID); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString(), "Error Accessing Things Registry"); + } + }, Item); + } + + return Task.CompletedTask; + + }, null); + } + } + + private async Task RegisterDevice(string RegistryJid) + { + if (this.registryClient is null || this.registryClient.ThingRegistryAddress != RegistryJid) + { + if (this.registryClient != null) + { + this.registryClient.Dispose(); + this.registryClient = null; + } + + this.registryClient = new ThingRegistryClient(this.xmppClient, RegistryJid); + } + + string s; + List<MetaDataTag> MetaInfo = new List<MetaDataTag>() + { + new MetaDataStringTag("CLASS", "Controller"), + new MetaDataStringTag("TYPE", "Controller"), + new MetaDataStringTag("MAN", "DEMO"), + new MetaDataStringTag("MODEL", "IOT DEMO Controller"), + new MetaDataStringTag("SN", this.deviceId), + new MetaDataNumericTag("V", 1.0) + }; + + MetaInfo.Add(new MetaDataStringTag("COUNTRY", COUNTRY)); + MetaInfo.Add(new MetaDataStringTag("REGION", REGION)); + MetaInfo.Add(new MetaDataStringTag("CITY", CITY)); + MetaInfo.Add(new MetaDataStringTag("AREA", AREA)); + MetaInfo.Add(new MetaDataStringTag("STREET", STREET)); + MetaInfo.Add(new MetaDataStringTag("STREETNR", STREETNR)); + MetaInfo.Add(new MetaDataStringTag("BLD", BLD)); + MetaInfo.Add(new MetaDataStringTag("APT", APT)); + MetaInfo.Add(new MetaDataStringTag("ROOM", ROOM)); + MetaInfo.Add(new MetaDataStringTag("NAME", NAME)); + + this.UpdateRegistration(MetaInfo.ToArray()); + } + private void RegisterDevice(MetaDataTag[] MetaInfo) + { + Console.WriteLine("Registering device."); + + this.registryClient.RegisterThing(true, MetaInfo, async (sender, e) => + { + try + { + if (e.Ok) + { + Console.WriteLine("Registration successful."); + + await RuntimeSettings.SetAsync("ThingRegistry.Location", true); + this.FindFriends(MetaInfo); + } + else + { + Console.WriteLine("Registration failed."); + await this.RegisterDevice(); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString(), "Error While Registering"); + } + }, null); + } + + private void UpdateRegistration(MetaDataTag[] MetaInfo) + { + Console.Write("Updating registration of device."); + + this.registryClient.UpdateThing(MetaInfo, (sender, e) => + { + if (e.Ok) + Console.WriteLine("Registration update successful."); + else + { + Console.WriteLine("Registration update failed."); + this.RegisterDevice(MetaInfo); + } + + this.FindFriends(MetaInfo); + + return Task.CompletedTask; + + }, null); + } + private void FindFriends(MetaDataTag[] MetaInfo) + { + double ms = (DateTime.Now - lastFindFriends).TotalMilliseconds; + if (ms < 60000) + { + int msi = (int)Math.Ceiling(60000 - ms); + Timer Timer = null; + + Console.WriteLine("Delaying search " + msi.ToString() + " ms."); + + Timer = new Timer((P) => + { + try + { + Timer?.Dispose(); + this.FindFriends(MetaInfo); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + }, null, msi, Timeout.Infinite); + + return; + } + + this.lastFindFriends = DateTime.Now; + this.sensorJid = null; + this.sensor = null; + + foreach (RosterItem Item in this.xmppClient.Roster) + { + if (Item.IsInGroup("Sensors")) + { + this.sensorJid = Item.BareJid; + this.sensor = this.GetReference(Item, "Sensor"); + } + } + + if (!string.IsNullOrEmpty(this.sensorJid)) + this.SubscribeToSensorData(); + + if (string.IsNullOrEmpty(this.sensorJid)) + { + List<SearchOperator> Search = new List<SearchOperator>(); + + foreach (MetaDataTag Tag in MetaInfo) + { + if (Tag is MetaDataStringTag StringTag) + { + switch (StringTag.Name) + { + case "COUNTRY": + case "REGION": + case "CITY": + case "AREA": + case "STREET": + case "STREETNR": + case "BLD": + case "APT": + case "ROOM": + case "NAME": + //Search.Add(new StringTagEqualTo(StringTag.Name, StringTag.StringValue)); + break; + } + } + } + + Search.Add(new StringTagEqualTo("TYPE", "Open Weather Map")); //Search.Add(new StringTagEqualTo("TYPE", "Sensors")); + + Console.WriteLine("Searching for IOT devices in my vicinity."); + + this.registryClient.Search(0, 100, Search.ToArray(), (sender, e) => + { + Console.WriteLine(e.Things.Length.ToString() + (e.More ? "+" : string.Empty) + " things found."); + + foreach (SearchResultThing Thing in e.Things) + { + foreach (MetaDataTag Tag in Thing.Tags) + { + if (Tag.Name == "TYPE" && Tag is MetaDataStringTag StringTag) + { + Console.WriteLine(Tag.StringValue); + switch (Tag.StringValue) + { + case "Open Weather Map": + if (string.IsNullOrEmpty(this.sensorJid)) + { + this.sensorJid = Thing.Jid; + this.sensor = Thing.Node; + + RosterItem Item = this.xmppClient[this.sensorJid]; + if (Item != null) + { + this.xmppClient.UpdateRosterItem(this.sensorJid, Item.Name, + this.AddReference(Item.Groups, "Sensor", Thing.Node)); + + if (Item.State != SubscriptionState.Both && Item.State != SubscriptionState.To) + this.xmppClient.RequestPresenceSubscription(this.sensorJid); + } + else + { + this.xmppClient.AddRosterItem(new RosterItem(this.sensorJid, string.Empty, + this.AddReference(null, "Sensor", Thing.Node))); + + this.xmppClient.RequestPresenceSubscription(this.sensorJid); + } + } + break; + } + } + } + } + + return Task.CompletedTask; + + }, null); + } + } + private string[] AddReference(string[] Groups, string Prefix, IThingReference NodeReference) + { + List<string> Result = new List<string>() + { + Prefix + }; + + if (!string.IsNullOrEmpty(NodeReference.NodeId)) + Result.Add(Prefix + ".nid:" + NodeReference.NodeId); + + if (!string.IsNullOrEmpty(NodeReference.SourceId)) + Result.Add(Prefix + ".sid:" + NodeReference.SourceId); + + if (!string.IsNullOrEmpty(NodeReference.Partition)) + Result.Add(Prefix + ".prt:" + NodeReference.Partition); + + if (Groups != null) + { + foreach (string Group in Groups) + { + if (!Group.StartsWith(Prefix)) + Result.Add(Group); + } + } + + return Result.ToArray(); + } + + private ThingReference GetReference(RosterItem Item, string Prefix) + { + string NodeId = string.Empty; + string SourceId = string.Empty; + string Partition = string.Empty; + + Prefix += "."; + + foreach (string Group in Item.Groups) + { + if (Group.StartsWith(Prefix)) + { + string s = Group.Substring(Prefix.Length); + int i = s.IndexOf(':'); + if (i < 0) + continue; + + switch (s.Substring(0, i).ToLower()) + { + case "nid": + NodeId = s.Substring(i + 1); + break; + + case "sid": + SourceId = s.Substring(i + 1); + break; + + case "prt": + Partition = s.Substring(i + 1); + break; + } + } + } + + return new ThingReference(NodeId, SourceId, Partition); + } + + private void AddFeatures() + { + this.xmppClient.OnError += (Sender, ex) => + { + Console.WriteLine("Error ", ex); + return Task.CompletedTask; + }; + + this.xmppClient.OnPasswordChanged += (Sender, e) => + { + Console.WriteLine("Password changed.", this.xmppClient.BareJID); + }; + + this.xmppClient.OnPresenceSubscribe += (Sender, e) => + { + Console.WriteLine("Accepting friendship request.", this.xmppClient.BareJID, e.From); + e.Accept(); + return Task.CompletedTask; + }; + + this.xmppClient.OnPresenceUnsubscribe += (Sender, e) => + { + Console.WriteLine("Friendship removed.", this.xmppClient.BareJID, e.From); + e.Accept(); + return Task.CompletedTask; + }; + + this.xmppClient.OnPresenceSubscribed += (Sender, e) => + { + Console.WriteLine("Friendship request accepted. " + this.xmppClient.BareJID + " " + e.From); + + if (string.Compare(e.FromBareJID, this.sensorJid, true) == 0) + this.SubscribeToSensorData(); + + return Task.CompletedTask; + }; + + this.xmppClient.OnPresenceUnsubscribed += (Sender, e) => + { + Console.WriteLine("Friendship removal accepted.", this.xmppClient.BareJID, e.From); + return Task.CompletedTask; + }; + + this.xmppClient.OnPresence += XmppClient_OnPresence; + + this.sensorClient = new SensorClient(this.xmppClient); + } + + private Task XmppClient_OnPresence(object Sender, PresenceEventArgs e) + { + Console.WriteLine("Presence received." + e.Availability.ToString() + e.From); + + if (this.sensorJid != null && + string.Compare(e.FromBareJID, this.sensorJid, true) == 0 && + e.IsOnline) + { + this.SubscribeToSensorData(); + } + return Task.CompletedTask; + } + + private void SubscribeToSensorData() + { + RosterItem SensorItem; + + if (!string.IsNullOrEmpty(this.sensorJid) && + (SensorItem = this.xmppClient[this.sensorJid]) != null) + { + if (SensorItem.HasLastPresence && SensorItem.LastPresence.IsOnline) + { + ThingReference[] Nodes; + + if (this.sensor.IsEmpty) + Nodes = null; + else + Nodes = new ThingReference[] { this.sensor }; + + if (this.subscription != null) + { + this.subscription.Unsubscribe(); + this.subscription = null; + } + + Console.WriteLine("Subscribing to events. : "+ SensorItem.LastPresenceFullJid); + + this.subscription = this.sensorClient.Subscribe(SensorItem.LastPresenceFullJid, + Nodes, FieldType.Momentary, new FieldSubscriptionRule[] + { + new FieldSubscriptionRule("Humidity", this.humidity, 1), + new FieldSubscriptionRule("Pressure", this.pressure , 1), + }, + new Waher.Content.Duration(false, 0, 0, 0, 0, 0, 1), + new Waher.Content.Duration(false, 0, 0, 0, 0, 1, 0), true); + + this.subscription.OnStateChanged += Subscription_OnStateChanged; + this.subscription.OnFieldsReceived += Subscription_OnFieldsReceived; + this.subscription.OnErrorsReceived += Subscription_OnErrorsReceived; + } + else if (SensorItem.State == SubscriptionState.From || SensorItem.State == SubscriptionState.None) + { + Console.Write("Requesting presence subscription. : "+this.sensorJid); + this.xmppClient.RequestPresenceSubscription(this.sensorJid); + } + } + } + + private Task Subscription_OnStateChanged(object obj, SensorDataReadoutState NewState) + { + Console.WriteLine("Sensor subscription state changed." + NewState.ToString()); + return Task.CompletedTask; + } + + private Task Subscription_OnFieldsReceived(object obj, IEnumerable<Field> NewFields) + { + Console.WriteLine("Fields received"); + + foreach (Field Field in NewFields) + { + switch (Field.Name) + { + case "Humidity": + if (Field is QuantityField T) + { + this.humidity = T.Value; + } + break; + case "Pressure": + if (Field is QuantityField P) + { + this.pressure = P.Value; + } + break; + } + } + + Console.WriteLine(this.humidity + "," + this.pressure); + this.formW.UpdateDataGrid(new WeatherData() { Pressure = (double)this.pressure, Humidity = (double)this.humidity }); + return Task.CompletedTask; + } + + private Task Subscription_OnErrorsReceived(object obj, IEnumerable<ThingError> NewErrors) + { + MessageBox.Show("Error Subscribing"); + + return Task.CompletedTask; + } + + public void Shutdown() + { + this.db?.Stop()?.Wait(); + this.db?.Flush()?.Wait(); + } + + } +} diff --git a/SensorClientAppW/WeatherData.cs b/SensorClientAppW/WeatherData.cs new file mode 100644 index 0000000..066c580 --- /dev/null +++ b/SensorClientAppW/WeatherData.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SensorClientAppW +{ + public class WeatherData + { + public double Humidity { get; set; } + public double Pressure { get; set; } + } +} |