﻿<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Catégorie .NET API - Constellation</title>
	<atom:link href="https://developer.myconstellation.io/client-api/net-package-api/feed/" rel="self" type="application/rss+xml" />
	<link>https://developer.myconstellation.io/client-api/net-package-api/</link>
	<description>Votre plateforme d&#039;interconnexion</description>
	<lastBuildDate>Thu, 19 Apr 2018 07:56:31 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.0.11</generator>

<image>
	<url>https://developer.myconstellation.io/wp-content/uploads/2016/02/256x256-e1457476015859.png</url>
	<title>Catégorie .NET API - Constellation</title>
	<link>https://developer.myconstellation.io/client-api/net-package-api/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Créez votre premier package Constellation en C#</title>
		<link>https://developer.myconstellation.io/getting-started/creez-votre-premier-package-constellation-en-csharp/</link>
					<comments>https://developer.myconstellation.io/getting-started/creez-votre-premier-package-constellation-en-csharp/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Wed, 16 Mar 2016 10:00:22 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[Guide de démarrage]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[PackageHost]]></category>
		<category><![CDATA[WriteLog]]></category>
		<category><![CDATA[Setting]]></category>
		<category><![CDATA[Publish]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1287</guid>

					<description><![CDATA[<p>Dans cet article nous allons découvrir comment créer et déployer votre premier package Constellation en C# avec Visual Studio. Prérequis Un accès “Administrator” à une Constellation (Management API &#38; Developper access) Le SDK Constellation installé Nous vous conseillons de suivre</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/getting-started/creez-votre-premier-package-constellation-en-csharp/">Créez votre premier package Constellation en C#</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Dans cet article nous allons découvrir comment créer et déployer votre premier package Constellation en C# avec Visual Studio.</p>
<p style="text-align: center;"><span id="more-1287"></span><br />
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/wo3960Gwv6k" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<h3>Prérequis</h3>
<ul>
<li>Un accès “Administrator” à une Constellation (Management API &amp; Developper access)</li>
<li>Le SDK Constellation installé</li>
</ul>
<p>Nous vous conseillons de suivre <a href="/getting-started/installer-constellation/">le guide de démarrage ici</a> avant de démarrer.</p>
<h3>Créez le package dans Visual Studio</h3>
<ul>
<li>Lancez Visual Studio</li>
<li>Créez un nouveau projet de type “Constellation Package Console” (dans la catégorie Constellation) :</li>
</ul>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-98.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Création d'un package Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-75.png" alt="Création d'un package Constellation" width="424" height="294" border="0" /></a></p>
<ul>
<li>Démarrez le package en mode debug en cliquant sur le bouton “Start” ou en pressant la touche “F5” :</li>
</ul>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-160.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" title="Debugging Visual Studio" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-137.png" alt="Debugging Visual Studio" width="424" height="111" border="0" /></a></p>
<ul>
<li>
<div align="left">Au démarrage, votre package affichera une sorte de ”Hello World” :</div>
</li>
</ul>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-100.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Hello World" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-77.png" alt="Hello World" width="424" height="209" border="0" /></a></p>
<p align="left">Vous constaterez que :</p>
<ul>
<li>
<div align="left">La méthode “<em>OnStart</em>” est la méthode invoquée au démarrage de votre package</div>
</li>
<li>
<div align="left"><em>PackageHost.WriteInfo</em> est une méthode pour écrire des logs de type “info”</div>
</li>
<li>
<div align="left">PackageHost.IsRunning = true (votre package est bien en cours)</div>
</li>
<li>
<div align="left">PackageHost.IsConnected = false (votre package n’est pas connecté à Constellation, car nous l’avons lancé en local)</div>
</li>
</ul>
<h3 align="left">Fonctionnement de base</h3>
<p align="left">Un package est une application !</p>
<p align="left">Il faut impérativement appeler la méthode “<em>PackageHost.Start</em>” au démarrage de l’application, c’est à dire dans la méthode “Main” autrement, ce n’est pas un package Constellation mais une simple application !</p>
<p align="left">Lorsque vous appelez la méthode “<em>PackageHost.Start</em>” vous devez impérativement transférer les arguments (args) et indiquer la classe de votre package qui est dans notre exemple “Program”.</p>
<p></p><pre class="crayon-plain-tag">static void Main(string[] args)
{
    PackageHost.Start&lt;Program&gt;(args);
}</pre><p></p>
<p align="left">La classe d’un package doit être une classe qui implémente l’interface “<u>IPackage</u>”. Cette interface définie trois méthodes :</p>
<ul>
<li>
<div align="left"><u>OnStart</u> qui sera invoqué lorsque le package a démarré</div>
</li>
<li>
<div align="left"><u>OnPreShutdown</u> : invoqué lorsque le package va s’arrêter (à ce stade votre package est toujours connecté à Constellation, vous pouvez encore pusher des StateObjects, envoyer des messages, écrire des logs sur le hub, etc..)</div>
</li>
<li>
<div align="left">OnShutdown : invoqué après le <u>OnPreShutdown</u> et après avoir fermé les connexions.</div>
</li>
</ul>
<p>Pour vous éviter de devoir implémenter ces trois méthodes, votre classe peut hériter de la classe “PackageBase”. Cette classe abstraite implémente l’interface <u>IPackage</u> dans des méthodes virtuelles vides.</p>
<p>Libre à vous d’implémenter les méthodes que vous souhaitez !</p>
<p>Dans le template de projet créé ci-dessus, la classe “Program” hérite de “PackageBase” et redéfinie la méthode “OnStart” pour écrire un message de type “info” (<em>PackageHost.WriteInfo</em>) lorsque le package a démarré.</p>
<h3>Ecrire des logs</h3>
<p>Pour écrire des logs depuis un package Constellation vous disposez des méthodes :</p>
<ul>
<li>PackageHost.WriteDebug</li>
<li>PackageHost.WriteInfo</li>
<li>PackageHost.WriteWarn</li>
<li>PackageHost.WriteError</li>
</ul>
<p>Chacune de ces méthodes écrivent un message qui peut être formaté avec des arguments à la manière d’un “string.Format” :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.WriteInfo("Je suis le package nommé {0} version {1}", PackageHost.PackageName, PackageHost.PackageVersion);</pre><p></p>
<p><u>Attention</u> : bien respecter les index dans le format de votre message sous peine d’avoir une erreur.</p>
<p><u>Note</u> : la méthode WriteDebug écrit seulement dans la console (mode debug local). Les logs de type “Debug” ne sont jamais envoyés dans la Constellation.</p>
<h3>Accéder aux settings</h3>
<p>Chaque package peut définir des paramètres de configuration définis au niveau du serveur Constellation. Cela vous permet de changer ces paramètres directement depuis la Constellation qui se chargera de redescendre ces paramètres sur vos packages.</p>
<p>Il y a deux types de settings :</p>
<ul>
<li>Les “Setting Value” : très simple il s’agit d’un couple clé/value à l’instant des &lt;appSettings&gt; d’une application .NET</li>
<li>Les “Setting Content”  : au lieu de définir la valeur d’un paramètre dans un attribut XML, on peut la définir dans un élément XML enfant permettant d’avoir des settings qui renferme du XML ou JSON</li>
</ul>
<p>Voici par exemple des “SettingValues”  déclarés dans notre configuration :</p>
<p></p><pre class="crayon-plain-tag">&lt;setting key="MyBoolSetting" value="true" /&gt;      
&lt;setting key="MyStringSetting" value="This is a string" /&gt;
&lt;setting key="MyIntSetting" value="123" /&gt;</pre><p></p>
<p>Ces trois settings définissent la valeur dans l’attribut “value” (= SettingValue).</p>
<p>Pour récupérer la valeur du paramètre “MyStringSetting”  dans votre code, utilisez la méthode “GetSettingValue” :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.WriteInfo("My String = {0}", PackageHost.GetSettingValue("MyStringSetting"));</pre><p></p>
<p>La méthode “GetSettingValue” renvoie la valeur d’un setting de type “string” mais vous pouvez également caster la valeur depuis cette méthode en utilisant sa forme générique :</p>
<p></p><pre class="crayon-plain-tag">int myIntSetting = PackageHost.GetSettingValue&lt;int&gt;("MyIntSetting");
bool myBoolSetting = PackageHost.GetSettingValue&lt;bool&gt;("MyBoolSetting");</pre><p></p>
<p>Si par contre vous avez un modèle de configuration plus compliqué qu’une série de clé/valeur vous pouvez utiliser les “Setting Contents” pour définir du XML ou JSON comme valeur de setting.</p>
<p>Par exemple la configuration de votre package peut définir deux autres settings, contenant du XML ou JSON de cette façon :</p>
<p></p><pre class="crayon-plain-tag">&lt;setting key="MyXmlDocument"&gt;
  &lt;content&gt;
    &lt;note date="09-02-2016"&gt;
      &lt;to&gt;Tove&lt;/to&gt;
      &lt;from&gt;Jani&lt;/from&gt;
      &lt;heading&gt;Reminder&lt;/heading&gt;
      &lt;body&gt;Don't forget me this weekend!&lt;/body&gt;
    &lt;/note&gt;
  &lt;/content&gt;
&lt;/setting&gt;

&lt;setting key="MyJsonObject"&gt;
  &lt;content&gt;
    &lt;![CDATA[
    {
      "Number": 123,
      "String" : "This is a test (local)",
      "Boolean": true
    }
    ]]&gt;
  &lt;/content&gt;
&lt;/setting&gt;</pre><p></p>
<p>Plusieurs méthodes pour récupérer ces settings :</p>
<ul>
<li><u>GetSettingValue</u> : vous pouvez toujours récupérer le contenu brute de votre setting sous forme d’un string</li>
<li><u>GetSettingAsJsonObject</u> : dé-sérialise le contenu JSON de votre setting et vous retourne un objet dynamique</li>
<li><u>GetSettingAsJsonObject&lt;T&gt;</u> : dé-sérialise le contenu JSON de votre setting et le convertie dans un objet de votre type (T)</li>
<li><u>GetSettingAsXmlDocument</u> : dé-sérialise le contenu XML de votre setting et vous retourne un XmlDocument</li>
<li><u>GetSettingAsConfigurationSection&lt;TConfigurationSection&gt;</u> : dé-sérialise le contenu XML de votre setting sous forme d’un ConfigurationSection .NET</li>
</ul>
<p>Par exemple, pour manipuler le setting XML on pourrait écrire :</p>
<p></p><pre class="crayon-plain-tag">var xml = PackageHost.GetSettingAsXmlDocument("MyXmlDocument");
PackageHost.WriteInfo("My XmlDocument Attribute = {0} , first node value = {1}", xml.ChildNodes[0].Attributes["date"].Value, xml.ChildNodes[0].FirstChild.InnerText);</pre><p></p>
<p>Autre exemple avec le setting JSON :</p>
<p></p><pre class="crayon-plain-tag">dynamic json = PackageHost.GetSettingAsJsonObject("MyJsonObject");
PackageHost.WriteInfo("My JsonObject String={0}, Int={1}, Boolean={2}", json.String, json.Number, json.Boolean);</pre><p></p>
<p>Les settings d’un package sont déclarés au niveau du serveur dans la déclaration du package et/ou dans le fichier App.config.</p>
<p>Tous les settings doivent être déclarés dans le manifeste du package (fichier PackageInfo.xml).</p>
<p>Retrouvez plus d’information sur les settings <a href="/client-api/net-package-api/les-settings/">dans cet article</a>.</p>
<h3>Publier des StateObjects</h3>
<p>Pour publier (Push) un StateObject dans Constellation vous devez invoquer la méthode “<em>PackageHost.PushStateObject</em>” en précisant obligatoirement le nom du StateObject et sa valeur.</p>
<p>Par exemple, vous pouvez publié n’importe quel type de base :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("MyString", "OK");
PackageHost.PushStateObject("MyNumber", 123);
PackageHost.PushStateObject("MyDecimal", 123.12);
PackageHost.PushStateObject("MyBoolean", true);</pre><p></p>
<p>Pouvez également publié des objets anonymes :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("AnonymousObject", new { String = "test", Number = 123 });</pre><p></p>
<p>Ou encore avec des types plus complexes :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject&lt;MyCustomObject&gt;("MyObject",  new MyCustomObject() { String = "test", Number = 123 });</pre><p></p>
<p>Vous avez également la possibilité de définir des paramètres optionnels comme les métadonnées de votre StateObject ou sa durée de vie.</p>
<p>Par exemple, le StateObject “ShortLife” a une durée de vie de 20 secondes après sa publication. Au delà il sera marqué comme expiré.</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("ShortLife", "Expire in 20 secs !!!", lifetime: 20);</pre><p></p>
<p>Ici, on publie un StateObject “Salon” en lui associé les metadatas “Id” et “Zone”.</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("Salon", monObjetZoneSalon, metadatas: new Dictionary&lt;string, object&gt; { { "Id", 42 }, { "Zone", "Salon" } });</pre><p></p>
<p>Sur les objets personnalisés il est recommande de décorer les classes de l’attribut [StateObject] si vous avez possibilité de modifier le code de la classe. Autrement, sur votre classe IPackage, ajoutez l’attribut [StateObjectKnownTypes] en définissant tous les types de StateObjects que vous serez amener à publier. Cela permet de décrire ces types dans la Constellation pour l’auto-description (utilisez entre autre par les générateurs de code).</p>
<p>Pour plus d’information sur <a href="/client-api/net-package-api/les-stateobjects/">les StateObjects dans l’API .NET cliquez ici</a>.</p>
<h3>Tester son package dans sa Constellation</h3>
<p>Pour tester nous allons créer un package qui push à intervalle régulier un StateObject.</p>
<p>Le code de notre Package sera :</p>
<p></p><pre class="crayon-plain-tag">public class Program : PackageBase
{
    static void Main(string[] args)
    {
        PackageHost.Start&lt;Program&gt;(args);
    }

    public override void OnStart()
    {
        PackageHost.WriteInfo("Package starting - IsRunning: {0} - IsConnected: {1}", PackageHost.IsRunning, PackageHost.IsConnected);
        PackageHost.WriteInfo("Je suis le package nommé {0} version {1}", PackageHost.PackageName, PackageHost.PackageVersion);

        while (PackageHost.IsRunning)
        {
            PackageHost.WriteInfo("Je push un long !");
            PackageHost.PushStateObject("DemoLong", DateTime.Now.Ticks);

            Thread.Sleep(PackageHost.GetSettingValue&lt;int&gt;("Interval"));
        }
    }
}</pre><p></p>
<p>Notre application console démarre bien le package au démarrage (Main) où le package est notre classe Program qui hérite de PackageBase (donc c’est un IPackage).</p>
<p>Au démarrage on écrit deux logs de type information qui indique notamment le nom du package, sa version, si il est démarré (IsRunning) et si il est connecté (IsConnected).</p>
<p>Ensuite on crée une boucle qui tournera tant que le package est démarré. A chaque itération on écrit un log, publie un StateObject avant de mettre le thread en pause avant la prochaine itération.</p>
<p>Le temps de pause est un setting de type “Int” que l’on a nommé “Interval”.</p>
<p>L’utilisateur pourra le configurer dans la configuration du package sur le serveur, mais pour être sûr d’avoir une valeur par défaut nous allons déclarer ce setting ainsi que sa valeur par default dans son manifest.</p>
<p>Editez le fichier PackageInfo.xml pour ajouter :</p>
<p></p><pre class="crayon-plain-tag">&lt;Setting name="Interval" type="Int32" defaultValue="5000" description="Interval en millisecondes" /&gt;</pre><p></p>
<p>Ainsi même si le setting n’est pas déclaré sur le serveur ni dans le App.config local,  cette valeur sera par défaut égale à 5 secondes.</p>
<p>Vous pouvez démarrer le projet en mode debug (“F5” ou bouton “Start”) mais bien entendu il ne sera pas connecté à Constellation.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-101.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Debug dans Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-78.png" alt="Debug dans Constellation" width="424" height="216" border="0" /></a></p>
<p>Pour débugger dans Constellation, vous trouverez une icone dans la toolbar (en en pressant Ctrl+Alt+F8) :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-161.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" title="Debug dans Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-138.png" alt="Debug dans Constellation" width="424" height="100" border="0" /></a></p>
<p>Vous pouvez également cliquez droit sur votre projet et dans le menu Constellation sélectionnez “Debug On Constellation” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-102.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Debug dans Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-79.png" alt="Debug dans Constellation" width="424" height="307" border="0" /></a></p>
<p align="left">Si vous n’avez pas encore configuré la Constellation utilisée pour le débogage, vous devriez voir une alerte vous invitant à configurer vos accès :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-103.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Enregistrement de la Constellation dans VS" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-80.png" alt="Enregistrement de la Constellation dans VS" width="424" height="205" border="0" /></a></p>
<p align="left">Cette fois ci, votre package tourne bien en étant connecté à votre Constellation :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-104.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Hello World Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-81.png" alt="Hello World Constellation" width="424" height="216" border="0" /></a></p>
<p align="left">Lancez maintenant votre Console Constellation, vous verrez en temps réel les logs de votre package.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-105.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Hello World Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-82.png" alt="Hello World Constellation" width="424" height="232" border="0" /></a></p>
<p align="left">Sur le StateObject Explorer, vous verrez également le StateObject “DemoLong” publié par le package “MonPremierPackage”.</p>
<p align="left">Notez que la sentinelle qui héberge le package est une sentinelle virtuelle nommée “Developer”.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-106.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="StateObject Explorer" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-83.png" alt="StateObject Explorer" width="424" height="140" border="0" /></a></p>
<p align="left">Vous pouvez cliquer sur “View” pour voir toutes les informations à propos de ce StateObject.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-107.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Visualisation des StateObjects" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-84.png" alt="Visualisation des StateObjects" width="227" height="244" border="0" /></a></p>
<p align="left">Pour finir, cliquez sur “Subscribe” pour vous abonner aux mises à jour.</p>
<h3 align="left">Publier son package dans Constellation</h3>
<p align="left">Nous voulons maintenant publier ce package et le déployer sur une des sentinelles de notre Constellation.</p>
<p align="left">Toujours dans la toolbar “Constellation”, cliquez sur “Publish Constellation package” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-162.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-139.png" alt="image" width="424" height="100" border="0" /></a></p>
<p align="left">Ou depuis le menu contextuel de votre projet, sélectionnez “Publish Constellation package” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-108.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Publier un package Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-85.png" alt="Publier un package Constellation" width="424" height="324" border="0" /></a></p>
<p align="left">Vous pouvez soit le publier en local puis l’uploader manuellement via la Console par exemple, soit le publier directement dans votre Constellation.</p>
<p align="left">Pour cela, sélectionnez “Upload on Constellation Server”.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-109.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Publier un package Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-86.png" alt="Publier un package Constellation" width="424" height="212" border="0" /></a></p>
<p align="left">Sélectionnez ensuite l’adresse de votre Constellation et cliquez sur “Publish”.</p>
<p align="left">Un message vous informera de la réussite de la publication. Dans le panneau “Ouput” de Visual Studio vous retrouvez également le détail de la publication :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-110.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Publier un package Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-87.png" alt="Publier un package Constellation" width="424" height="98" border="0" /></a></p>
<p align="left">Depuis la Console, sur la page “Package Repository” vous pourrez apercevoir votre package fraichement publié :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-111.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Package Repository" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-88.png" alt="Package Repository" width="424" height="153" border="0" /></a></p>
<p align="left">Pour le déployer, vous devez éditer la configuration de votre Constellation pour ajouter le package à une (ou plusieurs) sentinelle. Vous pouvez également redéfinir le setting “Interval” que nous exploitations dans notre package (bien que nous avons défini une valeur par défaut).</p>
<p></p><pre class="crayon-plain-tag">&lt;package name="MonPremierPackage" enable="true"&gt;
    &lt;settings&gt;
        &lt;setting key="Interval" value="2000" /&gt;
  &lt;/settings&gt;
&lt;/package&gt;</pre><p></p>
<p align="left">Vous pouvez éditer la configuration du serveur Constellation directement depuis Visual Studio en cloquant sur “Edit Constellation Server configuration” (depuis la toolbar ou via le menu contextuel).</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-163.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-140.png" alt="image" width="424" height="100" border="0" /></a></p>
<p align="left">La configuration sera automatiquement téléchargée du serveur et ouverte dans Visual Studio :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-164.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" title="Edition de la configuration depuis VS" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-141.png" alt="Edition de la configuration depuis VS" width="424" height="230" border="0" /></a></p>
<p align="left">L’avantage est de pouvoir profiter de la validation du schéma XML et de l&rsquo;IntelliSense de Visual Studio :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-165.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" title="Intellisense" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-142.png" alt="Intellisense" width="420" height="163" border="0" /></a></p>
<p align="left">Dès que vous enregistrerez votre configuration, Visual Studio détectera les modifications et vous proposera d’uploader et de recharger la nouvelle configuration sur votre serveur Constellation :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-166.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" title="Upload de la configuration" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-143.png" alt="Upload de la configuration" width="242" height="144" border="0" /></a></p>
<p align="left">Vous pouvez aussi éditer la configuration de votre Constellation depuis la Console. Après avoir ajouter votre package, cliquez sur “Save &amp; Deploy” (pour informer les sentinelles des nouveaux packages) :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-115.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Edition de la configuration" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-92.png" alt="Edition de la configuration" width="424" height="233" border="0" /></a></p>
<p align="left">Vous pourrez alors voir dans les logs que le package est automatiquement téléchargé et déployé sur sa sentinelle et commence à pusher des StateObject toutes les 2 secondes comme nous l’avons indiqué dans sa configuration :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-113.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Console log temps réel" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-90.png" alt="Console log temps réel" width="424" height="207" border="0" /></a></p>
<p align="left">Sur la page “Packages” vous pouvez maintenant contrôler votre package, l’arrêter, le redémarrer ou le recharger (= déploiement d’une nouvelle version).</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-114.png"><img class="colorbox-1287"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Contrôle des packages" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-91.png" alt="Contrôle des packages" width="424" height="126" border="0" /></a></p>
<h3 align="left">Next steps</h3>
<ul>
<li>
<div align="left">Les <a href="/client-api/net-package-api/les-bases-des-packages-net/">bases des packages .NET</a></div>
</li>
<li>
<div align="left"><a href="/client-api/net-package-api/settings/">Les Settings</a> : paramètres de configuration de vos packages</div>
</li>
<li>
<div align="left"><a href="/client-api/net-package-api/pushstateobject/">Publier des StateObjects</a></div>
</li>
<li>
<div align="left">Exposer vos méthodes dans Constellation grâce aux <a href="/client-api/net-package-api/messagecallbacks/">MessageCallbacks</a></div>
</li>
<li>
<div align="left"><a href="/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/">Envoyer des messages &amp; Invoquer des méthodes</a></div>
</li>
<li>
<div align="left"><a href="/client-api/net-package-api/consommer-des-stateobjects/">Consommer des StateObjects</a></div>
</li>
<li>
<div align="left">Accéder au hub de contrôle depuis un package C# avec le <a href="/client-api/net-package-api/controlmanager/">ControlManager</a></div>
</li>
<li>
<div align="left">Créer des <a href="/client-api/net-package-api/packages-ui-wpf-winform/">Packages UI</a> en Winform ou WPF</div>
</li>
</ul>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/getting-started/creez-votre-premier-package-constellation-en-csharp/">Créez votre premier package Constellation en C#</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/getting-started/creez-votre-premier-package-constellation-en-csharp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Les bases des packages .NET</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/les-bases-des-packages-net/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/les-bases-des-packages-net/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Mon, 21 Mar 2016 13:21:31 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[PackageHost]]></category>
		<category><![CDATA[WriteLog]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1522</guid>

					<description><![CDATA[<p>Fonctionnement de base Comme nous l’avons vu dans le tutoriel précèdent, un package est une application ! Il faut impérativement appeler la méthode “PackageHost.Start” au démarrage de l’application, c’est à dire dans la méthode “Main” autrement, ce n’est pas un</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/les-bases-des-packages-net/">Les bases des packages .NET</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h3 align="left">Fonctionnement de base</h3>
<p align="left">Comme nous l’avons vu dans <a href="/getting-started/creez-votre-premier-package-constellation-en-csharp/">le tutoriel précèdent</a>, un package est une application !</p>
<p align="left">Il faut impérativement appeler la méthode “<em>PackageHost.Start</em>” au démarrage de l’application, c’est à dire dans la méthode “Main” autrement, ce n’est pas un package Constellation mais une simple application .NET !</p>
<p align="left">Lorsque vous appelez la méthode “<em>PackageHost.Start</em>” vous devez impérativement transférer les arguments (args) et indiquer la classe de votre package (ici nommée “MonPackage”).</p>
<p></p><pre class="crayon-plain-tag">static void Main(string[] args)
{
    PackageHost.Start&lt;MonPackage&gt;(args);
}</pre><p></p>
<p align="left">La classe d’un package doit être une classe qui implémente l’interface “<em><u>IPackage</u></em>”. Cette interface définit trois méthodes :</p>
<ul>
<li>
<div align="left"><u>OnStart</u> qui sera invoqué lorsque le package a démarré</div>
</li>
<li>
<div align="left"><u>OnPreShutdown</u> : invoqué lorsque le package va s’arrêter (à ce stade votre package est toujours connecté à Constellation, vous pouvez encore pusher des StateObjects, envoyer des messages, écrire des logs sur le hub, etc..)</div>
</li>
<li>
<div align="left"><u>OnShutdown</u> : invoqué après le OnPreShutdown et après avoir fermé les connexions (plus aucune interaction avec la Constellation possible)</div>
</li>
</ul>
<p>Pour vous éviter de devoir implémenter ces trois méthodes, votre classe peut hériter de la classe “<em>PackageBase</em>”. Cette classe abstraite implémente l’interface <u>IPackage</u> dans des méthodes virtuelles vides.</p>
<p>Libre à vous d’implémenter les méthodes que vous souhaitez !</p>
<p></p><pre class="crayon-plain-tag">public class MonPackage : PackageBase
{
    //public override void OnStart()
    //{
    //}

    //public override void OnPreShutdown()
    //{
    //}

    //public override void OnShutdown()
    //{
    //}
}</pre><p></p>
<h3>Ecrire des logs</h3>
<p>Pour écrire des logs depuis un package Constellation vous disposez des méthodes suivantes :</p>
<ul>
<li>PackageHost.WriteDebug</li>
<li>PackageHost.WriteInfo</li>
<li>PackageHost.WriteWarn</li>
<li>PackageHost.WriteError</li>
</ul>
<p>Chacune de ces méthodes écrivent un message qui peut être formaté avec des arguments à la manière d’un “string.Format” :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.WriteInfo("Je suis le package nommé {0} version {1}", PackageHost.PackageName, PackageHost.PackageVersion);</pre><p></p>
<p><u>Attention</u> : bien respecter les index dans le format de votre message sous peine d’avoir une erreur.</p>
<p>Pour éviter cela, vous pouvez depuis C# 6.0 (intégré nativement depuis Visual Studio 2015), utiliser les « <a href="https://msdn.microsoft.com/fr-fr/library/dn961160.aspx?f=255&amp;MSPPError=-2147217396">chaines interpolée</a> » en plaçant en début de chaine un « $ » afin de pouvoir placer les arguments directement dans la chaine :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.WriteInfo($"Je suis le package nommé {PackageHost.PackageName} version {PackageHost.PackageVersion}");</pre><p></p>
<p><u>Note</u> : la méthode WriteDebug écrit seulement dans la console (mode debug local). Les logs de type “Debug” ne sont jamais envoyés dans la Constellation.</p>
<h3>Propriétés du PackageHost</h3>
<p>La classe centrale “PackageHost” expose différentes propriétés pour votre package Constellation.</p>
<h4>PackageHost.IsRunning</h4>
<p>Il s’agit d’un booléen qui indique si votre package est démarré. Il est affecté à “true” juste avant le OnStart et passe à “false” juste avant le OnPreShutdown.</p>
<p>Cela vous permet de connaitre l’état de votre package et sert notamment pour interrompre des boucles :</p>
<p></p><pre class="crayon-plain-tag">while (PackageHost.IsRunning)
{
    // Do job
}</pre><p></p>
<h4>PackageHost.IsStandAlone</h4>
<p>Cette propriété vous indique si votre package est démarré en “stand-alone”, c’est à dire lancé manuellement (en lançant le EXE) ou via Visual Studio (Debug on Constellation).</p>
<p>Si le package est démarré depuis une sentinelle Constellation, cette propriété est à “false”.</p>
<h4>PackageHost.IsConnected</h4>
<p>Vous indiques si le package est connecté ou non à la Constellation.</p>
<h4>PackageHost.ConstellationHub</h4>
<p>Retourne le proxy SignalR du hub Constellation.</p>
<h4>PackageHost.HasControlManager</h4>
<p>Indique si le package est connecté au hub de contrôle.</p>
<h4>PackageHost.ControlManager</h4>
<p>Retourne le client du hub de contrôle permettant de contrôler la Constellation. (<a href="/client-api/net-package-api/controlmanager/">plus d’information</a>)</p>
<p>Cette propriété est nulle si votre package ne demande pas de connexion au hub de contrôle. Vous devez donc tester si le client est disponible ou non avec la propriété ci-dessus :</p>
<p></p><pre class="crayon-plain-tag">if (PackageHost.HasControlManager)
{
     PackageHost.ControlManager.xxxx();
}</pre><p></p>
<h4>PackageHost.Package</h4>
<p>Retourne l’instance de votre package.</p>
<h4>PackageHost.PackageDescriptor</h4>
<p>Retourne l’instance du PackageDescriptor qui contient la description de vos MessagesCallbacks et StateObjects.</p>
<h4>PackageHost.PackageName</h4>
<p>Retourne le nom du package.</p>
<p>Le nom du package est défini dans le manifeste du package (PackageInfo.xml). Si aucune valeur n’est trouvée, le nom du package est le nom de l’assembly .NET du package.</p>
<h4>PackageHost.PackageInstanceName</h4>
<p>Retourne le nom de l’instance du package, c’est à dire le nom défini sur le serveur.</p>
<p>Un même package peut avoir plusieurs instances sur une même sentinelle mais avec des noms de package différents.</p>
<h4>PackageHost.SentinelName</h4>
<p>Retourne le nom de la sentinelle qui a démarré le package (si IsStandAlone = false).</p>
<p>Cette propriété est nulle si le package est démarré manuellement (en lançant le EXE) ou égale à “Developer” si lancé depuis Visual Studio.</p>
<h4>PackageHost.PackageVersion</h4>
<p>Retourne le numéro de version du package tel que défini dans le manifeste du package. Si il n’y a pas de manifeste, elle retourne le “PackageAssemblyVersion”.</p>
<h4>PackageHost.PackageAssemblyVersion</h4>
<p>Retourne le numéro de version de l’assembly du package.</p>
<h4>PackageHost.ConstellationClientVersion</h4>
<p>Retourne le numéro de version de la librairie Constellation utilisée par le package.</p>
<h4>PackageHost.PackageManifest</h4>
<p>Retourne le manifeste du package. (<a href="/concepts/package-manifest/">plus d’information</a>)</p>
<h4>PackageHost.Settings</h4>
<p>Retourne les settings du packages. (<a href="/client-api/net-package-api/settings/">plus d’information</a>)</p>
<h3>Connexion à Constellation</h3>
<p>Pour savoir si votre package est connecté à Constellation, vous pouvez interroger la propriété “PackageHost.IsConnected” comme indiqué ci-dessus.</p>
<p>Vous avez également l’évènement “ConnectionStateChanged” qui se déclenche lorsque le statut de la connexion change :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ConnectionStateChanged += (s, e) =&gt;
{
    if (PackageHost.IsConnected)
    {
        // Récupération de la connexion
        // TODO : MAJ des états ?
    }
    else
    {
        // Perte de la connexion
    }
};</pre><p></p>
<p>Notez que lors de la première connexion cet événement n’est pas levé car Constellation n’a pas encore invoqué la méthode “OnStart” de votre package. Autrement dit le package se connecte d’abord puis “vous donnes la main”.</p>
<p>Donc si l’événement “ConnectionStateChanged“ est déclenché et que la propriété PackageHost.IsConnected = true, c’est qu’il s’agit forcement d’une reconnexion !</p>
<p>Dans ce cas, et en fonction de votre package, il est recommandé de “mettre à jour l’état” de votre package en re-pushant vos StateObjects.</p>
<p><u>Exemple :</u></p>
<p>Vous développez un package Constellation qui fait le lien vers votre système d’alarme.</p>
<p>Chaque zone de votre alarme est publiée dans Constellation sous forme de StateObject. Vous avez donc autant de StateObject que de zone et chacun de ces StateObjects indique l’état d&rsquo;une zone de l&rsquo;alarme (ouverte ou fermée).</p>
<p>Si pour une raison ou pour une autre, la connexion entre le package et la Constellation est rompue, l’activité sur les zones de l’alarme ne peut donc plus être mis à jours.</p>
<p>C’est pour cela que lorsque votre package parvient à se reconnecter il peut être nécessaire de republier tous les StateObjects sur Constellation pour être sûr d’avoir des StateObjects à jour par rapport à votre système d’alarme.</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/les-bases-des-packages-net/">Les bases des packages .NET</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/les-bases-des-packages-net/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Settings : paramètres de configuration de vos packages</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/settings/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/settings/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Wed, 16 Mar 2016 12:15:58 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[Setting]]></category>
		<category><![CDATA[SettingsGroups]]></category>
		<category><![CDATA[SettingValue]]></category>
		<category><![CDATA[SettingContent]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1297</guid>

					<description><![CDATA[<p>Chaque package peut définir des paramètres de configuration centralisés au niveau du serveur Constellation. L&#8217;ensemble des paramètres des configurations de vos différents packages, virtuels ou non, sont donc centralisés dans un fichier unique. Une modification de valeur est automatiquement mis</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/settings/">Settings : paramètres de configuration de vos packages</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Chaque package peut définir des paramètres de configuration centralisés au niveau du serveur Constellation.</p>
<p>L&rsquo;ensemble des paramètres des configurations de vos différents packages, virtuels ou non, sont donc centralisés dans un fichier unique. Une modification de valeur est automatiquement mis à jour sur le package cible.</p>
<p>Il y a deux types de settings :</p>
<ul>
<li>Le “Setting Value” : il s’agit d’un couple clé/valeur défini dans des attributs XML à l’instar des &lt;appSettings&gt; d’une application .NET</li>
<li>Le “Setting Content”  : au lieu de définir la valeur d’un paramètre dans un attribut XML, on peut la définir dans un élément XML enfant permettant d’avoir des settings renfermant du XML ou JSON</li>
</ul>
<h3>Les Settings Values</h3>
<p>Voici par exemple des “SettingValues”  déclarés dans la configuration d&rsquo;un package :</p>
<p></p><pre class="crayon-plain-tag">&lt;setting key="MyBoolSetting" value="true" /&gt;      
&lt;setting key="MyStringSetting" value="This is a string" /&gt;
&lt;setting key="MyIntSetting" value="123" /&gt;</pre><p></p>
<p>Ces trois settings définissent la valeur dans l’attribut “value” (= SettingValue).</p>
<p>Pour récupérer la valeur du paramètre “MyStringSetting”  dans votre code C#, vous pouvez utiliser la méthode “<em>GetSettingValue</em>” :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.WriteInfo("My String = {0}", PackageHost.GetSettingValue("MyStringSetting"));</pre><p></p>
<p>La méthode “<em>GetSettingValue</em>” vous retourne par défaut la valeur d’un setting en “string” mais vous pouvez également convertir cette valeur en précisant le type attendu :</p>
<p></p><pre class="crayon-plain-tag">int myIntSetting = PackageHost.GetSettingValue&lt;int&gt;("MyIntSetting");
bool myBoolSetting = PackageHost.GetSettingValue&lt;bool&gt;("MyBoolSetting");</pre><p></p>
<h3>Les Settings Contents</h3>
<p>Si vous avez un modèle de configuration plus compliqué qu’une série de clé/valeur vous pouvez utiliser les “Setting Contents” pour définir du XML ou JSON comme valeur de setting.</p>
<p>Par exemple la configuration de votre package peut définir deux autres settings, l’un contenant du XML et l’autre du JSON de cette façon :</p>
<p></p><pre class="crayon-plain-tag">&lt;setting key="MyXmlDocument"&gt;
  &lt;content&gt;
    &lt;note date="09-02-2016"&gt;
      &lt;to&gt;Tove&lt;/to&gt;
      &lt;from&gt;Jani&lt;/from&gt;
      &lt;heading&gt;Reminder&lt;/heading&gt;
      &lt;body&gt;Don't forget me this weekend!&lt;/body&gt;
    &lt;/note&gt;
  &lt;/content&gt;
&lt;/setting&gt;

&lt;setting key="MyJsonObject"&gt;
  &lt;content&gt;
    &lt;![CDATA[
    {
      "Number": 123,
      "String" : "This is a test (local)",
      "Boolean": true
    }
    ]]&gt;
  &lt;/content&gt;
&lt;/setting&gt;</pre><p></p>
<p>Voici les méthodes pour récupérer ces settings :</p>
<ul>
<li><u>GetSettingValue</u> : récupère le contenu brute de votre setting sous forme d’un string</li>
<li><u>GetSettingAsJsonObject</u> : dé-sérialise le contenu JSON de votre setting et vous retourne un objet dynamique</li>
<li><u>GetSettingAsJsonObject&lt;T&gt;</u> : dé-sérialise le contenu JSON de votre setting et le converti dans un objet de votre type (T)</li>
<li><u>GetSettingAsXmlDocument</u> : dé-sérialise le contenu XML de votre setting et vous retourne un XmlDocument</li>
<li><u>GetSettingAsConfigurationSection&lt;TConfigurationSection&gt;</u> : dé-sérialise le contenu XML de votre setting sous forme d’un ConfigurationSection .NET</li>
</ul>
<p>Par exemple, pour manipuler le setting XML on pourrait écrire :</p>
<p></p><pre class="crayon-plain-tag">var xml = PackageHost.GetSettingAsXmlDocument("MyXmlDocument");
PackageHost.WriteInfo("My XmlDocument Attribute = {0} , first node value = {1}", xml.ChildNodes[0].Attributes["date"].Value, xml.ChildNodes[0].FirstChild.InnerText);</pre><p></p>
<p>Autre exemple avec le setting JSON :</p>
<p></p><pre class="crayon-plain-tag">dynamic json = PackageHost.GetSettingAsJsonObject("MyJsonObject");
PackageHost.WriteInfo("My JsonObject String={0}, Int={1}, Boolean={2}", json.String, json.Number, json.Boolean);</pre><p></p>
<h3>TryGetSettings</h3>
<p>Notez que toutes ces méthodes citées ci-dessus ont un équivalent de type “<em><strong>Try</strong>GetSettingXXX</em>” :</p>
<ul>
<li>TryGetSettingAsConfigurationSection&lt;TConfigurationSection&gt;</li>
<li>TryGetSettingAsJsonObject</li>
<li>TryGetSettingAsJsonObject&lt;T&gt;</li>
<li>TryGetSettingAsXmlDocument</li>
<li>TryGetSettingValue&lt;T&gt;</li>
</ul>
<p>Ces méthodes placent le résultat dans un paramètre de sortie (“out”) et vous retourne un booléen indiquant si l’opération est réussite ou non.</p>
<p>Par exemple :</p>
<p></p><pre class="crayon-plain-tag">int monParametre = 0;
if (PackageHost.TryGetSettingValue&lt;int&gt;("MyIntSetting", out monParametre))
{
    PackageHost.WriteInfo("Lecture de 'MyIntSetting' en int OK. Valeur = {0}", monParametre);
}
else
{
    PackageHost.WriteError("Impossible de récupérer le setting 'MyIntSetting' en int");
}</pre><p></p>
<h3>Déclarer les settings dans le Package Manifest</h3>
<p>Tous les settings devraient être déclarés dans le <a href="/concepts/package-manifest/">manifeste du package</a> (fichier PackageInfo.xml).</p>
<p>La déclaration des settings dans le manifeste n’a pas impact dans le fonctionnement du package mais il permet de décrire la configuration du package pour que des outils tel que la Console puisse proposer une interface graphique de configuration pour chaque package. Il est donc vivement recommandé de décrire les settings de ses packages. De plus il est possible de définir des valeurs par défaut dans le manifeste.</p>
<p>Chaque setting déclaré dans le manifeste doit obligatoirement comporter les attributs suivants  :</p>
<ul>
<li><u>Name</u> : le nom (clé) du setting</li>
<li><u>Type</u> : le type du setting</li>
</ul>
<p>Le type peut être :</p>
<ul>
<li><u>Boolean</u> : un booléen (true/false)</li>
<li><u>Double</u> : un double</li>
<li><u>String</u> : un chaine de caractère</li>
<li><u>Int32</u> : un entier (32 bits)</li>
<li><u>Int64</u> : un long (64 bits)</li>
<li><u>ConfigurationSection</u> : un section de configuration .NET</li>
<li><u>DateTime</u> : un DateTime</li>
<li><u>TimeSpan</u> : une durée</li>
<li><u>XmlDocument</u> : un document XML</li>
<li><u>JsonObject</u> : un objet JSON (objet ou tableau)</li>
</ul>
<p>Ensuite vous pouvez définir d’autre attribut pour décrire vos settings :</p>
<ul>
<li>“<u>isRequired</u>“ : indique si le setting est obligatoire ou non (par défaut “false”)</li>
<li>“<u>description</u>” : la description du setting (texte qui sera affiché à l’utilisateur)</li>
<li>“<u>defaultValue</u>” : la valeur par défaut du setting si le setting n’est pas déclaré (ni sur le serveur, ni dans le fichier local App.config et seulement si le setting n’est pas obligatoire)</li>
<li>“<u>schemaXSD</u>” : indique le nom du fichier du schéma XSD (chemin relatif au package) que la valeur XML doit valider (seulement pour les settings de type XmlDocument ou ConfigurationSection)</li>
<li>“<u>ignoreLocalValue</u>” : si “true” alors la valeur définie dans le fichier local App.config est ignorée</li>
<li>“<u>ignoreDefaultValue</u>” : si “true” alors la valeur par défaut définie dans le manifeste est ignorée</li>
</ul>
<p>Pour les settings <em>XmlDocument</em>, <em>ConfigurationSection</em> et <em>JsonObject</em> vous pouvez également ajouter une sous-section &lt;defaultContent&gt; sur vos settings pour définir le contenu par défaut.</p>
<p>Pour plus d’information, veuillez lire l&rsquo;article sur le <a href="/concepts/package-manifest/">Package Manifest</a>.</p>
<h3>Déclarez des settings dans votre configuration locale</h3>
<p>Les settings d’un package sont déclarés au niveau du serveur Constellation mais vous pouvez également les déclarer dans le fichier App.config de votre package. Cela est très utile pendant la phase de développement.</p>
<p>Pour définir les settings dans le fichier App.config vous devez utiliser la section “constellationSettings ». Le schéma et le fonctionnement sont exactement les mêmes que pour la configuration définie sur le serveur Constellation.</p>
<p>Par exemple, dans le fichier App.config de votre package, on peut écrire :</p>
<p></p><pre class="crayon-plain-tag">&lt;constellationSettings xmlns="http://schemas.myconstellation.io/Constellation/1.8/ConstellationSettings"&gt;
  &lt;settings&gt;
    &lt;setting key="MyStringSetting" value="This is a string" /&gt;
    &lt;setting key="MyBoolSetting" value="true" /&gt;
    &lt;setting key="MyXmlDocument"&gt;
      &lt;content&gt;
        &lt;note date="09-02-2016"&gt;
          &lt;to&gt;Tove&lt;/to&gt;
          &lt;from&gt;Jani&lt;/from&gt;
          &lt;heading&gt;Reminder&lt;/heading&gt;
          &lt;body&gt;Don't forget me this weekend!&lt;/body&gt;
        &lt;/note&gt;
      &lt;/content&gt;
    &lt;/setting&gt;
    &lt;setting key="MyJsonObject"&gt;
      &lt;content&gt;
        &lt;![CDATA[
        {
          "Number": 123,
          "String" : "This is a test (local)",
          "Boolean": true
        }
        ]]&gt;
      &lt;/content&gt;
    &lt;/setting&gt;
  &lt;/settings&gt;
&lt;/constellationSettings&gt;</pre><p></p>
<p>La structure est présente par défaut dans les projets Constellation créés depuis le SDK Constellation pour Visual Studio.</p>
<p>Si un même setting (= même clé) est déclaré à la fois sur le serveur Constellation et dans le fichier local, c’est toujours la valeur définie sur le serveur qui gagne (<a href="#Resolution_des_settings">voir ci-dessous</a>).</p>
<p>Notez également que si un setting est déclaré comme obligatoire dans le manifeste, il est automatiquement supprimé du fichier App.config lorsque <a href="/constellation-platform/constellation-sdk/publier-package-visual-studio/">vous publiez ce package depuis Visual Studio</a> (que ce soit en local ou sur un serveur). Vous pouvez donc mettre vos “credentials” ou autre information personnel pour vos développements tout en sachant qu’ils seront supprimés lorsque vous publierez votre package.</p>
<h3>Résolution des settings</h3>
<p>Par défaut, la valeur d’un setting est en priorité récupérée depuis la configuration du package défini sur le serveur Constellation.</p>
<p>Si le setting n’existe pas sur le serveur (et qu’il n’est pas déclaré comme obligatoire dans le manifeste du package), le package va chercher la valeur du setting dans le fichier de configuration local App.config.</p>
<p>Si le fichier local App.config ne défini pas non plus de valeur pour ce setting ou que le manifeste demande d’ignorer cette valeur (<em>ignoreLocalValue</em> à true), le package va alors chercher la valeur par défaut de ce setting dans le manifeste (attribut “<em>defaultValue</em>” ou section”<em>defaultContent</em>”).</p>
<p>Si le manifeste du package ne défini pas de valeur par défaut pour ce setting, ou que cette valeur doit être ignorée (<em>ignoreDefaultValue</em> à true), la valeur du setting sera nulle.</p>
<p>Le processus de résolution des settings peut se résumé ainsi :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/SettingResolution-1.png"><img class="colorbox-1297"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Résolution des settings" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/SettingResolution_thumb-1.png" alt="Résolution des settings" width="420" height="236" border="0" /></a></p>
<p>Prenons un exemple pour bien comprendre. Si votre package est déclaré sur le serveur de cette façon :</p>
<p></p><pre class="crayon-plain-tag">&lt;package name="MonPackage" enable="true"&gt;
  &lt;settings&gt;
    &lt;setting key="MyStringSetting" value="Valeur du serveur" /&gt;
  &lt;/settings&gt;
&lt;/package&gt;</pre><p></p>
<p>Et que le package contient dans son fichier local App.config la section suivante :</p>
<p></p><pre class="crayon-plain-tag">&lt;constellationSettings xmlns="http://schemas.myconstellation.io/Constellation/1.8/ConstellationSettings"&gt;
  &lt;settings&gt;
    &lt;setting key="MyStringSetting" value="Valeur locale" /&gt;
    &lt;setting key="MyIntSetting" value="42" /&gt;
  &lt;/settings&gt;
&lt;/constellationSettings&gt;</pre><p></p>
<p>Avec dans son manifeste, la déclaration suivante :</p>
<p></p><pre class="crayon-plain-tag">&lt;Settings&gt;
   &lt;Setting name="MyStringSetting" type="String" description="This is a String setting" /&gt;
   &lt;Setting name="MyIntSetting" type="Int32" isRequired="false" ignoreLocalValue="true"  defaultValue="1234" /&gt;
&lt;/Settings&gt;</pre><p></p>
<p>Imaginez maintenant le code C# suivant :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.WriteInfo("My String = {0}", PackageHost.GetSettingValue("MyStringSetting"));
PackageHost.WriteInfo("My Int = {0}", PackageHost.GetSettingValue("MyIntSetting"));</pre><p></p>
<p>Si vous lancez le package en debug depuis Visual Studio :</p>
<ul>
<li>My String = “Valeur locale”</li>
<li>My Int = 123</li>
</ul>
<p>Les valeurs sont simplement lues depuis le fichier de configuration locale. Si maintenant vous supprimez le setting dans votre App.config, la valeur de MyIntSetting sera 1234, c’est à dire la valeur par défaut du manifest.</p>
<p>Maintenant si vous déployez votre package dans votre Constellation :</p>
<ul>
<li>My String = “Valeur du serveur”</li>
<li>My Int = 1234</li>
</ul>
<p>My String est bien récupéré depuis la configuration du serveur (car prioritaire), cependant “MyIntSetting” n’étant pas  déclaré sur le serveur, le package va tenter de lire cette valeur depuis la configuration locale (123) mais le manifeste demande à ignorer la valeur locale (<em>ignoreLocalValue</em>= »true » dans le manifeste) ce qui l’amène à récupérer la valeur par défaut (<em>defaultValue</em>) déclaré dans le manifeste du package. Donc MyIntSetting = 1234.</p>
<p><u>Note sur le “ignoreDefaultValue”</u></p>
<p>On peut se demander quel est l’intérêt de définir une valeur par défaut tout demandant de l’ignorer (<em>ignoreDefaultValue</em> = true).</p>
<p>En fait la valeur par défaut du manifeste peut être considérée comme un “exemple”. La <em>defaultValue</em> ou <em>defaultContent</em> est affiché à titre d’exemple à l’utilisateur lors de la configuration de son package dans la Console Constellation. Cela est surtout fort utile pour les “Setting Contents”, car ça renseigne l’utilisateur la structure du XML ou du JSON du setting.</p>
<p>Mais une valeur d’ “exemple” ne doit pas être chargée ! De ce fait on déclare un setting avec une defaultValue/defaultContent pour l’exemple et on demande à ne jamais tenir compte de cette valeur en l&rsquo;ignorant (ignoreDefaultValue = true).</p>
<p>Si on veut une valeur “par défaut”, on utilisera le fichier local App.config.</p>
<p>En clair, on peut voir les choses ainsi :</p>
<ul>
<li>Valeur déclarée dans la configuration du package au niveau du serveur Constellation = configuration de l’utilisateur</li>
<li>Valeur déclarée dans la configuration locale du package (App.config) = configuration pour le développement ou valeur par défaut</li>
<li>Valeur déclarée dans le manifeste du package (PackagInfo.xml) = exemple de configuration ou valeur par défaut</li>
</ul>
<h3>Mise à jour dynamique</h3>
<p>Les settings d&rsquo;un package sont récupérés du serveur Constellation au démarrage du package. Il est également possible de “pusher” les settings d’un package lorsque le package est en fonctionnement.</p>
<p>Il y a trois façons de “pusher” les settings sur un package :</p>
<ul>
<li>Lors du rechargement de la configuration Constellation sur le serveur</li>
<li>Lors d’un “UpdateSettings” sur un package en particulier via le hub contrôle</li>
<li>A la demande d’un package via la méthode “RequestSettings”</li>
</ul>
<p>Le hub de contrôle (control hub) expose une méthode “reloadServerConfiguration” permettant de recharger la configuration de la Constellation au niveau du serveur.  Cette méthode prend en paramètre un booléen (optionnel) indiquant si il faut ou non déployer la configuration après son rechargement sur le serveur. Par défaut ce paramètre est à “false” (la configuration est seulement rechargée).</p>
<p>Lorsque la configuration est déployée, le serveur Constellation informe toutes les sentinelles et tous les packages que la configuration a changé. Chaque package recevra la valeurs des settings déclarées dans la nouvelle configuration et chaque sentinelle se conformera à sa configuration (arrêt des packages supprimés et déploiement/démarrage des packages ajoutés).</p>
<p>Il est possible de lancer cette action depuis la Console Constellation (soit depuis la page “<a href="/constellation-platform/constellation-console/gerer-packages-avec-la-console-constellation/">Packages</a>” ou soit depuis la page “<a href="/constellation-platform/constellation-console/configuration-editor/">Configuration Editor</a>« ).</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-140.png"><img class="colorbox-1297"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Rechargement de la configuration" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-117.png" alt="Rechargement de la configuration" width="244" height="109" border="0" /></a></p>
<p align="left">L’autre possibilité, toujours sur le hub de contrôle, avec une méthode “<em>updateSettings</em>” permettant de pusher les settings du serveur vers un package en particulier. Vous retrouverez également cette action depuis la Console Constellation :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-141.png"><img class="colorbox-1297"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Mise à jour des settings d'un package" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-118.png" alt="Mise à jour des settings d'un package" width="244" height="173" border="0" /></a></p>
<p align="left">Enfin, le package lui-même peut faire la demande au serveur de lui renvoyer ses settings par la méthode suivante :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RequestSettings();</pre><p></p>
<p align="left">Dans tous les cas, le dictionnaire des settings de votre package (propriété <em>PackageHost.Settings</em>) sera mis à jour de façon de transparent (en tenant compte du processus de résolution des settings décrit ci-dessus).</p>
<p align="left">Ainsi, lorsque vous appelez les méthodes <em>GetSettingXXXX</em>, elles vous retourneront toujours les dernières valeurs des settings. C&rsquo;est pourquoi il est conseillé de ne jamais mettre en cache les valeurs des settings et de toujours invoquer les méthodes <em>GetSettingsXXX</em> pour tenir compte des modifications en temps réel.</p>
<p align="left">Vous avez également la possibilité de vous abonnez à l’événement “<em>PackageHost.SettingsUpdated</em>” pour être informé de ces mises à jour et agir en conséquence :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.SettingsUpdated += (s, e) =&gt;
{
    PackageHost.WriteInfo("Mise à jours des settings");
    PackageHost.WriteInfo("Il y a {0} setting(s)", PackageHost.Settings.Count);
};</pre><p></p>
<h3>Les groupes de Settings</h3>
<p>Vous pouvez grouper des settings (Content ou Value) dans des groupes au niveau du serveur et importer ces groupes dans les settings de vos packages ou dans d’autres groupes.</p>
<p>Par exemple, créons un groupe pour “HWMonitorSettings” qui contient le setting “Interval” :</p>
<p></p><pre class="crayon-plain-tag">&lt;settingsGroups&gt;
  &lt;group name="HWMonitorSettings"&gt;
    &lt;settings&gt;
      &lt;setting key="Interval" value="500" /&gt;
    &lt;/settings&gt;
  &lt;/group&gt;
&lt;/settingsGroups&gt;</pre><p></p>
<p>Ainsi pour chaque instance du package “HWMonitor”, vous pouvez importer le groupe “HWMonitorSettings” afin de centraliser la configuration de ce package :</p>
<p></p><pre class="crayon-plain-tag">&lt;package name="HWMonitor"&gt;
  &lt;settings&gt;
    &lt;import&gt;
      &lt;settingGroup groupName="HWMonitorSettings" /&gt;
    &lt;/import&gt;
  &lt;/settings&gt;
&lt;/package&gt;</pre><p></p>
<p>Vous pouvez importer des groupes dans des groupes sans limite.</p>
<p>C’est toujours la valeur du setting la plus proche du package qui gagne (surcharge).</p>
<p>Bien entendu, vous pouvez créer autant de groupe que vous voulez et chaque groupe peut contenir des SettingValues ou des SettingContents :</p>
<p></p><pre class="crayon-plain-tag">&lt;settingsGroups&gt;
  &lt;group name="test"&gt;
    &lt;settings&gt;
      &lt;setting key="Demo1" value="Seb" /&gt;
      &lt;setting key="Demo2" value="123" /&gt;
    &lt;/settings&gt;
  &lt;/group&gt;
  &lt;group name="test2"&gt;
    &lt;settings&gt;
      &lt;import&gt;
        &lt;settingGroup groupName="common" /&gt;
      &lt;/import&gt;
      &lt;setting key="Demo1" value="Sebastien" /&gt;
      &lt;setting key="Demo2" value="2015" /&gt;
    &lt;/settings&gt;
  &lt;/group&gt;
  &lt;group name="common"&gt;
    &lt;settings&gt;
      &lt;setting key="MyStringSetting" value="This is a string" /&gt;
      &lt;setting key="MyBoolSetting" value="true" /&gt;
      &lt;setting key="MyXmlDocument"&gt;
        &lt;content&gt;
          &lt;note date="09-02-2016"&gt;
            &lt;to&gt;Tove&lt;/to&gt;
            &lt;from&gt;Jani&lt;/from&gt;
            &lt;heading&gt;Reminder&lt;/heading&gt;
            &lt;body&gt;Don't forget me this weekend!&lt;/body&gt;
          &lt;/note&gt;
        &lt;/content&gt;
      &lt;/setting&gt;
      &lt;setting key="MyJsonObject"&gt;
        &lt;content&gt;
          &lt;![CDATA[
        {
          "Number": 123,
          "String" : "This is a test (local)",
          "Boolean": true
        }
        ]]&gt;
        &lt;/content&gt;
      &lt;/setting&gt;
    &lt;/settings&gt;
  &lt;/group&gt;
&lt;/settingsGroups&gt;</pre><p></p>
<p>Pour bien comprendre, imaginez le package suivant :</p>
<p></p><pre class="crayon-plain-tag">&lt;package name="DemoPackage"&gt;
  &lt;settings&gt;
    &lt;import&gt;
      &lt;settingGroup groupName="test" /&gt;
      &lt;settingGroup groupName="test2" /&gt;
    &lt;/import&gt;
    &lt;setting key="numberTest" value="42" /&gt;
    &lt;setting key="Demo1" value="It’me" /&gt;
  &lt;/settings&gt;
&lt;/package&gt;</pre><p></p>
<p>Ici cette instance du package “DemoPackage” contient 7 settings (sans prendre en compte les settings déclarés dans le fichier local et le manifeste) :</p>
<ul>
<li>numberTest = 42 (défini dans les settings du package)</li>
<li>Demo1 = “It’s me” (défini dans les settings du package, cette valeur écrase celle des groupes importés)</li>
<li>Demo2 = 2015 (défini par le groupe “test2”. Cette valeur écrase la valeur du groupe “test”, car le groupe “test2” est importé APRES le groupe “test”)</li>
<li>MyStringSetting, MyBoolSetting, MyXmlDocument et MyJsonObject définis par le groupe “common” (groupe importé dans le groupe “test2” lui même importé sur le package “DemoPackage”).</li>
</ul>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/settings/">Settings : paramètres de configuration de vos packages</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/settings/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Publier des StateObjects</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/pushstateobject/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/pushstateobject/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Fri, 18 Mar 2016 22:08:49 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[StateObject]]></category>
		<category><![CDATA[PushStateObject]]></category>
		<category><![CDATA[PurgeStateObject]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1304</guid>

					<description><![CDATA[<p>Publier des StateObjects Pour publier (Push) un StateObject dans Constellation vous devez invoquer la méthode “PackageHost.PushStateObject” en précisant obligatoirement le nom du StateObject et sa valeur. Par exemple, vous pouvez publier un StateObject avec en valeur n’importe quel type de</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/pushstateobject/">Publier des StateObjects</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h3>Publier des StateObjects</h3>
<p>Pour publier (Push) un StateObject dans Constellation vous devez invoquer la méthode “<em>PackageHost.PushStateObject</em>” en précisant obligatoirement le nom du StateObject et sa valeur.</p>
<p>Par exemple, vous pouvez publier un StateObject avec en valeur n’importe quel type de base :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("MyString", "OK");
PackageHost.PushStateObject("MyNumber", 123);
PackageHost.PushStateObject("MyDecimal", 123.12);
PackageHost.PushStateObject("MyBoolean", true);</pre><p></p>
<p>Vous pouvez parcourir tous les StateObjects de votre Constellation grâce au “<a href="/constellation-platform/constellation-console/stateobjects-explorer/">StateObject Explorer</a>” de la Console Constellation :<a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-142.png"><img class="colorbox-1304"  loading="lazy" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border-width: 0px;" title="StateObject Explorer" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-119.png" alt="StateObject Explorer" width="424" height="240" border="0" /></a></p>
<p>Pouvez également publier des objets avec un type anonyme :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("AnonymousObject", new { String = "test", Number = 123 });</pre><p></p>
<p>Ou encore avec des types complexes :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject&lt;MyCustomObject&gt;("MyObject",  new MyCustomObject() { String = "test", Number = 123 });</pre><p></p>
<p align="left">Depuis la Console, vous pouvez analyser en détail vos StateObjects et même les suivre en temps réel :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-143.png"><img class="colorbox-1304"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="StateObject Explorer" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-120.png" alt="StateObject Explorer" width="424" height="261" border="0" /></a></p>
<p>Vous avez également la possibilité de définir des paramètres optionnels sur cette méthode pour ajouter les métadonnées de votre StateObject ou encore définir sa durée de vie.</p>
<p>Par exemple, ici le StateObject “ShortLife” a ici une durée de vie de 20 secondes. Au delà il sera donc considéré comme expiré si il n&rsquo;est pas mis à jour après 20 secondes.</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("ShortLife", "Expire in 20 secs !!!", lifetime: 20);</pre><p></p>
<p>Ci-dessous, on publie un StateObject “Salon” en lui associant les metadatas “Id” et “Zone”.</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("Salon", monObjetZoneSalon, metadatas: new Dictionary&lt;string, object&gt; { { "Id", 42 }, { "Zone", "Salon" } });</pre><p></p>
<p><u>Note</u> : à chaque (re)démarrage de votre package, tous les StateObjects du package sont purgés de la Constellation.</p>
<h3>Décrire les types de StateObjects</h3>
<p>Il est recommandé de décrire les types complexes de vos StateObjects dans la Constellation pour activer l’auto-description (utilisé entre autre par les <a href="/constellation-platform/constellation-sdk/generateur-de-code/">générateurs de code</a> ou le <a href="/constellation-platform/constellation-console/messagecallbacks-explorer/">MessageCallback Explorer </a>de la Console).</p>
<p>Sur les objets personnalisés il suffit de décorer les classes de l’attribut [StateObject]. Par exemple pour reprendre l’exemple ci dessus, la classe du StateObject “MyObject” pourrait être défini ainsi :</p>
<p></p><pre class="crayon-plain-tag">/// &lt;summary&gt;
/// Mon StateObject complexe
/// &lt;/summary&gt;
[StateObject]
public class MyCustomObject
{
    /// &lt;summary&gt;
    /// Gets or sets the number.
    /// &lt;/summary&gt;
    public int Number { get; set; }
    /// &lt;summary&gt;
    /// Gets or sets the string.
    /// &lt;/summary&gt;
    public string String { get; set; }
    /// &lt;summary&gt;
    /// Gets or sets the date time.
    /// &lt;/summary&gt;
    public DateTime UnTypeComplexe { get; set; }
}</pre><p></p>
<p>Deux points à retenir :</p>
<ul>
<li>Marquer la classe de l’attribut [StateObject] pour l’inclure dans la description du package</li>
<li>Ajouter des commentaires de documentation XML pour décrire le StateObject et ses propriétés.</li>
</ul>
<p>Autrement si vous être amené à publier des StateObjects d’un type dont vous n’avez pas la possibilité de modifier le code pour ajouter l’attribut [StateObject] (par exemple un type d’une libraire tierce), vous pouvez sur votre classe IPackage, ajoutez l’attribut [StateObjectKnownTypes] en définissant la liste des types de StateObjects à décrire :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectKnownTypes(typeof(ThridParty.ComplexObject), typeof(AnotherThridParty.AnotherType))]
public class Program : PackageBase
{
}</pre><p></p>
<p>Vous avez également la possibilité d’ajouter à tout moment la description d’un type de StateObject via les méthodes :</p>
<ul>
<li>PackageHost.DescribeStateObjectTypes : en passant une liste de Type</li>
<li>PackageHost.DescribeStateObjectTypesFromAssembly : en passant une assembly (qui sera scannée à la recherche des classes marquées par l’attribut [StateObject])</li>
</ul>
<p>Si vous ajoutez vous-même des types de StateObjects dans la description du package via les deux méthodes ci-dessus, vous devez ensuite mettre à jour la description du package par la ligne :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.DeclarePackageDescriptor();</pre><p></p>
<h3>Supprimer des StateObjects</h3>
<p>Vous avez deux moyens de supprimer des StateObjects :</p>
<ul>
<li>PackageHost.PurgeStateObjects : pour supprimer les StateObjects de votre package</li>
<li>PackageHost.ControlManager.PurgeStateObjects : pour supprimer n’importe quel StateObject de votre Constellation en utilisant le ControlManager (nécessite une clé d’accès autorisant l’accès au hub de contrôle). <a href="/client-api/net-package-api/controlmanager/">Plus di’nformation ici</a></li>
</ul>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/pushstateobject/">Publier des StateObjects</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/pushstateobject/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>MessageCallbacks : exposez vos méthodes</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/messagecallbacks/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/messagecallbacks/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Wed, 16 Mar 2016 14:14:25 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[PackageDescriptor]]></category>
		<category><![CDATA[MessageCallback]]></category>
		<category><![CDATA[Saga]]></category>
		<category><![CDATA[Messaging]]></category>
		<category><![CDATA[MessageContent]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1343</guid>

					<description><![CDATA[<p>Avant de tout, il est recommandé de lire cet article d’introduction aux concepts de Message, Scope, MessageCallback &#38; Saga dans Constellation pour bien comprendre le principe des MessageCallbacks. Exposer des méthodes Pour exposer une méthode .NET dans Constellation, il suffit</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/messagecallbacks/">MessageCallbacks : exposez vos méthodes</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Avant de tout, il est recommandé de lire <a href="/concepts/messaging-message-scope-messagecallback-saga/">cet article d’introduction aux concepts de Message, Scope, MessageCallback &amp; Saga</a> dans Constellation pour bien comprendre le principe des MessageCallbacks.</p>
<h3>Exposer des méthodes</h3>
<p>Pour exposer une méthode .NET dans Constellation, il suffit d&rsquo;ajouter l&rsquo;attribut “MessageCallback” sur une méthode :</p>
<p></p><pre class="crayon-plain-tag">[MessageCallback]
void MyMethod()
{
    PackageHost.WriteInfo("This is a MessageCallback !");
}</pre><p></p>
<p>Cela fonctionne que votre méthode soit privée ou public, d&rsquo;instance ou statique.</p>
<p>Une méthode marquée MessageCallback peut accepter zéro, un ou plusieurs paramètres :</p>
<p></p><pre class="crayon-plain-tag">[MessageCallback]       
void MyMethodWithMultipleParameters(string input, int number, bool boolean)
{
    PackageHost.WriteInfo("Input= {0} - Number: {1} - Boolean: {2}", input, number, boolean);
}</pre><p></p>
<p>De plus chaque paramètre peut être de type simple ou complexe :</p>
<p></p><pre class="crayon-plain-tag">[MessageCallback] 
void MyMethodWithComplexParameter(UserInfo user)
{
    PackageHost.WriteInfo("User: {0} &amp; Password = {1}", user.User, user.Password); 
}</pre><p></p>
<p>Par défaut seules les méthodes marquées [MessageCallback] sur votre classe ”<a href="/client-api/net-package-api/les-bases-des-packages-net/">IPackage</a>” sont enregistrées.</p>
<p>Si vous souhaitez également enregistrer les MessageCallbacks référencés de vos autres types, vous devez appeler la méthode “RegisterMessageCallbacks” en passant en paramètre l&rsquo;instance ou le type à enregistrer :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RegisterMessageCallbacks(source);</pre><p></p>
<h3>Testez vos MessageCallbacks depuis la Console Constellation</h3>
<p>Si vous <a href="/constellation-platform/constellation-sdk/publier-package-visual-studio/">publiez votre package</a> ou <a href="/getting-started/creez-votre-premier-package-constellation-en-csharp/">démarrer votre package depuis VS</a>  avec les trois méthodes ci-dessus, vous retrouverez ces trois MessageCallbacks dans <a href="/constellation-platform/constellation-console/messagecallbacks-explorer/">l’explorateur </a>:</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-116.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="MessageCallback Explorer" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-93.png" alt="MessageCallback Explorer" width="424" height="191" border="0" /></a></p>
<p align="left">Tout y est décrit, y compris les types complexes :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-117.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Description des types" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-94.png" alt="Description des types" width="424" height="165" border="0" /></a></p>
<p align="left">Vous pourrez alors directement tester vos MC depuis la Console :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-118.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="MessageCallback Explorer" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-95.png" alt="MessageCallback Explorer" width="424" height="58" border="0" /></a></p>
<p align="left">Ce qui compte tenu du code donné en exemple ci-dessus affichera des messages dans les logs Constellation visible sur la <a href="/constellation-platform/constellation-console/console-log/">Console Log</a> :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-119.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Demo" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-96.png" alt="Demo" width="424" height="25" border="0" /></a></p>
<h3 align="left">Personnaliser le nom du MessageCallback</h3>
<p align="left">Vous pouvez également changer le nom du MessageCallback sans forcement changer le nom de votre méthode :</p>
<p></p><pre class="crayon-plain-tag">[MessageCallback(Key="Logon")]
void MyMethodWithComplexParameter(UserInfo user)
{
    PackageHost.WriteInfo("User: {0} &amp; Password = {1}", user.User, user.Password);
}</pre><p></p>
<p align="left">Ici la méthode se nomme toujours “MyMethodWithComplexParameter” dans notre code C# mais est vue dans Constellation comme le MessageCallback nommé “Logon” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-120.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="MessageCallback Explorer" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-97.png" alt="MessageCallback Explorer" width="424" height="212" border="0" /></a></p>
<h3 align="left">MessageCallback caché</h3>
<p align="left">Vous pouvez aussi exposer des MessageCallbacks que vous ne souhaitez “rendre publique”. En d&rsquo;autre mots, vous ne souhaitez pas que votre MC soit référencé dans la description de votre package.</p>
<p align="left">Exemple : vous avez un package qui envoie des messages à un groupe pour prévenir de l’arrivée d’un événement (ex: le téléphone sonne). Vous souhaitez, dans le code de votre package, déclencher une méthode lorsque cet événement se produit. Autrement dit, vous créez une méthode marquée “MessageCallback“ qui porte le même nom que le MessageKey envoyé par l’autre package (le même nom de méthode ou via la propriété Key comme vu ci-dessus).</p>
<p align="left">Ainsi, comme votre package fait partie du groupe cible, lorsque le téléphone sonne, l’autre package envoie un message au groupe, vous recevez donc le message et comme vous avez un MessageCallback du même nom, votre méthode est invoquée !</p>
<p align="left">C’est parfait à l’exception que tout le monde “voit l’existence” de cette méthode et peuvent tous l’invoquer !</p>
<p align="left">Deux solutions :</p>
<ul>
<li>
<div align="left">Cacher le MessageCallback (= ne pas le déclarer dans la description du package)</div>
</li>
<li>
<div align="left">Vérifier l’émetteur</div>
</li>
</ul>
<p align="left">Pour cacher un MC, il suffit simple de définir la propriété “IsHidden” à vrai :</p>
<p></p><pre class="crayon-plain-tag">[MessageCallback(IsHidden = true)]
void HiddenMethod()
{
    PackageHost.WriteInfo("This is a MessageCallback !");
}</pre><p></p>
<p align="left">Ainsi la méthode, n&rsquo;apparaîtra nulle part, personne ne pourra savoir que votre package “écoute” des messages dont la clé est ici “HiddenMethod”.</p>
<h3 align="left">MessageContext</h3>
<p align="left">Vous pouvez, dans le code d&rsquo;un “MessageCallback”, accéder au contexte du message reçu via la propriété “<em>MessageContext.Current</em>”.</p>
<p align="left">Le contexte du message contient notamment le MessageSender (identifié de l’émetteur du message) et le MessageScope (scope dans lequel le message a été envoyé ).</p>
<p align="left">Par exemple on pourrait écrire :</p>
<p></p><pre class="crayon-plain-tag">[MessageCallback(IsHidden = true)]
void HiddenMethod()
{
    if(MessageContext.Current.Sender.FriendlyName == "SENTINEL-DEMO/MySafePackage")
    {
        PackageHost.WriteInfo("OK j’autorise le package MySafePackage de la sentinelle SENTINEL-DEMO");
    }
    else
    {
        PackageHost.WriteWarn("Je refuse que {0} puisse invoquer cette méthode !", MessageContext.Current.Sender.FriendlyName);
    }
}</pre><p></p>
<h3>Répondre à une saga</h3>
<p>Comme nous l’avons vu dans les concepts, il est possible de répondre à un message en renvoyant un message avec le même identifiant de saga. De ce fait, un MessageCallback peut renvoyer une réponse.</p>
<p>Dans la pratique, il suffit de regarder dans le contexte du message si c’est une saga (c’est à dire que le scope porte un identifiant de saga). Si c’est une saga, vous pouvez appeler la méthode d’extension “<em>SendResponse</em>” qui se chargera pour vous d’envoyer le contenu de votre message dans un scope portant le même identifiant de saga et à destination du package émetteur.</p>
<p>De plus, pour indiquer que le MessageCallback répond aux sagas, on indique quel est le type de l’objet de réponse dans l&rsquo;attribut [MessageCallback] avec la propriété <em>ResponseType</em>.</p>
<p>Par exemple :</p>
<p></p><pre class="crayon-plain-tag">[MessageCallback(Key="Logon", ResponseType=typeof(bool))]
void MyMethodWithComplexParameter(UserInfo user)
{
    PackageHost.WriteInfo("User: {0} &amp; Password = {1}", user.User, user.Password);

    if (MessageContext.Current.IsSaga)
    {
        MessageContext.Current.SendResponse(user.User == user.Password);
    }
}</pre><p></p>
<p>Avec Constellation 1.8, la syntaxe est encore plus simple. Vous pouvez simplement écrire votre MessageCallback avec une fonction .NET, c&rsquo;est à dire une méthode qui retourne (<em>return</em>) une valeur :</p>
<p></p><pre class="crayon-plain-tag">[MessageCallback(Key="Logon")]
bool MyMethodWithComplexParameter(UserInfo user)
{
    PackageHost.WriteInfo("User: {0} &amp; Password = {1}", user.User, user.Password);

    return user.User == user.Password;
}</pre><p></p>
<p>La propriété “ResponseType” du MessageCallback est automatiquement définie avec le type de retour de la méthode. C’est l’API .NET qui se chargera d’envoyer la réponse si le message est reçu est une saga. La réponse renvoyée étant l’objet retourné par votre méthode, dans l’exemple ci-dessus un booléen.</p>
<p>Ainsi en republiant le package avec le code ci-dessus, vous constaterez dans le <a href="/constellation-platform/constellation-console/messagecallbacks-explorer/">MessageCallbacks Explorer</a> que votre MessageCallback indique son type de réponse dans le cas d&rsquo;une saga :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-121.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="MessageCallback avec saga" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-98.png" alt="MessageCallback avec saga" width="424" height="209" border="0" /></a></p>
<p align="left">Vous pourrez alors tester votre MessageCallback :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-122.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Envoyer un message dans une saga" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-99.png" alt="Envoyer un message dans une saga" width="424" height="244" border="0" /></a></p>
<p align="left">Et visualisez le message de retour, ici un booléen :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-123.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Réponse d'une saga" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-100.png" alt="Réponse d'une saga" width="424" height="438" border="0" /></a></p>
<h3>Décrire ses MessageCallbacks</h3>
<p>Pour finir il est vivement conseillé de décrire ses MessageCallbacks et les types utilisés en paramètre ou en réponse.</p>
<p>Vous avez deux possibilités :</p>
<ul>
<li>Utilisez la propriété “Description” sur l’attribut MessageCallback (mais limité aux MC et non aux types)</li>
<li>Utilisez les commentaires de documentation XML</li>
</ul>
<p>Pour gagner en productivité, je vous recommande l’extension Visual Studio <a href="http://submain.com/products/ghostdoc.aspx">GhostDoc de SubMain</a> pour générer automatiquement des commentaires de documentation XML dans votre code .NET.</p>
<p>Ainsi ajoutons des commentaires de documentation XML sur nos MessageCallbacks et sur la classe UserInfo utilisée dans nos exemples comme paramètre de notre MC :</p>
<p></p><pre class="crayon-plain-tag">/// &lt;summary&gt;
/// Methode d'identification d'un utilisateur.
/// &lt;/summary&gt;
/// &lt;param name="user"&gt;L'utilisateur à identifier.&lt;/param&gt;
/// &lt;returns&gt;&lt;c&gt;true&lt;/c&gt; si identifié&lt;/returns&gt;
[MessageCallback(Key="Logon")]
bool MyMethodWithComplexParameter(UserInfo user)
{
    PackageHost.WriteInfo("User: {0} &amp; Password = {1}", user.User, user.Password);

    return user.User == user.Password;
}

/// &lt;summary&gt;
/// Classe decrivrant un utilisateur
/// &lt;/summary&gt;
public class UserInfo
{
    /// &lt;summary&gt;
    /// Le nom d'utilisateur
    /// &lt;/summary&gt;
    public string User { get; set; }

    /// &lt;summary&gt;
    /// Le mot de passe de l'utilisateur
    /// &lt;/summary&gt;
    public string Password { get; set; }
}</pre><p></p>
<p>Si vous redéployez le package, vous observerez que l&rsquo;ensemble des MessageCallbacks et des paramètres sont décrits par vos commentaires XML :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-124.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="MessageCallback documenté" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-101.png" alt="MessageCallback documenté" width="424" height="128" border="0" /></a></p>
<h3>S’abonner et recevoir les messages d’un groupe</h3>
<p>Comme <a href="/concepts/messaging-message-scope-messagecallback-saga/">vous le savez</a>, il existe plusieurs type de scope pour recevoir un message. Par défaut votre package recevra les messages adressés au scope “All” et “Other” (si il n’est pas l’émetteur du message) mais aussi et surtout ceux destinés au scope de sa sentinelle et/ou de son package.</p>
<p>Il y a un dernier type de scope fort utile : “les groupes”. Pour recevoir des messages destinés à un groupe, il faut d&rsquo;abord s’abonner au groupe.</p>
<p>Vous avez deux manières d’ajouter votre package dans un groupe :</p>
<ul>
<li>Depuis la configuration du serveur</li>
<li>Depuis le code de votre package</li>
</ul>
<p>La première méthode se réalise dans la configuration de votre package directement dans la configuration du serveur Constellation.</p>
<p>Il suffit d’ajouter un élément “group” en indiquant les noms des groupes à rejoindre.</p>
<p></p><pre class="crayon-plain-tag">&lt;sentinel name="PO-SWARIN" credential="StandardAccess"&gt;
    &lt;packages&gt;        
        &lt;package name="HWMonitor" enable="true"&gt;&lt;/package&gt;  
        &lt;package name="ConstellationPackageConsole1" enable="true"&gt;&lt;/package&gt; 
        &lt;package name="MonPremierPackage" enable="true"&gt;
            &lt;groups&gt;
                &lt;group name="MonGroupDemo" /&gt;
            &lt;/groups&gt;
            &lt;settings&gt;
                &lt;setting key="Interval" value="2000" /&gt;
            &lt;/settings&gt;
        &lt;/package&gt;  
    &lt;/packages&gt;                
 &lt;/sentinel&gt;</pre><p></p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-149.png"><img class="colorbox-1343"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Ajout de groupe dans la configuration" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-126.png" alt="Ajout de groupe dans la configuration" width="424" height="261" border="0" /></a></p>
<p style="text-align: left;" align="center">Vous pouvez aussi utiliser<a href="/constellation-platform/constellation-console/gerer-packages-avec-la-console-constellation/#Editer_les_groupes_dun_package"> la Console Constellation pour éditer les groupes</a> de chaque package.</p>
<p align="left">Autre manière, directement dans votre code en utilisant la méthode “<em>PackageHost.SubscribeMessages</em>” (ou <em>PackageHost.UnsubscribeMessages</em>).</p>
<p align="left">Par exemple :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.SubscribeMessages("MonGroupDemo");</pre><p></p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/messagecallbacks/">MessageCallbacks : exposez vos méthodes</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/messagecallbacks/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Consommer des StateObjects</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/consommer-des-stateobjects/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/consommer-des-stateobjects/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Sun, 20 Mar 2016 14:18:48 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[StateObjectNotifier]]></category>
		<category><![CDATA[StateObjectCollectionNotifier]]></category>
		<category><![CDATA[StateObject]]></category>
		<category><![CDATA[StateObjectLink]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1474</guid>

					<description><![CDATA[<p>Chaque package, virtuel ou non, peut publier des StateObjects dans votre Constellation. Découvrons dans cet article comment intégrer les valeurs de ces StateObjets dans votre code C#. La base : le Request &#38; Subscribe de StateObjects Le hub Constellation comporte deux méthodes</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/consommer-des-stateobjects/">Consommer des StateObjects</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Chaque package, virtuel ou non, peut <a href="/client-api/net-package-api/pushstateobject/">publier des StateObjects</a> dans votre Constellation. Découvrons dans cet article comment intégrer les valeurs de ces StateObjets dans votre code C#.</p>
<h3>La base : le Request &amp; Subscribe de StateObjects</h3>
<p>Le hub Constellation comporte deux méthodes :</p>
<ul>
<li>RequestStateObjects : permettant d’interroger « à un instant T »  des StateObjects de votre Constellation</li>
<li>SubscribeStateObjects : permettant de s&rsquo;abonner aux mises à jours des StateObjects de votre Constellation</li>
</ul>
<p>Ces deux méthodes acceptent jusqu’à 4 paramètres, tous optionnels :</p>
<p></p><pre class="crayon-plain-tag">string sentinel = "*", string package = "*", string name = "*", string type = "*"</pre><p></p>
<p>Vous devez définir le ou StateObjects que vous souhaitez récupérer (<em>Request</em>) ou suivre (<em>Subscribe</em>) en appliquant des filtres. Le wildcard “*” permet de tout sélectionner, c&rsquo;est à dire de ne pas filtrer.</p>
<p>Par exemple, pour récupérer tous les StateObjects du package “MonPackage” (et peut importe le nombre d’instance du package) :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RequestStateObjects(package: "MonPackage");</pre><p></p>
<p>Si vous souhaitez tous les StateObjects du package “MonPackage” qui tourne sur une sentinelle en particulier, nommée ci-dessous « MA-SENTINELLE » :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RequestStateObjects(sentinel:"MA-SENTINELLE", package: "MonPackage");</pre><p></p>
<p>Pour cibler un StateObject en participer sur une instance d&rsquo;un package :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RequestStateObjects(sentinel: "MA-SENTINELLE", package: "MonPackage", name:"MonStateObject");</pre><p></p>
<p>Bien entendu, vous pouvez définir la combinaison que vous souhaitez !</p>
<p>Par exemple, récupérons tous les StateObjects de type “HWMonitor.HardwareList” dans notre Constellation :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RequestStateObjects(type: "HWMonitor.HardwareList");</pre><p></p>
<p>Comme on sait que ce type de StateObject ne peut être publié que par le package “HWMonitor”, on pourrait écrire :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RequestStateObjects(package: "HWMonitor", type: "HWMonitor.HardwareList");</pre><p></p>
<p>Dans les deux cas (<em>Request</em> ou <em>Subscribe</em>) les StateObjects sont reçus un par un par l’événement .NET : “PackageHost.StateObjectUpdated”.</p>
<p>Par exemple, déployons le package”<a href="/getting-started/telecharger-et-deployer-des-packages-sur-vos-sentinelles/">HWMonitor</a>” et analysons le StateObject “Hardware” via le <a href="/constellation-platform/constellation-console/stateobjects-explorer/">StateObject Explorer</a> :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-133.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Detail d'un StateObject dans la Console" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-110.png" alt="Detail d'un StateObject dans la Console" width="424" height="466" border="0" /></a></p>
<p align="left">On retrouve un StateObject de type “HWMonitor.HardwareList” qui contient une liste du hardware de la machine.</p>
<p align="left">Dans notre package nous allons récupérer tous les StateObjects de type “HWMonitor.HardwareList” (donc autant de StateObjects que nous avons d’instance de ce package dans notre Constellation).</p>
<p align="left">On pourrait écrire :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.StateObjectUpdated += (s, e) =&gt;
{
    PackageHost.WriteInfo("Hardware sur la machine {0}", e.StateObject.SentinelName);
    foreach (dynamic hw in e.StateObject.DynamicValue)
    {
        PackageHost.WriteInfo("{0} ({1})", hw.Name, hw.Identifier);
    }
};
PackageHost.RequestStateObjects(package: "HWMonitor", type: "HWMonitor.HardwareList");</pre><p></p>
<p>En testant notre package dans Constellation :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-134.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Console log" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-111.png" alt="Console log" width="424" height="92" border="0" /></a></p>
<p align="left">Notez qu&rsquo;ici nous avons utilisé la méthode « <em>RequestStateObjects</em> » c&rsquo;est à dire que nous récupérons tous les StateObjects du package HWMonitor (quelque soit la sentinelle) de type « HWMonitor.HardwareList » au moement de l&rsquo;invocation de la méthode.</p>
<p align="left">Allons un peu plus long en affichant en temps réel la consommation du CPU. Cette valeur est publiée dans le StateObject “/intelcpu/0/load/0” par le package HWMonitor.</p>
<p align="left">Pour suivre en temps réel la consommation du CPU des machines (sentinelles) sur lesquelles le package HWMonitor est déployé, on commence par s&rsquo;abonner à ce StateObject :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.SubscribeStateObjects(package: "HWMonitor", name: "/intelcpu/0/load/0");</pre><p></p>
<p>Puis dans l’événement “<em>StateObjectUpdated</em>” il faudra différencier le traitement en fonction du StateObject reçu.</p>
<p>Le code final :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.StateObjectUpdated += (s, e) =&gt;
{
    if (e.StateObject.Name == "Hardware")
    {
        PackageHost.WriteInfo("Hardware sur la machine {0}", e.StateObject.SentinelName);
        foreach (dynamic hw in e.StateObject.DynamicValue)
        {
            PackageHost.WriteInfo("{0} ({1})", hw.Name, hw.Identifier);
        }
    }
    else if (e.StateObject.Name == "/intelcpu/0/load/0")
    {
        PackageHost.WriteInfo("CPU de {0} = {1}%", e.StateObject.SentinelName, e.StateObject.DynamicValue.Value);
    }
};
PackageHost.RequestStateObjects(package: "HWMonitor", type: "HWMonitor.HardwareList");
PackageHost.SubscribeStateObjects(package: "HWMonitor", name: "/intelcpu/0/load/0");</pre><p></p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-135.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Console log" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-112.png" alt="Console log" width="424" height="116" border="0" /></a></p>
<p style="text-align: left;" align="center">Notez qu&rsquo;ici nous avons utilisé la méthode « <em>SubscribeStateObjects</em> » c&rsquo;est à dire que nous nous abonnons à tous les StateObjects nommés “/intelcpu/0/load/0” du package HWMonitor (quelque soit la sentinelle). C&rsquo;est un abonnement, donc à chaque modification des StateObjects respectant notre filtre, nous recevrons dans notre code les nouvelles valeurs des StateObjects.</p>
<p style="text-align: left;" align="center">Cependant, si les StateObjects changent peu fréquemment, nous n&rsquo;obtenons rien immédiatement. C&rsquo;est pourquoi il est parfois nécessaire de faire un <em>Request</em> suivi d&rsquo;un <em>Subscribe</em> pour obtenir la version actuelle du/des StateObject(s) et s&rsquo;abonner aux mises à jour futures.</p>
<h3>Les StateObjectLink</h3>
<p>Pour simplifier l’exploitation des StateObjects dans votre code, l&rsquo;API Constellation introduit la notion de “StateObjectLink”.</p>
<p>Avec l&rsquo;API.NET, il vous suffit simplement de déclarer une propriété .NET dans votre code que vous allez décorer avec l’attribut [<strong>StateObjectLink</strong>].</p>
<p>Votre propriété .NET peut être privée ou publique, d’instance ou statique. De plus le type de votre propriété .NET peut être :</p>
<ul>
<li>Un type de base</li>
<li>Un type complexe</li>
<li>Un dynamic</li>
<li>Un StateObject</li>
<li>Un StateObjectNotifier</li>
<li>Un StateObjectCollectionNotifier</li>
</ul>
<p>Par défaut seules les propriétés .NET <u>de l’instance de votre package</u> (IPackage) sont enregistrées. Si dans votre package vous instanciez des classes contenants des StateObjectsLink vous devez appeler la méthode “<em>PackageHost.RegisterStateObjectLinks</em>” en passant l’instance de votre classe pour l’enregistrement de ses StateObjectLinks (autrement les propriétés resteront nulles).</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RegisterStateObjectLinks(monObjet);</pre><p></p>
<h4>Lier la valeur d&rsquo;un StateObject à une propriété NET</h4>
<p>Prenons l’exemple du StateObject “MyNumber” que vous avons <a href="/client-api/net-package-api/pushstateobject/">publié dans cet article</a> par la ligne :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject("MyNumber", 123);</pre><p></p>
<p>Pour qu’un autre package puisse l’inclure dans son code, on peut simplement écrire :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink(Name = "MyNumber")]
public int MyNumber { get; set; }</pre><p></p>
<p>Ici la propriété est de type “int” (car la valeur du StateObject est un int) et est liée à la valeur de ce StateObject.</p>
<p>Dès que le StateObject est mis à jour (<em>PushStateObject</em>) par le package qui la produit, votre propriété est également mise à jour de sorte que vous ayez toujours la dernière valeur du StateObject dans votre propriété.</p>
<p>Bien entendu la valeur du StateObject doit être compatible avec le type de la propriété liée (même type ou cast implicite possible), sinon la valeur du StateObject lié ne sera jamais affecté à votre propriété.</p>
<p>Vous pouvez également utiliser des types complexes. Par exemple prenons l’exemple déjà cité ci-dessus :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.PushStateObject&lt;MyCustomObject&gt;("MyObject",  new MyCustomObject() { String = "test", Number = 123 });</pre><p></p>
<p>Pour créer le StateObjectLink depuis un autre package, on pourra écrire :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink(Name = "MyObject")]
public MyCustomObject MyObject { get; set; }</pre><p></p>
<p>La propriété “MyObject” sera bien du type “MyCustomObject” et sera lié au StateObject nommé “MyObject”.</p>
<p>Cela sous entend que votre package ait la même définition du type “MyCustomObject” : soit en recopiant la classe ou soit en partageant une assembly commune. Notez toutefois que, comme Constellation connait la description des types utilisés par les StateObjects ou MessagesCallbacks, il est possible de générer le code (<a href="#Generer_du_code">lire ici</a>).</p>
<p>Maintenant si le StateObject est un type anonyme ou que vous n’avez pas la définition du type dans votre code, vous pouvez utiliser le type “dynamic”.</p>
<p>On pourrait alors écrire :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink(Name = "MyObject")]
public dynamic MyObject { get; set; }</pre><p></p>
<p>Pour terminer reprenons notre StateObject “Hardware” publié par le package HWMonitor :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink(Package = "HWMonitor", Type = "HWMonitor.HardwareList")]
private dynamic Hardware { get; set; }</pre><p></p>
<p>Et à tout moment dans votre code pour pourrez itérer sur cette liste :</p>
<p></p><pre class="crayon-plain-tag">foreach (dynamic hw in this.Hardware)
{
    PackageHost.WriteInfo("{0} ({1})", hw.Name, hw.Identifier);
}</pre><p></p>
<p>On pourrait reprendre également l’exemple du CPU mais nous aurions un problème : comment savoir quand le StateObject à été mis à jour pour afficher la nouvelle valeur ? C’est que nous verrons avec le <a href="#StateObjectNotifier_notification_de_mise_a_jour">StateObjectNotifier ci-dessous</a>.</p>
<h4>L’unicité des liens</h4>
<p>Dans les exemples ci-dessus, nous lions des propriétés .NET aux StateObjects de votre Constellation en utilisant seulement leurs noms (Name). Or comme vous le savez il peut y avoir, dans votre Constellation, plusieurs packages sur plusieurs sentinelles qui publient des StateObjects avec le même nom !</p>
<p>De ce fait, je peux par exemple écrire :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink(Package = "MonPackage", Name = "MyObject")]
public dynamic MyObject { get; set; }</pre><p></p>
<p>Ici, je crée un lien entre cette propriété .NET et le<strong>s</strong> StateObjects nommés “MyObject” et publiés par le package “MonPackage”. Mais je ne sais pas combien d’instance du package “MonPackage” je trouverai dans ma Constellation (je pourrais déployer ce package sur toutes mes sentinelles).</p>
<p>L’unicité ne peut se faire qu&rsquo;avec le triplet : Nom de la sentinelle + Nom du package + Nom du StateObject.</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink(Sentinel = "MON-PC", Package = "MonPackage", Name = "MyObject")]
public dynamic MyObject { get; set; }</pre><p></p>
<p>Dans ce dernier cas j’ai la garantie de lier cette propriété .NET à un et un seul StateObject : celui nommé “MyObject” publié par le package “MonPackage” et déployé sur la sentinelle “MON-PC”.</p>
<p>Dans le cas où votre lien correspond à plusieurs StateObjects, chaque mise à jours de l’un d’entre eux est affectée à la propriété.</p>
<p>Ainsi si vous avez un package nommé “MonPackage” qui tourne sur deux sentinelles (disons “PC1” et “PC2”), votre propriété “MyObject” définie ci-dessous sera tantôt liée à la valeur du StateObject publié par le package sur PC1 tantôt sur l’instance de PC2.</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink(Package = "MonPackage", Name = "MyObject")]
public dynamic MyObject { get; set; }</pre><p></p>
<p>Soyez donc vigilant aux liens que vous créez, ou sinon utilisez les StateObject<strong>Collection</strong>Notifier comme nous le verrons <a href="#StateObjectCollectionNotifier_collection_de_StateObjectNotifier">dans la suite de cet article</a>.</p>
<h4>Lier le StateObject entier à une propriété .NET</h4>
<p>Jusqu’à présent nous avons lier des propriétés .NET avec <u>les valeurs</u> de StateObjects. Une fois la liaison établie, vous pouvez utiliser ces propriétés pour accéder à la valeur des StateObjects mais non au StateObject lui même.</p>
<p>Le StateObject contient à la fois la valeur du StateObject mais également les propriétés du StateObject comme son nom, le couple sentinelle/package qui l’a publié, sa date de publication, sa durée de vie (lifetime), son type ou encore des métadonnées (dictionnaire de string/object).</p>
<p>Pour cela, il suffit simplement de créer une propriété de type “StateObject”.</p>
<p>Par exemple pour le StateObject “Hardware” du package “HWMonitor” de la sentinelle “MON-PC” (pour n’avoir qu’une seule valeur), on peut écrire :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink("MON-PC", "HWMonitor", "Hardware")]
private StateObject Hardware { get; set; }</pre><p></p>
<p>Vous aurez ensuite accès aux différentes propriétés de votre StateObject :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb1.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Un StateObject" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb1_thumb.png" alt="Un StateObject" width="424" height="272" border="0" /></a></p>
<p align="left">La valeur du StateObject peut être obtenue par la propriété “Value” ou “DynamicValue”.</p>
<p align="left">La propriété “DynamicValue” retourne la “Value” en tant qu’objet dynamique. Dans le cas d’un objet complexe, la “Value” sera de type JObject ou JArray.</p>
<p align="left">Il est donc conseillé de travailler directement avec la “DynamicValue” pour résoudre dynamiquement la valeur de votre StateObject.</p>
<p align="left">Vous disposez également d’une méthode GetValue&lt;T&gt; (ou son équivalent <em>TryGetValue</em>) qui se chargera de convertir votre valeur de StateObject en T .</p>
<h4>StateObjectNotifier : être notifié des mises à jour des StateObjects liés</h4>
<p>Jusqu’à présent nous lions des propriétés .NET avec la valeur d’un StateObject ou le StateObject lui même.</p>
<p>Dans les deux cas, vous pouvez accéder à tout moment à la dernière version de votre StateObject (ou sa valeur) publié dans votre Constellation car un <em>StateObjectLink</em> réalise implicitement un <em>RequestStateObjects</em> à l’initialisation de la propriété puis s’abonne aux StateObjects (<em>SubscribeStateObject</em>) pour mettre à jour en temps réel votre propriété dès que le ou les StateObjects liés sont mis à jour.</p>
<p>Seulement vous ne savez pas “quand” vos StateObject liés sont mis à jour, autrement dit quand est-ce que vos propriétés .NET sont mises à jour.</p>
<p>Pour cela il existe les <em>StateObjectNotifiers</em>. Le principe est simple, vous devez simplement créer une propriété liés de type StateObjectNotifier.</p>
<p>Reprenons le StateObject du CPU publié par le package HWMonitor sur “MON-PC” :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink("MON-PC", "HWMonitor", "/intelcpu/0/load/0")]
private StateObjectNotifier CPU { get; set; }</pre><p></p>
<p>La classe StateObjectNotifier un container de StateObject. Elle comporte une propriété “Value” dans laquelle est contenu le StateObject.</p>
<p></p><pre class="crayon-plain-tag">StateObject stateObjectActuel = this.CPU.Value;</pre><p></p>
<p>On peut donc afficher la consommation à instant T :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.WriteInfo("CPU = {0}%", this.CPU.Value.DynamicValue.Value);</pre><p></p>
<p>Pour détailler :</p>
<ul>
<li>this (l’instance courante)</li>
<li>.CPU (le StateObjectNofitier lié au StateObject de notre CPU)</li>
<li>.Value (l’objet StateObject en question)</li>
<li>.DynamicValue (la valeur du StateObject sous forme dynamique)</li>
<li>.Value (la propriété de l’objet publié par le package HWMonitor)</li>
</ul>
<p>Notez que le StateObjectNotifier comporte une propriété “DynamicValue” qui retourne la “DynamicValue” du StateObject (en clair : StateObjectNotifier.DynamicValue = StateObjectNotifier.Value.DynamicValue).</p>
<p>On peut donc simplifier  le code par :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.WriteInfo("CPU = {0}%", this.CPU.DynamicValue.Value);</pre><p></p>
<p>Lorsque le (ou les) StateObjets liés sont mis à jour, l’instance du StateObjectNotifier reste inchangée, c’est seulement sa propriété Value qui est mis à jour. De ce fait il est possible de s’abonner à des événements sur un StateObjectNotifier.</p>
<p>A ce sujet, le StateObjectNotifier implémente l’interface bien connue en .NET et notamment dans le monde WPF : “<em>INotifyPropertyChanged</em>”.</p>
<p>De ce fait, un StateObjectNotifier comporte un événement “PropertyChanged” qui vous informera lorsque le StateObject change.</p>
<p></p><pre class="crayon-plain-tag">this.CPU.PropertyChanged += (s, e) =&gt;
{
    PackageHost.WriteInfo("Le StateObject a été mis à jour");
    PackageHost.WriteInfo("CPU = {0}%", this.CPU.DynamicValue.Value);
};</pre><p></p>
<p>Très pratique notamment pour rafraichir une interface graphique WPF (binding WPF).</p>
<p>De plus le <em>StateObjectNotifier </em>comporte un 2ème événement qui se déclenche lui aussi à la mise à jour du StateObject : le “<em>ValueChanged</em>”.</p>
<p>La différence avec le <em>PropertyChanged</em> réside dans l’EventArgs passé lorsque l’événement se déclenche :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb5.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="ValueChanged d'un StateObjectNotifier" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb5_thumb.png" alt="ValueChanged d'un StateObjectNotifier" width="424" height="179" border="0" /></a></p>
<p align="left">Le “<em>StateObjectChangedEventArgs</em>” fourni trois propriétés :</p>
<ul>
<li>
<div align="left">NewState : la nouvelle version du StateObject</div>
</li>
<li>
<div align="left">OldState : l’ancienne version du StateObject</div>
</li>
<li>
<div align="left">IsNew : un booléen qui indique si c’est un “nouveau” StateObject (c’est à dire que OldState est null)</div>
</li>
</ul>
<p align="left">On peut donc comparer l’évolution du StateObject</p>
<p></p><pre class="crayon-plain-tag">this.CPU.ValueChanged += (s, e) =&gt;
{
    if (e.IsNew)
    {
         PackageHost.WriteInfo("CPU = {0}%", e.NewState.DynamicValue.Value);
    }
    else
    {
         double difference = (double)e.NewState.DynamicValue.Value - (double)e.OldState.DynamicValue.Value;
         PackageHost.WriteInfo("CPU = {0}% - TENDANDE: ", e.NewState.DynamicValue.Value, difference &gt; 0 ? "A LA HAUSSE" : "A LA BAISSE");
    }
};</pre><p></p>
<h4>StateObjectLink et Notifier personnalisés</h4>
<p>Notez que vous pouvez créer vos propres attributs “StateObjectLink” personnalisés en héritant de la classe “StateObjectLinkAttribute”.</p>
<p>De la même façon vous pouvez également créer vos propres StateObjectNotifier en créant simplement une classe qui hérite de “StateObjectNotifier”.</p>
<h4>StateObjectCollectionNotifier : collection de StateObjectNotifier</h4>
<p>Comme nous l’avons vu plus haut, si un StateObjectLink peut lier plusieurs StateObjects dans la même propriété .NET. Chaque mise à jour d’un des StateObjects “remplace” le précèdent !</p>
<p>Pour pouvoir lier plusieurs StateObjects dans une seule propriété .NET, nous pouvons utiliser les <em>StateObjectCollectionNotifiers</em>. La classe <em>StateObjectCollectionNotifier</em> est une <a href="https://msdn.microsoft.com/fr-fr/library/ms668604(v=vs.110).aspx">ObservableCollection</a> de StateObjectNotifier.</p>
<p>Prenons cet exemple :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink("HWMonitor", "/intelcpu/0/load/0")]
public StateObjectCollectionNotifier CPUs { get; set; }</pre><p></p>
<p>Nous utilisons le constructeur du StateObjectLink où le 1er argument est le nom du package et le 2ème le nom du StateObject.</p>
<p>Pour faciliter la compréhension, on peut également utiliser les paramètres nommés :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink(Package = "HWMonitor", Name = "/intelcpu/0/load/0")]
public StateObjectCollectionNotifier CPUs { get; set; }</pre><p></p>
<p>Nous créons donc un lien vers le StateObjects “/intelcpu/0/load/0” du package HWMonitor (celui qui correspond à la consommation du CPU).</p>
<p>On aura donc un StateObject par sentinelle où ce package est déployé.</p>
<p>Par exemple, dans ma Constellation on trouve 9 StateObjects qui correspond, un par machine (sentinelle) :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb7.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="StateObject Explorer" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb7_thumb.png" alt="StateObject Explorer" width="424" height="251" border="0" /></a></p>
<p align="left">Comme il s’agit d’une collection de StateObjectNotifier, vous pouvez itérer pour chaque StateObject afin d’afficher par exemple le CPU de chaque sentinelle où le package “HWMonitor” est déployé :</p>
<p></p><pre class="crayon-plain-tag">foreach (StateObjectNotifier stateobject in this.CPUs)
{
     PackageHost.WriteInfo("CPU sur {0} = {1}%", stateobject.Value.SentinelName, stateobject.DynamicValue.Value);
}</pre><p></p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb10.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Console log" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb10_thumb.png" alt="Console log" width="424" height="104" border="0" /></a></p>
<p>Tout comme le StateObjectNotifier, la classe StateObjectCollectionNotifier comporte aussi l’évènement ValueChanged :</p>
<p></p><pre class="crayon-plain-tag">this.CPUs.ValueChanged += (s, e) =&gt;
{
     PackageHost.WriteInfo("CPU sur {0} = {1}%", e.NewState.SentinelName, e.NewState.DynamicValue.Value);
};</pre><p></p>
<h3>Générer du code</h3>
<p>Le principe est le même que <a href="/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/#Generateur_de_code">la génération de code pour les MessageCallbacks</a>.</p>
<p>Ouvrez le générateur de code (clic-droit sur votre projet dans Visual Studio &gt; Constellation &gt; Generate Code) et sélectionnez votre Constellation puis cliquez sur “Discover”. Sélectionnez ensuite les packages que vous souhaitez ajouter dans votre code et cliquez sur “Generate”.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-144.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Générateur de code" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-121.png" alt="Générateur de code" width="221" height="244" border="0" /></a></p>
<p align="left">Comme pour les MessageCallbacks, vous devez ajouter le namespace correspondant aux “StateObjects” du package que vous souhaitez manipuler.</p>
<p align="left">Par exemple pour inclure les StateObjects du package “MonPremierPackage” :</p>
<p></p><pre class="crayon-plain-tag">using ConstellationPackageConsole1.MonPremierPackage.StateObjects;</pre><p></p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-145.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Using du code généré par package" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-122.png" alt="Using du code généré par package" width="424" height="36" border="0" /></a></p>
<p align="left">Ensuite créez votre propriété .NET liée à un StateObject mais en utilisant le StateObjectLink généré par package.</p>
<p align="left">Exemple, pour les StateObjects du package “MonPremierPackage”, vous avez la classe “MonPremierPackageStateObjectLink”.</p>
<p align="left">Inutile de définir le “PackageName” sur ce StateObjectLink par il est nativement fixé par cet attribut personnalisé.</p>
<p align="left">Pour le nom du StateObject, vous pouvez utiliser l’énumération MonPremierPackageStateObjectNames générée par le générateur de code :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-146.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="StateObjectLink généré par package" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-123.png" alt="StateObjectLink généré par package" width="424" height="65" border="0" /></a></p>
<p align="left">Par exemple, pour lier notre propriété “MonObject” au StateObject “MyObject” publié par le “MonPremierPackage” (<a href="/client-api/net-package-api/stateobjects/#Publiez_des_StateObjects">revoir l’exemple ici</a>), on peut écrire :</p>
<p></p><pre class="crayon-plain-tag">[MonPremierPackageStateObjectLink(MonPremierPackageStateObjectNames.MyObject)]
public StateObjectNotifier MonObject { get; set; }</pre><p></p>
<p align="left">Ensuite, sur chaque StateObject ou StateObjectNotifier, vous trouverez des méthodes d’extension permettant de convertir la valeur du StateObject dans le type généré dans votre code à partir de la description du package :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-147.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Méthode d'extension générées sur les StateObject &amp; StateObjectLink" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-124.png" alt="Méthode d'extension générées sur les StateObject &amp; StateObjectLink" width="424" height="214" border="0" /></a></p>
<p align="left">Par exemple, pour convertir votre StateObjectNotifier en “MyCustomObject”, le type du StateObject défini dans le package “MonPremierPackage” :</p>
<p></p><pre class="crayon-plain-tag">MyCustomObject myObject = this.MonObject.AsMonPremierPackageMyCustomObject();</pre><p></p>
<p align="left">Chaque type de StateObject décrit dans la Constellation est reproduit par le générateur de code dans votre projet.</p>
<p align="left">Vous retrouvez alors toutes les propriétés du StateObject proprement typées et documentées :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-148.png"><img class="colorbox-1474"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Classes générées" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-125.png" alt="Classes générées" width="424" height="86" border="0" /></a></p>
<p style="text-align: left;" align="center">N’hésitez pas <a href="/constellation-platform/constellation-sdk/generateur-de-code/">lire cet article très complet sur la génération de code C#</a> dans Constellation.</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/consommer-des-stateobjects/">Consommer des StateObjects</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/consommer-des-stateobjects/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Envoyer des messages &#038; Invoquer des MessageCallbacks</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Wed, 16 Mar 2016 16:41:17 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[MessageScope]]></category>
		<category><![CDATA[SendMessage]]></category>
		<category><![CDATA[Messaging]]></category>
		<category><![CDATA[MessageContext]]></category>
		<category><![CDATA[Proxy]]></category>
		<category><![CDATA[SendMessageProxy]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1379</guid>

					<description><![CDATA[<p>Avant de suivre la lecture de cet article vous devez avoir compris les concepts de base des MessageCallbacks. Créer un scope Un message Constellation est envoyé à un Scope qui représente le ou les destinataires du message. Un scope Constellation est</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/">Envoyer des messages &#038; Invoquer des MessageCallbacks</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Avant de suivre la lecture de cet article vous devez avoir compris les <a href="/concepts/messaging-message-scope-messagecallback-saga/">concepts de base</a> des MessageCallbacks.</p>
<h3>Créer un scope</h3>
<p>Un message Constellation est envoyé à un Scope qui représente le ou les destinataires du message.</p>
<p>Un scope Constellation est décrit dans l&rsquo;API.NET par la classe “MessageScope”. Vous pouvez créer un scope avec la méthode statique “Create”.</p>
<p>Par exemple pour créer un scope vers les instances du package “MonPackage” de votre Constellation  :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create("MonPackage")</pre><p></p>
<p>Par défaut le type de scope est “Package”. La ligne ci-dessus est donc identique à la ligne :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create(MessageScope.ScopeType.Package, "MonPackage")</pre><p></p>
<p>Si vous souhaitez cibler une instance d’un package en particulier, vous devez spécifier la sentinelle sur laquelle est hébergée le package que vous ciblez :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create("MaSentinelle/MonPackage")</pre><p></p>
<p>Si vous souhaitez cibler plusieurs packages :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create(MessageScope.ScopeType.Package, "MonPackage", "MonAutrePackage", "EncoreUnAutrePackage")</pre><p></p>
<p>Bien entendu vous pouvez donner le nom d’une instance en particulier (sentinelle + package) ou juste le nom du package :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create(MessageScope.ScopeType.Package, "MaSentinelle/MonPackage", "MonAutrePackage")</pre><p></p>
<p>Le scope “Sentinel” permet de cibler tous les packages sur une sentinelle donnée. Vous pouvez définir autant de sentinelle que vous souhaitez :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create(MessageScope.ScopeType.Sentinel, "MaSentinelle", "MonAutreSentinelle")</pre><p></p>
<p>Le scope “Group” permet de cibler les packages qui <a href="/client-api/net-package-api/messagecallbacks/#Sabonner_et_recevoir_les_messages_dun_groupe">sont abonnés à un groupe</a>. Ici créons un scope pour tous les packages membre du groupe “GroupA” :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create(MessageScope.ScopeType.Group, "GroupA")</pre><p></p>
<p>Vous pouvez bien sûr définir autant de groupe dans un scope que vous souhaitez :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create(MessageScope.ScopeType.Group, "GroupA", "GroupB", "GroupC")</pre><p></p>
<p>Enfin vous pouvez créer des scopes pour cibler tous les clients connectés dans votre Constellation (All) ou tout le monde sauf l’émetteur (Others). Dans ces deux cas, il n’y a pas d’argument à spécifier :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create(MessageScope.ScopeType.Others)</pre><p></p>
<h3>Envoyer un message avec le SendMessage</h3>
<p>La méthode de base pour envoyer un message dans Constellation est “<em>PackageHost.SendMessage</em>”.</p>
<p>Vous devez définir trois arguments pour envoyer un message :</p>
<ul>
<li>Le scope (les destinataires de votre message)</li>
<li>La clé du message (MessageKey)</li>
<li>Le contenu du message (Data)</li>
</ul>
<p>Comme vous le savez, on se sert des messages pour invoquer des méthodes d’autre packages. La clé du message est en fait le nom de la méthode (= le MessageCallback) à invoquer.</p>
<p>Prenons pour exemple les MessageCallbacks définis dans <a href="/client-api/net-package-api/messagecallbacks/">l’article précédent</a>.</p>
<p>Pour invoquer le MessageCallback “MyMethod” sans paramètre (Data = null) on pourrait écrire :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.SendMessage(MessageScope.Create("MonPackage"), "MyMethod", null);</pre><p></p>
<p>Pour une méthode avec plusieurs paramètres, ils faut les encapsuler dans un tableau d’objet :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.SendMessage(MessageScope.Create("MonPackage"), "MyMethodWithMultipleParameters", new object[] { "Un String", 123, true });</pre><p></p>
<p>Cela fonctionne aussi avec des objets complexes. Par exemple pour invoquer notre MessageCallback “Logon” :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.SendMessage(MessageScope.Create("MonPackage"), "Logon", new { User = "seb", Password = "seb" });</pre><p></p>
<p>Avec l’API .NET on ne se sert généralement jamais de cette méthode “SendMessage” car vous avez à disposition le “SendMessageProxy” que nous allons découvrir ci-dessous.</p>
<h3>Envoyer un message avec un proxy dynamique</h3>
<p>La classe “SendMessageProxy” est un objet dynamique qui permet d’envoyer un message de la même manière que vous appelez une méthode en programmation .NET.</p>
<p>Pour créer un proxy dynamique vous devez au préalable avoir créer votre scope :</p>
<p></p><pre class="crayon-plain-tag">MessageScope scope = MessageScope.Create("MonPremierPackage");
dynamic proxy = new SendMessageProxy(scope);</pre><p></p>
<p>Pour simplifier le code, vous pouvez utiliser la méthode d’extension “GetProxy()” sur le MessageScope :</p>
<p></p><pre class="crayon-plain-tag">MessageScope scope = MessageScope.Create("MonPremierPackage");
dynamic proxy = scope.GetProxy();</pre><p></p>
<p>Attention, pour accéder aux méthodes d’extension du <em>MessageScope</em> vous devez ajouter le namespace “Constellation.Package” :</p>
<p></p><pre class="crayon-plain-tag">using Constellation.Package;</pre><p></p>
<p>Une fois le proxy  dynamique créé il suffit d’appeler &lsquo;”fictivement” une méthode pour envoyer le message.</p>
<p>Par exemple pour envoyer le message “MyMethod” avec un contenu du vide, on écrira simplement :</p>
<p></p><pre class="crayon-plain-tag">proxy.MyMethod();</pre><p></p>
<p>Si votre message doit contenir plusieurs arguments :</p>
<p></p><pre class="crayon-plain-tag">proxy.MyMethodWithMultipleParameters("Un String", 123, true);</pre><p></p>
<p>Ou encore avec notre MessageCallback “Logon” qui prend en argument un objet complexe :</p>
<p></p><pre class="crayon-plain-tag">proxy.Logon(new { User = "seb", Password = "seb" })</pre><p></p>
<p>Ainsi vous pouvez invoquer un MessageCallback de n’importe quel package de la même façon que vous invoquerez une méthode de votre code .NET. La méthode invoquée est la clé du message et les arguments sont inclus dans le contenu du message.</p>
<p>Bien sûr avec la méthode d’extension vous pouvez faire tout cela en une ligne de code : créer le scope, récupérer le proxy dynamique et envoyer le message :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create("MonPremierPackage").GetProxy().Logon(new { User = "seb", Password = "seb" });</pre><p></p>
<p>Vous avez également à disposition la méthode “<em>CreateMessageProxy</em>” sur le PackageHost. Cette méthode permet de créer un scope et vous retourne le proxy dynamique.</p>
<p>Les deux lignes ci-dessous sont donc identiques :</p>
<p></p><pre class="crayon-plain-tag">dynamic proxy = MessageScope.Create("MonPremierPackage").GetProxy();
dynamic proxy = PackageHost.CreateMessageProxy("MonPremierPackage");</pre><p></p>
<p>De ce fait, pour invoquer nos trois MessageCallbacks, on peut écrire simplement :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.CreateMessageProxy("MonPremierPackage").MyMethod();
PackageHost.CreateMessageProxy("MonPremierPackage").MyMethodWithMultipleParameters("Un String", 123, true);
PackageHost.CreateMessageProxy("MonPremierPackage").Logon(new { User = "seb", Password = "seb" });</pre><p></p>
<p>Tout comme la méthode “MessageScope.Create”, le “CreateMessageProxy” peut créer tout type de scope.</p>
<p>Par exemple, invoquons le MessageCallbacks “MyMessageCallback” avec deux arguments (un string et un datetime) sur tous les packages du groupe A et B de notre Constellation :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.CreateMessageProxy(MessageScope.ScopeType.Group, "GroupA", "GroupB").MyMessageCallback("Mon Argument", DateTime.Now);</pre><p></p>
<h3>Invoquer un MessageCallback avec réponse – Utilisation des Sagas</h3>
<p>Lorsque vous envoyez un message dans un scope vous pouvez ajouter un identifiant de Saga sur ce scope. Cela indiquera au destinataire que vous souhaitez une réponse en retour (<a href="/client-api/net-package-api/messagecallbacks/#Repondre_a_une_saga">plus d’info</a>).</p>
<p>L’identifiant de Saga doit être aléatoire et unique. Vous avez une méthode d’extension “WithSaga” sur un MessageScope vous permettant de définir l’identifiant de la saga (SagaId) avec un GUID :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create("MonPremierPackage").WithSaga();</pre><p></p>
<p>La méthode d’extension vous retourne le MessageScope, vous pouvez donc directement récupérer le proxy dynamique et envoyer votre message :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create("MonPremierPackage").WithSaga().GetProxy().Logon(new { User = "seb", Password = "seb" });</pre><p></p>
<p>Pour la réception des réponses, vous avez une méthode “RegisterSagaResponseCallback” qui permet d’enregistrer la fonction à invoquer lors de la réponse :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.RegisterSagaResponseCallback(sagaId, (reponse) =&gt;
{
      // Response pour la saga "sagaId"
});</pre><p></p>
<p>Pour simplifier le code, vous avez une méthode d’extension “OnSagaResponse” sur un MessageScope qui permet à la fois d’ajouter un identifiant de saga sur le scope (WithSaga) et d’enregistrer la fonction de retour.</p>
<p>De ce fait, vous pouvez en une seule ligne créer votre scope avec saga, définir le code qui sera invoqué à la réception la réponse, récupérer le proxy dynamique et invoquer votre MessageCallback avec ses paramètres :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create("MonPremierPackage").OnSagaResponse((response) =&gt;
{
    // Reponse de la saga
}).GetProxy().Logon(new { User = "seb", Password = "seb" });</pre><p></p>
<p>Comme pour les MessageCallbacks, la méthode de réception d’une réponse d’une saga est invoquée dans un thread dédié dans lequel vous avez accès au contexte du message via la propriété “MessageContext.Current”.</p>
<p>Vous pouvez donc savoir quel est le package qui vous a répondu (<em>Sender</em>) ou encore quel était l’identifiant de la saga utilisée.</p>
<p>De plus, notez que la méthode <em>OnSagaResponse</em> est générique afin de pouvoir définir le type de retour (le cast sera réalisé automatiquement).</p>
<p>On peut donc écrire :</p>
<p></p><pre class="crayon-plain-tag">MessageScope.Create("MonPremierPackage").OnSagaResponse&lt;bool&gt;((response) =&gt;
{
    PackageHost.WriteInfo("Reponse de {0} pour la saga {1}",                 
        MessageContext.Current.SagaId,
        MessageContext.Current.Sender.FriendlyName);

    PackageHost.WriteInfo(response ? "OK" : "DENIED");

}).GetProxy().Logon(new { User = "seb", Password = "seb" });</pre><p></p>
<p>Notez également que si votre scope cible plusieurs packages (ex : si “MonPremierPackage” est déployé sur plusieurs sentinelles), votre fonction de traitement de la réponse sera invoquée plusieurs fois, pour chaque package qui répondra (car l’identifiant de Saga est le même).</p>
<h3>Utiliser les “Tasks” (awaitable) pour attendre la réponse d’une saga</h3>
<p>C’est une nouveauté de la 1.8 de Constellation. Pour bien comprendre, résumons ce que l’on connait déjà !</p>
<p>Lorsque vous invoquez une méthode sur le proxy dynamique, celui-ci envoie un message dans Constellation où la clé du message est le nom de la méthode invoquée et le contenu du message sont les paramètres de la méthode invoquée.</p>
<p>Ces deux lignes sont donc identiques :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.SendMessage(MessageScope.Create("MonPackage"), "Logon", new { User = "seb", Password = "seb" });
MessageScope.Create("MonPremierPackage").GetProxy().Logon(new { User = "seb", Password = "seb" });</pre><p></p>
<p>Sur la 2ème ligne, vous invoquez dynamiquement la méthode “Logon” sur le <em>SendMessageProxy</em> (créé par la méthode GetProxy()) pour envoyer (<em>PackageHost.SendMessage</em>) le message “Logon”.</p>
<p>Ces deux lignes font donc bien la même chose. Dans les deux cas le message est envoyé dans un scope qui ne porte pas de “SagaId” (donc aucune réponse n’est attendue).</p>
<p>Je rappelle également que vous pouvez créer un scope et récupérer son proxy dynamique directement avec la méthode <em>PackageHost.CreateMessageProxy</em>. On peut donc encore simplifier le code par la ligne :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.CreateMessageProxy("MonPremierPackage").Logon(new { User = "seb", Password = "seb" });</pre><p></p>
<p>D’un point de vue .NET, l’invocation dynamique de notre MessageCallback  “Logon » sur le proxy dynamique ne retourne rien, du moins il retourne “null”.</p>
<p></p><pre class="crayon-plain-tag">object result = PackageHost.CreateMessageProxy("MonPremierPackage").Logon(new { User = "seb", Password = "seb" });
// ici result = null</pre><p></p>
<p>Observez maintenant le code ci-dessous :</p>
<p></p><pre class="crayon-plain-tag">Task&lt;dynamic&gt; reponse = PackageHost.CreateMessageProxy("MonPremierPackage").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" });</pre><p></p>
<p>Si vous examinez attentivement le MessageCallback que nous invoquons sur le proxy dynamique, on utilise une forme générique : au lieu d’avoir “Logon(xxxxx)”, on a ”Logon<strong>&lt;bool&gt;</strong>(xxxxx)<em>”</em></p>
<p>Le fait de définir un type générique indique au proxy dynamique que vous attendez une réponse et donc qu’il s’agit d’une saga !</p>
<p>Le type générique est le type de réponse que vous attendez. Ici le MessageCallback “Logon” que vous avons écrit dans l’article précédent renvoi un booléen (bool). Vous pouvez utiliser n’importe quel type de base ou types complexes.</p>
<p>Si c’est la réponse est un objet anonyme ou si vous n’avez pas la définition de l’objet de retour dans votre code,  vous pouvez utiliser un “dynamic” (ex <em>Logon<strong>&lt;dynamic&gt;</strong>(xxxx)</em>)<em>.</em></p>
<p>Quoi qu’il en soit il est obligatoire d’invoquer le MessageCallback  en utilisant la syntaxe générique pour que le proxy dynamique ajoute automatiquement un identifiant de saga (<em>WithSaga</em>) lors de l’envoi du message.</p>
<p>Revenons donc à notre appel :</p>
<p></p><pre class="crayon-plain-tag">Task&lt;dynamic&gt; reponse = PackageHost.CreateMessageProxy("MonPremierPackage").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" });</pre><p></p>
<p>Le proxy dynamique nous retourne une ”Task&lt;dynamic&gt;”, c’est à dire une tache qui se charge d’envoyer et d’attendre la réception de la (première) réponse.</p>
<p>Vous pouvez bloquer le thread appelant jusqu&rsquo;à ce que l&rsquo;opération asynchrone soit terminée,  c’est à dire jusqu’à l’obtention de la réponse :</p>
<p></p><pre class="crayon-plain-tag">Task&lt;dynamic&gt; reponse = PackageHost.CreateMessageProxy("MonPremierPackage").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" });
PackageHost.WriteInfo((bool)reponse.Result ? "OK" : "DENIED");</pre><p></p>
<h4>Ajouter des timeouts</h4>
<p>Comme vous bloquez le thread appelant, prenez garde de mettre des gardes fou car nous n’avez aucune garantie qu’un package va répondre à votre saga. La tache pourrait ne jamais être complétée! Vous devriez donc attendre un certain temps avant de considérer la tache “hors délai”.</p>
<p>Par exemple attendons pendant 3 secondes maximum pour une réponse à notre Saga “Logon” :</p>
<p></p><pre class="crayon-plain-tag">Task&lt;dynamic&gt; reponse = PackageHost.CreateMessageProxy("MonPremierPackage").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" });
if (reponse.Wait(3000) &amp;&amp; reponse.IsCompleted)
{
    PackageHost.WriteInfo((bool)reponse.Result ? "OK" : "DENIED");
}
else
{
    PackageHost.WriteError("Aucune réponse !");
}</pre><p></p>
<h4>Async/Await</h4>
<p>Aussi vous pouvez utiliser le pattern async/await, très pratique pour les packages UI WPF/Winform :</p>
<p></p><pre class="crayon-plain-tag">public async void Logon()
{
    bool reponse = await PackageHost.CreateMessageProxy("MonPremierPackage").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" });
    PackageHost.WriteInfo(reponse ? "OK" : "DENIED");
}</pre><p></p>
<h4>Résultat de la tache</h4>
<p>Notez bien que le proxy dynamique vous renverra toujours une “Task&lt;<strong>dynamic</strong>&gt;” quelque soit le type générique précisé au niveau de l’appel du MessageCallback (“bool” dans l’exemple du “Logon”).</p>
<p>Cependant le résultat de la tache (<em>Task.Result</em>) est bien du type que celui passé au niveau de l’appel du MessageCallback.</p>
<p>Si vous souhaitez toutefois récupérer une Task&lt;T&gt; où T est le type de retour de votre saga, vous devez créer une nouvelle tache pour réaliser le “cast” à la suite de la tache créée par Constellation.</p>
<p>Par exemple :</p>
<p></p><pre class="crayon-plain-tag">Task&lt;dynamic&gt; task = PackageHost.CreateMessageProxy("MonPremierPackage").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" });
Task&lt;bool&gt; logonTask = task.ContinueWith&lt;bool&gt;(t =&gt; (bool)t.Result);</pre><p></p>
<h4>Annuler les taches</h4>
<p>Lorsque l’on créé des taches en .NET on peut avoir besoin de leurs associer un jeton d’annulation : le <em>CancellationToken</em>.</p>
<p>Pour cela, il vous suffit de passer un “CancellationToken” comme dernier argument de votre invocation du MessageCallback :</p>
<p></p><pre class="crayon-plain-tag">CancellationTokenSource source = new CancellationTokenSource(1000);
CancellationToken token = source.Token;
try
{
    Task&lt;dynamic&gt; reponse = PackageHost.CreateMessageProxy("ConstellationPackageConsole1").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" }, token);
    PackageHost.WriteInfo("Result = {0}", reponse.Result);
}
catch (AggregateException ae)
{
    foreach (Exception e in ae.InnerExceptions)
    {
        if (e is TaskCanceledException)
            PackageHost.WriteError("Unable to compute mean: {0}", ((TaskCanceledException)e).Message);
        else
            PackageHost.WriteError("Exception: " + e.GetType().Name);
    }
}
finally
{
    source.Dispose();
}</pre><p></p>
<p>Dans l’exemple ci-dessus on crée un <em>CancellationToken</em> à partir d’un <em>CancellationTokenSource</em> que l’on passe en tant que dernier paramètre du MessageCallback.</p>
<p>Le <em>CancellationTokenSource</em> est ici construit pour annuler la tache après 1 seconde (1000 ms). Vous pouvez aussi annuler la tache en en appelant la méthode “Cancel” sur l&rsquo;objet <em>CancellationTokenSource</em> :</p>
<p></p><pre class="crayon-plain-tag">source.Cancel();</pre><p></p>
<p>Bien entendu, le proxy dynamique se sert de votre <em>CancellationToken</em> pour savoir quand annuler la tache. De plus notez bien que le proxy retire ce <em>CancellationToken</em> des paramètres du message (= le <em>CancellationToken</em> n’est pas envoyé dans le message !).</p>
<p>Prenez garde à bien gérer les exceptions, si la tache est annulée une exception “<em>TaskCanceledException</em>” est levée.</p>
<h4>Le contexte du message</h4>
<p>Dans une méthode marquée comme “MessageCallback” ou dans le callback de retour d’une saga (<em>OnSagaResponse</em>) vous pouvez toujours accéder au contexte de réception du message avec la propriété “<em>MessageContext.Current</em>”.</p>
<p>En effet les MessageCallbacks ou les callbacks de retour des sagas sont invoqués dans un thread à part ce qui permet de l’attacher au contexte de réception du message.</p>
<p>Cependant avec les taches, le résultat est dispatché dans le thread de l’appelant. Autrement dit, vous ne pouvez pas accéder au contexte courant (<em>MessageContext.Current</em>) !</p>
<p>La solution consiste à “demander” au proxy dynamique de vous “remplir” un contexte que vous avez initialisé au préalable.</p>
<p>Pour ce faire, commencez par déclarer un contexte vide (<em>MessageContext.None</em>) et passer l’instance comme dernier paramètre d&rsquo;un appel :</p>
<p></p><pre class="crayon-plain-tag">MessageContext context = MessageContext.None;
Task&lt;dynamic&gt; reponse = PackageHost.CreateMessageProxy("ConstellationPackageConsole1").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" }, context);
PackageHost.WriteInfo("Result = {0} - Response received from : {1}", reponse.Result, context.Sender.FriendlyName);</pre><p></p>
<p>Une fois la tache complétée, la variable “context” sera remplie avec le contexte de la réponse lors de la réception. Vous pourrez donc accéder au contexte même dans vos taches.</p>
<p>Le MessageContext est toujours le dernière paramètr et le CancellationToken est donc l’avant dernier paramètre dans le cas où vous utilisez les deux :</p>
<p></p><pre class="crayon-plain-tag">CancellationTokenSource source = new CancellationTokenSource(1000);
CancellationToken token = source.Token;
MessageContext context = MessageContext.None;
try
{
    Task&lt;dynamic&gt; reponse = PackageHost.CreateMessageProxy("ConstellationPackageConsole1").Logon&lt;bool&gt;(new { User = "seb", Password = "seb" }, token, context);
    PackageHost.WriteInfo("Result = {0} - Response received from : {1}", reponse.Result, context.Sender.FriendlyName);
}
catch (AggregateException ae)
{
    foreach (Exception e in ae.InnerExceptions)
    {
        if (e is TaskCanceledException)
            PackageHost.WriteError("Unable to compute mean: {0}", ((TaskCanceledException)e).Message);
        else
            PackageHost.WriteError("Exception: " + e.GetType().Name);
    }
}
finally
{
    source.Dispose();
}</pre><p></p>
<h3>Générateur de code</h3>
<p>Comme l’ensemble des MessageCallbacks sont déclarés dans la Constellation (sauf si ils sont marqués comme “<a href="/client-api/net-package-api/messagecallbacks/#MessageCallback_cache">Hidden</a>”), il est possible de générer du code. Vous pouvez <a href="/constellation-platform/constellation-sdk/generateur-de-code/">lire un article très complet sur le sujet ici</a>.</p>
<p>Dans le menu contextuel de votre projet Visual Studio, sélectionnez “Generate Code” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-125.png"><img class="colorbox-1379"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Générer du code" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-102.png" alt="Générer du code" width="424" height="330" border="0" /></a></p>
<p align="left">Sélectionnez votre Constellation et cliquez sur “Discover” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-126.png"><img class="colorbox-1379"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Générer du code" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-103.png" alt="Générer du code" width="424" height="469" border="0" /></a></p>
<p align="left">Sélectionnez le ou les packages que vous souhaitez invoquer depuis votre package et cliquez sur “Generate”.</p>
<p align="left">Le générateur de code va créer différentes classes présentant votre Constellation avec les packages sélectionnés (MessageCallbacks et StateObjects).</p>
<p align="left">Dans notre cas nous allons inclure la définition des MessagesCallbacks de notre package “MonPremierPackage”.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-127.png"><img class="colorbox-1379"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Ajout du using" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-104.png" alt="Ajout du using" width="424" height="132" border="0" /></a></p>
<p align="left">Commençons donc par ajouter le using vers le MessageCallbacks de MonPremierPackage :</p>
<p></p><pre class="crayon-plain-tag">using ConstellationPackageConsole1.MonPremierPackage.MessageCallbacks;</pre><p></p>
<p align="left">Dans la classe générée “MyConstellation” vous retrouverez la liste des sentinelles et packages de votre Constellation :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-128.png"><img class="colorbox-1379"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Enumération générée" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-105.png" alt="Enumération générée" width="424" height="65" border="0" /></a></p>
<p>Vous pourrez alors créer un scope personnalisé pour le package sélectionné :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-129.png"><img class="colorbox-1379"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="MessageScope généré par package" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-106.png" alt="MessageScope généré par package" width="424" height="84" border="0" /></a></p>
<p align="left">Ce scope personnalisé contiendra l’ensemble des MessageCallbacks :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-130.png"><img class="colorbox-1379"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-107.png" alt="image" width="424" height="69" border="0" /></a></p>
<p align="left">Et sur chaque MessageCallback, vous retrouvez les bons types de retour, les commentaires et les types complexes proprement générés :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-131.png"><img class="colorbox-1379"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="MessageCallbacks générés" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-108.png" alt="MessageCallbacks générés" width="424" height="67" border="0" /></a></p>
<p>De ce fait, pour reprendre l’exemple du MessageCallback “Logon”, on pourra écrire le code :</p>
<p></p><pre class="crayon-plain-tag">var context = MessageContext.None;
var token = new CancellationTokenSource(5000).Token;
var user = new UserInfo() { User = "seb", Password = "seb" };

var task = MyConstellation.Packages.MonPremierPackage.CreateMonPremierPackageScope().Logon(user, token, out context);

PackageHost.WriteInfo("Logon user {0} - Result = {1} - Sender : {2}", user.User, task.Result, context.Sender.FriendlyName);</pre><p></p>
<p><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-132.png"><img class="colorbox-1379"  loading="lazy" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border-width: 0px;" title="Démonstration" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-109.png" alt="Démonstration" width="424" height="205" border="0" /></a></p>
<p>N’hésitez pas <a href="/constellation-platform/constellation-sdk/generateur-de-code/">lire cet article très complet sur la génération de code C#</a> dans Constellation.</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/">Envoyer des messages &#038; Invoquer des MessageCallbacks</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Persistance de données dans un package</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/persistance-de-donnes-dans-un-package/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/persistance-de-donnes-dans-un-package/#comments</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Mon, 21 Mar 2016 15:39:53 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[StateObject]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1536</guid>

					<description><![CDATA[<p>Certains de vos packages peuvent avoir besoin d’enregistrer/persister des données et vous devez savoir qu&#8217;à chaque mise à jour d&#8217;un package par sa sentinelle, l’ensemble du package est supprimé avant d’être redéployé. Il existe deux possibilités pour persister les données</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/persistance-de-donnes-dans-un-package/">Persistance de données dans un package</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Certains de vos packages peuvent avoir besoin d’enregistrer/persister des données et vous devez savoir qu&rsquo;à chaque mise à jour d&rsquo;un package par sa sentinelle, l’ensemble du package est supprimé avant d’être redéployé.</p>
<p>Il existe deux possibilités pour persister les données :</p>
<ul>
<li>Utiliser un stockage “hors Constellation”</li>
<li>Utiliser les StateObjects</li>
</ul>
<h3>Stocker en dehors de Constellation</h3>
<p>Comme vous le savez, un package n’est ni plus ni moins qu’une application.</p>
<p>Vous pouvez donc utiliser n’importe quel système de stockage :</p>
<ul>
<li>File System</li>
<li>Base de donnée en tout genre</li>
<li>Web service</li>
<li>Etc …</li>
</ul>
<p>Libre à vous de stoker vos données où bon vous sembles mais attention si vous souhaitez diffuser votre package, vous ajoutez des prérequis pour l’installation de votre package.</p>
<h4>Note sur le File System</h4>
<p>Vous pouvez bien sûr stocker des données dans des fichiers. Par défaut le “CurrentDirectory” du package est son répertoire de déploiement. Prenez garde toutefois car à chaque déploiement du package par la sentinelle ce répertoire est vidé.</p>
<p>Vous devez donc utiliser un répertoire “externe” qu’on définira par un <a href="/client-api/net-package-api/settings/">setting</a>.</p>
<h4>SerializationHelper</h4>
<p>Notez que la librairie Constellation met à disposition la classe “<em>SerializationHelper</em>” dans le namespace “Constellation.Utils” permettant de sérialiser et dé-sérialiser facilement des objets en JSON ou en XML.</p>
<p></p><pre class="crayon-plain-tag">Constellation.Utils.SerializationHelper.SerializeToFile&lt;MyCustomObject&gt;(new MyCustomObject() { Number = 42, String = "Demo" }, PackageHost.GetSettingValue("MyData.json"));
MyCustomObject myData = Constellation.Utils.SerializationHelper.DeserializeFromFile&lt;MyCustomObject&gt;(PackageHost.GetSettingValue("MyData.json"));</pre><p></p>
<p>Vous disposez des méthodes :</p>
<ul>
<li>SerializeToFile et DeserializeFromFile pour sérialiser/dé-sérialiser vers/depuis un fichier</li>
<li>SerializeToString et DeserializeFromString pour sérialiser/dé-sérialiser vers/depuis un string</li>
</ul>
<p>Par défaut c’est le format JSON qui est utilisé mais vous pouvez définir le <em>DataContractSerialiser</em> (format XML) dans les paramètres de la méthode.</p>
<h3>Utiliser les StateObjects comme stockage</h3>
<p>Le principe est d’utiliser les StateObjects comme stockage, très utile pour sauvegarder/restaurer l’état d’un package.</p>
<p>Chaque package peut <a href="/client-api/net-package-api/stateobjects/">publier des StateObjects</a> que ce soit de simple valeur ou de véritable objet complexe. A chaque (re)démarrage de votre package, tous les StateObjects du package sont purgés de la Constellation mais cependant vous avez la possibilité de les récupérer dans votre code avant cette purge.</p>
<p>Pour cela vous devez ajouter dans le <a href="/concepts/package-manifest/">manifeste du package</a> (<em>PackageInfo.xml</em>) l’attribut “RequestLastStateObjectsOnStart” à true.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-150.png"><img class="colorbox-1536"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="RequestLastStateObjectsOnStart" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-127.png" alt="RequestLastStateObjectsOnStart" width="424" height="202" border="0" /></a></p>
<p align="left">Ensuite dans votre code, vous devez le plus tôt possible dans le “OnStart” vous abonnez à l’événement “LastStateObjectsReceived“.</p>
<p align="left">Cet événement sera levé lorsque votre package recevra ses anciens StateObjects du serveur avant d’être purgés. L’argument passé dans l’événement contient une liste des StateObjects avant purge.</p>
<p></p><pre class="crayon-plain-tag">PackageHost.LastStateObjectsReceived += (s, e) =&gt;
{
    PackageHost.WriteInfo("Reception de mes anciens StateObjects au nombre de {0}", e.StateObjects.Count);
};</pre><p></p>
<p>Vous pouvez “bêtement” re-pusher l’ensemble des StateObjects sans aucune modification :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.LastStateObjectsReceived += (s, e) =&gt;
{
    foreach (StateObject so in e.StateObjects)
    {
        // Re-Pusher “bêtement” les StateObjects
        PackageHost.PushStateObject(so.Name, so.DynamicValue, so.Type, so.Metadatas, so.Lifetime);
    }
};</pre><p></p>
<p>Grace à ce mécanisme, vos packages peuvent conserver des StateObject sur “leur état”.</p>
<p>Par exemple, imaginez un package “Brain” qui pilote l’allumage automatique des éclairages d’un jardin.</p>
<p>Ce package déclare un <a href="/client-api/net-package-api/consommer-des-stateobjects/">StateObjectLink </a>vers un StateObject qui indique la luminosité extérieure. Si la valeur franchie un certain seuil, le package décide d&rsquo;allumer ou éteindre les éclairages en envoyant un message pour déclencher un MessageCallback du package pilotant les éclairages.</p>
<p>Imaginez maintenant que suite à l’allumage automatique des éclairages par votre package vous décidez de les éteindre manuellement. Puis, quelques instants plus tard, pour diverses raisons vous devez redémarrer (ou mettre à jour) votre package “Brain”. Vos éclairages risquent de se rallumer car votre “Brain” n’a pas mémorisé qu’il a déjà réalisé cette action !</p>
<p>Pour ce genre de problématique, il est intéressant de stocker ces variables dans une classe d’état et de publier cette classe dans un StateObject qu’on pourra nommer “State”. Lorsque notre package redémarre, on demandera à Constellation de nous renvoyer les derniers StateObjects.</p>
<p>Cela nous permettra de récupérer le dernier état de notre package avant son redémarrage.</p>
<p>Par exemple, vous pouvez définir une classe d’état de la façon suivante :</p>
<p></p><pre class="crayon-plain-tag">public class CurrentState
{
    public DateTime OuvertureVolet { get; set; }

    public DateTime FemertureVolet { get; set; }

    public DateTime OuvertureLumiereJardin { get; set; } 

    // etc....

    public const string STATEOBJECT_NAME = "CurrentState";

    private static CurrentState current;

    public static CurrentState Current
    {
        get { return current; }
    }

    public static void Load(StateObject stateObject = null)
    {
        if (stateObject != null)
        {
            PackageHost.WriteInfo("Restoring state from {0}", stateObject.LastUpdate);
            current = stateObject.GetValue&lt;CurrentState&gt;();
        }
        else
        {
            PackageHost.WriteInfo("Creating new state");
            current = new CurrentState();
        }
        current.Save();
    }

    public void Save()
    {
        PackageHost.WriteInfo("Saving current state");
        PackageHost.PushStateObject(STATEOBJECT_NAME, current);
    }
}</pre><p></p>
<p>Sans oublier d’ajouter un handler pour la restauration de l’état précèdent :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.LastStateObjectsReceived += (s, e) =&gt;
{
    if (e.StateObjects != null)
    {
        PackageHost.WriteInfo("Received old StateObjects (Count:{0})", e.StateObjects.Count);
        CurrentState.Load(e.StateObjects.FirstOrDefault(so =&gt; so.Name == CurrentState.STATEOBJECT_NAME));
    }
};</pre><p></p>
<p>De cette façon vous pouvez à tout moment accéder à votre variable via votre classe statique “State.Current” et dès que vous changez une valeur, invoquez la méthode Save(), pour re-pusher votre “State” comme StateObject de votre package :</p>
<p></p><pre class="crayon-plain-tag">if (DateTime.Now.Date != CurrentState.Current.FemertureVoletSalon.Date &amp;&amp; ** Luminosite &lt; seuil ****)
{
   // Fermer le volet ici !!
   CurrentState.Current.FemertureVoletSalon = DateTime.Now;
   CurrentState.Current.Save();
}</pre><p></p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/persistance-de-donnes-dans-un-package/">Persistance de données dans un package</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/persistance-de-donnes-dans-un-package/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Accéder au hub de contrôle avec le ControlManager</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/controlmanager/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/controlmanager/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Mon, 21 Mar 2016 10:24:49 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[ControlHub]]></category>
		<category><![CDATA[ControlManager]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1491</guid>

					<description><![CDATA[<p>Le hub de contrôle permet d’accéder à différentes fonctions de pilotage de votre Constellation. Voyons en détail comment contrôler votre Constellation depuis un package C#/.NET. Accéder au hub de contrôle Tout d’abord il faut déclarer que votre package souhaite un accès</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/controlmanager/">Accéder au hub de contrôle avec le ControlManager</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Le <a href="/concepts/les-differents-hubs-et-interfaces-rest-du-serveur-constellation/">hub de contrôle</a> permet d’accéder à différentes fonctions de pilotage de votre Constellation.</p>
<p>Voyons en détail comment contrôler votre Constellation depuis un package C#/.NET.</p>
<h3>Accéder au hub de contrôle</h3>
<p>Tout d’abord il faut déclarer que votre package souhaite un accès au hub de contrôle. Pour cela vous devez ajouter l’attribut “EnableControlHub” à <em>true</em> dans le <a href="/concepts/package-manifest/">manifeste </a>de votre package (fichier <em>PackageInfo.xml</em>).</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-151.png"><img class="colorbox-1491"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-128.png" alt="image" width="424" height="258" border="0" /></a></p>
<p>Ensuite, il faut que votre package se connecte à votre Constellation avec une “Access Key” qui possède l’autorisation d’accès au hub de contrôle.</p>
<p>Il faut ajouter l’attribut “enableControlHub” à true sur un de vos credentials et affecter ce credential à votre package :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-152.png"><img class="colorbox-1491"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-129.png" alt="image" width="424" height="233" border="0" /></a></p>
<p>Ainsi votre package pourra accéder au hub de contrôle de contrôle. Vous devriez d’ailleurs toujours tester la propriété “<em>HasControlManager</em>” pour gérer les erreurs où l’accès au hub de contrôle n&rsquo;est possible.</p>
<p></p><pre class="crayon-plain-tag">if (PackageHost.HasControlManager)
{
    PackageHost.WriteInfo("ControlHub OK");
}
else
{
    PackageHost.WriteError("ControlHub access denied !");
}</pre><p></p>
<h3>Suivre les sentinelles et leurs statuts</h3>
<p>Il y a deux manières d’obtenir la liste des sentinelles enregistrées dans votre Constellation :</p>
<ul>
<li><u>RequestSentinelsList</u> : vous retourne la liste des sentinelles en une seule fois par l’événement “<em>SentinelsListUpdated</em>”</li>
<li><u>RequestSentinelUpdates</u> : vous retourne l’état de chaque sentinelle dans l’événement “<em>SentinelUpdated</em>” (cet événement est levé pour chaque sentinelle)</li>
</ul>
<p>En principe pour récupérer la liste des sentinelles de votre Constellation, vous utiliserez toujours la première méthode.</p>
<p>Par exemple :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.SentinelsListUpdated += (s, e) =&gt;
{
    PackageHost.WriteInfo("Il y a {0} sentinelle(s)", e.Sentinels.Count);
    foreach (SentinelInfo sentinel in e.Sentinels)
    {
        PackageHost.WriteInfo("Sentinelle '{0}' (Plateforme: {1}) =&gt; IsConnected = {2}",
            sentinel.Description.SentinelName,
            sentinel.Description.Platform,
            sentinel.IsConnected);
    }
};
PackageHost.ControlManager.RequestSentinelsList();</pre><p></p>
<p>Vous pouvez également vous abonner en temps réel aux mises à jour des sentinelles de votre Constellation.</p>
<p>Pour cela il faut activer la réception des mises à jour :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.ReceiveSentinelUpdates = true;</pre><p></p>
<p>Puis attacher un handler sur l’événement “SentinelUpdated” qui se produit à chaque mise à jour d’une sentinelle :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.SentinelUpdated += (s, e) =&gt;
{
    PackageHost.WriteInfo("Sentinelle '{0}' (Plateforme: {1}) =&gt; IsConnected = {2}",
        e.Sentinel.Description.SentinelName,
        e.Sentinel.Description.Platform,
        e.Sentinel.IsConnected);
};</pre><p></p>
<h3>Superviser les packages</h3>
<h4>Récupérer les packages d’une sentinelle</h4>
<p>Pour récupérer la liste des packages qui sont déployés sur une sentinelle vous devez attacher un handler sur l’événement “<em>PackagesListUpdated</em>” et appeler la méthode “<em>RequestPackagesList</em>” en spécifiant le nom de la sentinelle.</p>
<p>Pour exemple :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.PackagesListUpdated += (s, e) =&gt;
{
    PackageHost.WriteInfo("Il y a {0} package(s) sur la sentinelle {1}", e.Packages.Count, e.SentinelName);
};
PackageHost.ControlManager.RequestPackagesList("MA-SENTINELLE");</pre><p></p>
<p>Chaque “PackageInfo” contient différentes informations sur l’instance du package (description du package, état du package, état de la connexion, version du package, etc…) :</p>
<p></p><pre class="crayon-plain-tag">foreach (PackageInfo package in e.Packages)
{
    PackageHost.WriteInfo("Package '{0}' - Etat = {1}", package.Package.Name, package.State);
}</pre><p></p>
<h4>Suivre l’état des packages</h4>
<p>Vous pouvez aussi vous abonnez aux mises à jour des états des packages :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.ReceivePackageState = true;</pre><p></p>
<p>Vous pourrez ensuite être notifier de tout changement d’état de vos packages dans votre Constellation :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.PackageStateUpdated += (s, e) =&gt;
{
    PackageHost.WriteInfo("Le package '{0}' est maintenant dans l'état {1}", e.PackageName, e.State);
};</pre><p></p>
<h4>Suivre la consommation des packages</h4>
<p>Vous pouvez aussi récupérer la consommation des ressources (CPU et RAM) de vos packages :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.ReceivePackageUsage = true;</pre><p></p>
<p>Par exemple, on pourrait écrire un warning si un package consomme plus de 50% du CPU :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.PackageUsageUpdated += (s, e) =&gt;
{
    if (e.CPU &gt; 50) // Package consommant plus de 50% du CPU à un instant T
    {
        PackageHost.WriteWarn("Le package {0} consomme maintenant plus de 50% !!! CPU={1}% RAM={2}ko",
            e.PackageName, e.CPU, e.RAM / 1024);
    }
};</pre><p></p>
<h4>Contrôler les packages</h4>
<p>Vous pouvez démarrer un package sur une sentinelle avec la méthode “<em>StartPackage</em>” :</p>
<p></p><pre class="crayon-plain-tag">// Démrrage du package "MonPremierPackage" sur la sentinelle "MON-PC"
PackageHost.ControlManager.StartPackage("MON-PC", "MonPremierPackage");</pre><p></p>
<p>Ou encore l’arrêter avec la méthode “<em>StopPackage</em>” :</p>
<p></p><pre class="crayon-plain-tag">// Arret du package "MonPremierPackage" sur la sentinelle "MON-PC"
PackageHost.ControlManager.StopPackage("MON-PC", "MonPremierPackage");</pre><p></p>
<p>Pour redémarrer (<em>Stop</em> puis <em>Start</em>) un package sur une sentinelle donnée, utilisez la méthode “<em>RestartPackage</em>” :</p>
<p></p><pre class="crayon-plain-tag">// Redémarrage du package "MonPremierPackage" sur la sentinelle "MON-PC"
PackageHost.ControlManager.RestartPackage("MON-PC", "MonPremierPackage");</pre><p></p>
<p>Enfin pour mettre à jour un package, utilisez la méthode “<em>ReloadPackage</em>”.</p>
<p>Un “reload” stoppe le package, ordonne à la sentinelle de re-télécharger le package sur le serveur, le déploie en local avant de le démarrer.</p>
<p></p><pre class="crayon-plain-tag">// Mise à jour du package "MonPremierPackage" sur la sentinelle "MON-PC"
PackageHost.ControlManager.ReloadPackage("MON-PC", "MonPremierPackage");</pre><p></p>
<h3>Accéder aux logs en temps réel</h3>
<p>Pour récupérer les logs produits par vos sentinelles et packages dans votre Constellation, activez la propriété suivante :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.ReceivePackageLog = true;</pre><p></p>
<p>Il suffit ensuite d’attacher handler sur l’événement “<em>LogEntryReceived</em> “.</p>
<p>Vous recevrez en paramètre un objet “<em>LogEntry</em>” qui contient toutes les informations sur un log (le message, la date, le couple Sentinelle/Package à l’origine du log et la sévérité du message).</p>
<p>Par exemple :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.LogEntryReceived += (s, e) =&gt;
{
    if (e.LogEntry.PackageName != PackageHost.PackageInstanceName) // Ne pas prendre les logs de ce package pour éviter la boucle ;)
    {
        // On log des logs, juste pour l'exemple ;)
        PackageHost.WriteInfo("Je reçois un log daté du {0} du package {1} sur la sentinelle {2} de type {3}." +
            "Message = {4}",
            e.LogEntry.Date.ToString(),
            e.LogEntry.SentinelName,
            e.LogEntry.PackageName,
            e.LogEntry.Level,
            e.LogEntry.Message);
    }
};</pre><p></p>
<p>Notez qu’ici on logue dans Constellation (“WriteInfo”) les logs des autres packages (d’où le &lt;if&gt;). Ca n’a pas de sens, c’est juste pour l’exemple !</p>
<h3>Récupérer la description des packages</h3>
<p>Comme vous le savez, tous les <a href="/client-api/net-package-api/messagecallbacks/#Decrire_ses_MessageCallbacks">MessageCallbacks ainsi que les types personnalisés</a> utilisés en entrée ou en sortie sont décrits dans un <a href="/concepts/messaging-message-scope-messagecallback-saga/">PackageDescriptor</a> tout comme les <a href="/client-api/net-package-api/stateobjects/#Decrire_les_types_de_StateObjects">types personnalisés des StateObjects</a>.</p>
<p>En vous connectant au hub de contrôle vous pouvez récupérer le <em>PackageDescriptor</em> de chaque package. Par exemple :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.PackageDescriptorUpdated += (s, e) =&gt;
{
    PackageHost.WriteInfo("Pour le package {0}, il y a {1} MessageCallback(s) associés à " +
                         "{2} type(s) ainsi que {3} type(s) de StateObject",
        e.PackageName,
        e.Descriptor.MessageCallbacks.Count,
        e.Descriptor.MessageCallbackTypes.Count,
        e.Descriptor.StateObjectTypes.Count);
};
PackageHost.ControlManager.RequestPackageDescriptor("MonPremierPackage");</pre><p></p>
<h3>Purger les StateObjects</h3>
<p>Pour supprimer tous les StateObjects d’un package déployé une sentinelle :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.PurgeStateObjects("MON-PC", "MonPremierPackage");</pre><p></p>
<p>Vous pouvez aussi spécifier le nom du StateObject, pour supprimer un StateObject en particulier (identifié par son nom) :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.PurgeStateObjects("MON-PC", "MonPremierPackage", "Demo");</pre><p></p>
<p>Ou encore, tous les StateObjects d’un type particulier d’une instance de package :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.PurgeStateObjects("MON-PC", "MonPremierPackage", type:"TypeDemo");</pre><p></p>
<h3>Recharger la configuration</h3>
<p>Vous pouvez recharger et déployer la configuration Constellation en invoquant la méthode :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.ReloadServerConfiguration();</pre><p></p>
<p>Vous pouvez également passer un booléen pour indiquer si il faut déployer la configuration (c’est à dire pousser la configuration sur chaque sentinelle et package). Par défaut, la valeur est à “true” (la configuration est déployée).</p>
<p>Autrement :</p>
<p></p><pre class="crayon-plain-tag">PackageHost.ControlManager.ReloadServerConfiguration(false);</pre><p></p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/controlmanager/">Accéder au hub de contrôle avec le ControlManager</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/controlmanager/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Créer des packages UI en Winform ou WPF</title>
		<link>https://developer.myconstellation.io/client-api/net-package-api/packages-ui-wpf-winform/</link>
					<comments>https://developer.myconstellation.io/client-api/net-package-api/packages-ui-wpf-winform/#respond</comments>
		
		<dc:creator><![CDATA[Sebastien Warin]]></dc:creator>
		<pubDate>Tue, 22 Mar 2016 16:12:30 +0000</pubDate>
				<category><![CDATA[.NET API]]></category>
		<category><![CDATA[Guide de démarrage]]></category>
		<category><![CDATA[XAML]]></category>
		<category><![CDATA[StateObject]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[MessageCallback]]></category>
		<category><![CDATA[StateObjectLink]]></category>
		<category><![CDATA[Sentinel UI]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[Winform]]></category>
		<category><![CDATA[UI]]></category>
		<guid isPermaLink="false">https://developer.myconstellation.io/?p=1563</guid>

					<description><![CDATA[<p>Vous pouvez créer des applications graphiques et les déployer sur vos sentinelles UI grâce à Constellation. Chaque package UI pourra invoquer ou exposer des MessageCallbacks, consommer ou produire des StateObjects, etc… Hello World WPF Dans Visual Studio, vous créez un</p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/packages-ui-wpf-winform/">Créer des packages UI en Winform ou WPF</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Vous pouvez créer des applications graphiques et les déployer sur vos sentinelles UI grâce à Constellation.</p>
<p>Chaque package UI pourra <a href="/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/">invoquer</a> ou <a href="/client-api/net-package-api/messagecallbacks/">exposer</a> des MessageCallbacks, <a href="/client-api/net-package-api/consommer-des-stateobjects/">consommer</a> ou <a href="/client-api/net-package-api/stateobjects/">produire</a> des StateObjects, etc…</p>
<p><span id="more-1563"></span></p>
<h3>Hello World WPF</h3>
<p>Dans Visual Studio, vous créez un package WPF :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-153.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Création d'un package UI" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-130.png" alt="Création d'un package UI" width="424" height="294" border="0" /></a></p>
<p align="left">Le template est une application WPF classique :</p>
<p align="left"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-154.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border-width: 0px;" title="Structure du package WPF" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-131.png" alt="Structure du package WPF" width="244" height="204" border="0" /></a></p>
<p align="left">Le <em><a href="/client-api/net-package-api/les-bases-des-packages-net/#Fonctionnement_de_base">IPackage</a></em> de ce package est la classe “App” (App.xaml.cs). Ce package lance la fenêtre MainWindow au démarrage (méthode “OnStart”).</p>
<p align="left">La “MainWindow” est une Window WPF classique à l’exception que dans le constructeur, on enregistre automatiquement les <a href="/client-api/net-package-api/consommer-des-stateobjects/#StateObjectLink_et_Notifier_personnalises">[StateObjectLink]</a> et les <a href="/client-api/net-package-api/messagecallbacks/#Exposer_des_methodes">[MessageCallback]</a> de la classe. De plus on renvoie la description du package (dans le cas où vous avez ajoutez des MessageCallbacks).</p>
<p></p><pre class="crayon-plain-tag">public partial class MainWindow : Window
{
    public MainWindow()
    {
        PackageHost.RegisterStateObjectLinks(this);
        PackageHost.RegisterMessageCallbacks(this);
        PackageHost.DeclarePackageDescriptor();
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        this.Title = string.Format("IsRunning: {0} - IsConnected: {1} - IsStandAlone: {2}", PackageHost.IsRunning, PackageHost.IsConnected, PackageHost.IsStandAlone);
        PackageHost.WriteInfo("I'm running !");
    }
}</pre><p></p>
<p align="left">Au chargement de la fenêtre on logge un message dans Constellation et on affiche quelques propriété sur l’état du package dans le titre de cette fenêtre !</p>
<p align="left">Ajoutons un simple label “Hello World” au centre de notre fenêtre :</p>
<p></p><pre class="crayon-plain-tag">&lt;Label x:Name="label" Content="Hello World" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48"/&gt;</pre><p></p>
<p align="left">Le code XAML sera donc:</p>
<p></p><pre class="crayon-plain-tag">&lt;Window x:Class="MonPackageWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded"&gt;
    &lt;Grid&gt;
        &lt;Label x:Name="label" Content="Hello World" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48"/&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;</pre><p></p>
<p align="left">Pour tester notre package en debug sans être connecté à Constellation : “F5”</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-167.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Debug du package en local" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-144.png" alt="Debug du package en local" width="424" height="344" border="0" /></a></p>
<p align="left">Vous noterez que les <a href="/client-api/net-package-api/les-bases-des-packages-net/#Ecrire_des_logs">WriteLog</a> Constellation sont toujours afficher dans la fenêtre de sortie de Visual Studio.</p>
<p align="left">Maintenant pour lançons le debug de notre package dans Visual Studio tout en le connectant à Constellation (raccourci Ctrl+Alt+F8)</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-168.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-145.png" alt="image" width="424" height="102" border="0" /></a></p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-169.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Debug du package dans Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-146.png" alt="Debug du package dans Constellation" width="424" height="228" border="0" /></a></p>
<h3 align="left">Invoquer des MessageCallbacks</h3>
<p align="left">Vous pouvez <a href="/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/">invoquer</a> ou <a href="/client-api/net-package-api/messagecallbacks/">exposer</a> des MessageCallbacks comme n’importe quel package connecté dans votre Constellation.</p>
<p align="left">Pour <a href="/client-api/net-package-api/messagecallbacks/">exposer</a> des MessageCallbacks (des méthodes NET), il suffit d’ajouter l’attribut [MessageCallback] sur vos méthodes.</p>
<p align="left">Pour <a href="/client-api/net-package-api/envoyer-des-messages-invoquer-des-messagecallbacks/">invoquer</a> des MessageCallbacks, il faut créer un scope et envoyer le message. Grace au proxy dynamique, vous pouvez invoquer un MessageCallback comme vous invoquerez une méthode .NET.</p>
<p align="left">Dans cet exemple nous allons invoquer des MessageCallbacks des packages WindowsControl et GoogleTraffic. Vous pouvez <a href="/getting-started/telecharger-et-deployer-des-packages-sur-vos-sentinelles/">suivre le guide ici</a> pour déployer ces deux packages.</p>
<p align="left">En vous rendant sur la page “MessageCallbacks Explorer” de la Console, vous pouvez explorer les MC exposés par les packages.</p>
<p align="left">Par exemple, le package WindowsControl expose plusieurs MessageCallbacks pour arrêter, redémarrer, mettre en veille ou verrouiller l’ordinateur (= la sentinelle) sur lequel le package est déployé.</p>
<p align="left">Le package GoogleTraffic expose un MessageCallback “GetRoute” pour calculer le temps de route en spécifiant un point de départ et d’arrivée. Ce MessageCallback  est une saga pour vous retourner la réponse.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-191.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="MessageCallbacks Explorer" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-168.png" alt="MessageCallbacks Explorer" width="424" height="237" border="0" /></a></p>
<p align="left">Pour simplifier le développement et éviter de travailler avec des types dynamiques, nous allons auto-générer le code.</p>
<p align="left">Cliquez sur le bouton “Generate Code” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-185.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Génération de code" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-162.png" alt="Génération de code" width="424" height="67" border="0" /></a></p>
<p align="left">Sélectionnez votre Constellation, cliquez sur “Discover” et sélectionnez les packages que vous souhaitez ajouter dans le code généré puis cliquez sur “Generate” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-186.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Génération de code" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-163.png" alt="Génération de code" width="424" height="474" border="0" /></a></p>
<p align="left">Editez le code de la MainWindow (<em>MainWindow.xaml.cs</em>) pour ajouter le code généré des MessageCallbacks pour les packages GoogleTraffic et WindowsControl :</p>
<p></p><pre class="crayon-plain-tag">using MonPackageWPF.GoogleTraffic.MessageCallbacks;
using MonPackageWPF.WindowsControl.MessageCallbacks;</pre><p></p>
<p align="left">Dans la vue XAML, ajoutons deux boutons : l’un pour mettre en veille et l’autre pour faire un test d’itinéraire :</p>
<p></p><pre class="crayon-plain-tag">&lt;Button x:Name="btSleep" Content="Sleep !" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10" Width="75" Click="btSleep_Click"/&gt;
&lt;Button x:Name="btTestRoute" Content="Test Route" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="10,0,0,10" Width="75" Click="btTestRoute_Click" /&gt;</pre><p></p>
<p align="left">Pour le premier bouton, nous allons sélectionner l’instance du package “WindowsControl” sur la sentinelle “PO_SEB” pour créer un scope afin d’invoquer le MessageCallback “Sleep” :</p>
<p></p><pre class="crayon-plain-tag">private void btSleep_Click(object sender, RoutedEventArgs e)
{
    MyConstellation.PackageInstances.PO_SEB_WindowsControl.CreateWindowsControlScope().Sleep();
}</pre><p></p>
<p align="left">Prenez garde à créer un scope sur une instance (sentinelle + package) car sinon, si vous créez un scope sur le package “WindowsControl”, toutes les sentinelles hébergeant ce package se mettront en veille !</p>
<p align="left">Pour le deuxième bouton, nous allons demander les différentes routes pour un “Lille-Paris”. Comme il s’agit d’une saga (message avec réponse) nous allons l’invoquer en “async/await” et afficher dans le label la meilleure route :</p>
<p></p><pre class="crayon-plain-tag">private async void btTestRoute_Click(object sender, RoutedEventArgs e)
{
    label.Content = "Calcul en cours ...";
    var route = await MyConstellation.Packages.GoogleTraffic.CreateGoogleTrafficScope().GetRoutes("lille", "paris");
    var bestRoute = route.OrderBy(r =&gt; r.TimeWithTraffic).FirstOrDefault();
    label.Content = $"{bestRoute.Name}\nDistance:{bestRoute.DistanceInKm}km\nTemps : {bestRoute.TimeWithTraffic}";
}</pre><p></p>
<p align="left">Lancer le debug dans Constellation : <img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Debug On Constellation" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-196.png" alt="Debug On Constellation" width="104" height="34" border="0" />  (ou Ctrl+Alt+F8).</p>
<p align="left">Premier test, en cliquant sur Sleep, vous allez envoyer un message pour invoquer le MessageCallback “Sleep” du package “WindowsControl” sur la sentinelle ici nommée “PO-SEB”. Ainsi le Windows “PO-SEB” se mettra instantanément en veille !</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-193.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Test du MC &quot;Sleep&quot;" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-170.png" alt="Test du MC &quot;Sleep&quot;" width="244" height="165" border="0" /></a></p>
<p align="left">Deuxième test, pour invoquer le MessageCallback “GetRoutes” du package GoogleTraffic :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-194.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Test du MC &quot;GetRoutes&quot; en Async" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-171.png" alt="Test du MC &quot;GetRoutes&quot; en Async" width="244" height="165" border="0" /></a><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-195.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Réponse à la saga &quot;GetRoutes&quot;" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-172.png" alt="Réponse à la saga &quot;GetRoutes&quot;" width="244" height="165" border="0" /></a></p>
<h3 align="left">Consommer des StateObjects dans votre vue XAML</h3>
<p align="left">Assurez-vous d’avoir dans votre Constellation au moins un package “HWMonitor” déployé sur une sentinelle. Au besoin, vous pouvez <a href="/getting-started/telecharger-et-deployer-des-packages-sur-vos-sentinelles/">suivre ce guide ici</a>.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-197.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="HWMonitor" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-173.png" alt="HWMonitor" width="424" height="223" border="0" /></a></p>
<p align="left">Pour comprendre en détail, la <a href="/client-api/net-package-api/consommer-des-stateobjects/">consommation des StateObjects</a> dans vos packages n’hésitez pas à relire <a href="/client-api/net-package-api/consommer-des-stateobjects/">cet article</a>.</p>
<p align="left">Par exemple, pour afficher en temps réel la consommation CPU (StateObject nommé “/intelcpu/0/load/0”) mesurée par le package HWMonitor sur sa sentinelle (ici “PO-SEB”), ajoutons un StateObjectLink :</p>
<p></p><pre class="crayon-plain-tag">[StateObjectLink("PO-SEB", "HWMonitor", "/intelcpu/0/load/0")]
public StateObjectNotifier CPU { get; set; }</pre><p></p>
<p align="left">Vous pouvez également générer du code en sélectionnant le package HWMonitor. Vous pourrez ensuite ajouter un “using” vers les StateObjects de ce package :</p>
<p></p><pre class="crayon-plain-tag">using MonPackageWPF.HWMonitor.StateObjects;</pre><p></p>
<p align="left">Cela vous permettra d’utiliser un le “HWMonitorStateObjectLink” avec des énumérations générées pour vos sentinelles et nom de StateObjects :</p>
<p></p><pre class="crayon-plain-tag">[HWMonitorStateObjectLink(MyConstellation.Sentinels.PO_SEB, HWMonitorStateObjectNames._intelcpu_0_load_0)]
public StateObjectNotifier CPU { get; set; }</pre><p></p>
<p align="left">Comme vous le savez, le StateObjectNotifier implémente l’interface INotifyPropertyChanged. Vous pouvez donc lier cette propriété dans votre vue XAML pour voir votre StateObject en temps réel sur votre interface.</p>
<p align="left">Nous allons modifier le label “Hello World” pour afficher en temps réel votre consommation CPU. Pour cela changeons le contenu (Content) du label avec la propriété “Value” du StateObject publié par le package HWMonitor.</p>
<p align="left">Cette propriété contient la valeur (ici de l’utilisation du CPU) avec un nombre décimal. Nous ajoutons également l’attribut “ContentStringFormat” pour n’afficher que 2 chiffres après la virgule.</p>
<p></p><pre class="crayon-plain-tag">&lt;Label x:Name="label" Content="{Binding Path=CPU.DynamicValue.Value}" ContentStringFormat="{}{0:N2}%" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48"/&gt;</pre><p></p>
<p align="left">Pour pourvoir utiliser des propriétés .NET comme binding dans votre vue XAML, vous devez spécifier le DataContext de votre fenêtre vers votre classe MainWindow soit via le code (<em>this.DataContent = this</em>) ou soit directement dans votre vue XAML en ajoutant cette attribut sur l’élément Window :</p>
<p></p><pre class="crayon-plain-tag">DataContext="{Binding RelativeSource={RelativeSource Self}}"</pre><p></p>
<p align="left">Résultat, vous pouvez suivre en temps réel le CPU ici de la machine “PO-SEB” :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-200.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Binding au CPU du package HWMonitor" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-176.png" alt="Binding au CPU du package HWMonitor" width="424" height="284" border="0" /></a></p>
<p align="left">Allons un peu plus loin en ajoutons dans notre code C#, un nouveau StateObjectLink :</p>
<p></p><pre class="crayon-plain-tag">[HWMonitorStateObjectLink(HWMonitorStateObjectNames._intelcpu_0_load_0)]
public StateObjectCollectionNotifier CPUs { get; set; }</pre><p></p>
<p align="left">Ce “lien” ne précise que le nom du StateObject (ici “/intelcpu/0/load/0”) et le package (HWMonitorStateObjectLink est la classe générée qui spécifie implicitement le package à “HWMonitor).</p>
<p align="left">De ce fait, toutes les consommations CPU mesurées par les instances du package HWMonitor seront captées par ce “link” ! On utilisera donc un StateObject<strong>Collection</strong>Notifier car on aura autant de StateObjects qu’on a d’instance de ce package.</p>
<p align="left">Dans la vue XAML, ajoutons un menu déroulant (combobox) pour afficher le nom des sentinelles (= le nom des machines) des StateObjects de votre collection “CPUs” :</p>
<p></p><pre class="crayon-plain-tag">&lt;ComboBox x:Name="comboBox" ItemsSource="{Binding Path=CPUs}" DisplayMemberPath="Value.SentinelName" HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" /&gt;</pre><p></p>
<p align="left">Enfin, modifions une nouvelle fois notre label. Cette fois ci la valeur à afficher n’est pas celle du StateObject “CPU”, mais celle du StateObject sélectionné par la combobox :</p>
<p></p><pre class="crayon-plain-tag">&lt;Label x:Name="label" Content="{Binding ElementName=comboBox, Path=SelectedItem.DynamicValue.Value}" ContentStringFormat="{}{0:N2}%" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48"/&gt;</pre><p></p>
<p align="left">On obtient donc la possibilité de suivre la consommation de chaque machine (= sentinelle) où le package HWMonitor est déployé :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-201.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-177.png" alt="image" width="424" height="286" border="0" /></a></p>
<p align="left">Pour terminer on pourrait également ajouter un StateObjectLink qui contiendrait TOUS les StateObjects produits par les packages HWMonitor, peut importe le nom du StateObject et la sentinelle :</p>
<p></p><pre class="crayon-plain-tag">[HWMonitorStateObjectLink]
public StateObjectCollectionNotifier HWMonitor { get; set; }</pre><p></p>
<p align="left">Pour afficher toutes ces données, on peut utiliser un DataGrid lié à votre collection de StateObject “HWMonitor” :</p>
<p></p><pre class="crayon-plain-tag">&lt;DataGrid ItemsSource="{Binding HWMonitor}"&gt;&lt;/DataGrid&gt;</pre><p></p>
<p align="left">On obtiendrait une vue avec deux colonnes, la propriété DynamicValue et Value des StateObjectNotifier :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-198.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="DataGrid sur une collection de StateObjectNotifier" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-174.png" alt="DataGrid sur une collection de StateObjectNotifier" width="424" height="426" border="0" /></a></p>
<p align="left">Pour rendre cela plus visuelle, définissions explicitement des colonnes avec le nom de la sentinelle, le nom du StateObject, la propriété “Name” de la valeur du StateObject  (le nom du compteur), la “Value” et l’unité de la mesure (Unit).</p>
<p align="left">En XAML cela se traduit par le code suivant :</p>
<p></p><pre class="crayon-plain-tag">&lt;DataGrid ItemsSource="{Binding HWMonitor}" AutoGenerateColumns="False" Margin="0, 0, 0, 40"&gt;
    &lt;DataGrid.Columns&gt;
        &lt;DataGridTextColumn Header="Sentinel" Binding="{Binding Path=Value.SentinelName}"&gt;&lt;/DataGridTextColumn&gt;
        &lt;DataGridTextColumn Header="StateObject name" Binding="{Binding Path=Value.Name}"&gt;&lt;/DataGridTextColumn&gt;
        &lt;DataGridTextColumn Header="Counter name" Binding="{Binding Path=DynamicValue.Name}"&gt;&lt;/DataGridTextColumn&gt;
        &lt;DataGridTextColumn Header="Value" Binding="{Binding Path=DynamicValue.Value}"&gt;&lt;/DataGridTextColumn&gt;
        &lt;DataGridTextColumn Header="Unit" Binding="{Binding Path=DynamicValue.Unit}"&gt;&lt;/DataGridTextColumn&gt;
    &lt;/DataGrid.Columns&gt;
&lt;/DataGrid&gt;</pre><p></p>
<p align="left">Le résultat :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-202.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="StateObjects des packages HWMonitor" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-178.png" alt="StateObjects des packages HWMonitor" width="424" height="615" border="0" /></a></p>
<p align="left">Prenez garde car les StateObjects sont mis à jour dans le StateObjectsCollectionNotifier par des threads différents. Vous risquez donc d’avoir des exceptions du type “<em>An ItemsControl is inconsistent with its items source</em>”. Afin d’éviter ce genre d’erreur, utilisez la  méthode “<em>BindingOperations.EnableCollectionSynchronization</em>”.</p>
<p align="left">Pour cela, dans votre classe, ajoutez un objet de synchronisation :</p>
<p></p><pre class="crayon-plain-tag">private static object _syncLock = new object();</pre><p></p>
<p align="left">Puis dans le constructeur de votre fenêtre, après le <em>InitializeComponent()</em>, activez la synchronisation de la collection sur votre StateObjectCollectionNotifier (ici nommé ‘HWMonitor’) :</p>
<p></p><pre class="crayon-plain-tag">BindingOperations.EnableCollectionSynchronization(HWMonitor, _syncLock);</pre><p></p>
<p align="left">Si lancez votre package dans une Constellation avec plusieurs instances du packages HWMonitor sur vos différentes sentinelles, vous aurez une vision temps réel de l’ensemble de vos machines Windows avec seulement ces quelques lignes de XAML et Constellation :</p>
<p align="left"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-203.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border: 0px;" title="StateObjects des packages HWMonitor" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-179.png" alt="StateObjects des packages HWMonitor" width="424" height="580" border="0" /></a></p>
<h3 align="left">Déployez votre package UI</h3>
<h4 align="left">Publier le package</h4>
<p align="left">Le sujet a été traité dans <a href="/getting-started/creez-votre-premier-package-constellation-en-csharp/#Publier_son_package_dans_Constellation">le guide de démarrage,</a> il suffit de cliquer-droit sur votre projet et sélectionner dans le menu Constellation “Publish On Constellation” ou directement depuis la toolbar :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-170.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Publication du package" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-147.png" alt="Publication du package" width="424" height="97" border="0" /></a></p>
<p align="left">Vous pourrez alors choisir le type de publication (Local ou Upload sur le serveur Constellation) :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-157.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Publication du package" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-134.png" alt="Publication du package" width="424" height="212" border="0" /></a></p>
<h4 align="left">Déployer le package sur une sentinelle UI</h4>
<p align="left">Vous devez impérativement déployer un package “UI” sur une sentinelle UI. Si vous tentez d’ajouter un package UI sur une sentinelle service, le package démarrera mais aucune fenêtre ne pourra être visible (le service ne peut pas interagir avec le bureau Windows).</p>
<p align="left">Les sentinelles UI ont le suffixe “_UI” dans leurs noms. Ici pour cette Constellation, il y a deux sentinelles connectées, l’une de type “Service” et l’autre “UI”, toutes deux sur la même machine (nommé “PO-SEB”).</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-171.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Sentinelles connectées" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-148.png" alt="Sentinelles connectées" width="424" height="263" border="0" /></a></p>
<p align="left">Pour ajouter notre package à la sentinelle UI, vous pouvez éditer la configuration de vos Constellation directement depuis Visual Studio :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-172.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Ajout du package depuis Visual Studio" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-149.png" alt="Ajout du package depuis Visual Studio" width="424" height="232" border="0" /></a></p>
<p align="left">Ou bien depuis la Console Constellation :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-173.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Ajout du package dans une sentinelle UI" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-150.png" alt="Ajout du package dans une sentinelle UI" width="424" height="263" border="0" /></a></p>
<p align="left">Pour déployer la configuration, cliquez sur le bouton “Save &amp; Deploy” depuis la Console, ou directement sur la page des “Packages” cliquez sur “Reload &amp; Deploy”.</p>
<p align="left">Votre package UI sera démarré et vous pourrez le contrôler depuis la Console comme pour les autres packages.</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-174.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="Contrôle du package UI sur la Console" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-151.png" alt="Contrôle du package UI sur la Console" width="424" height="263" border="0" /></a></p>
<h4 align="left">Démarrer son package “manuellement”</h4>
<p align="left">Si vous créez une application à destination d’une borne d’affichage, comme un miroir, le package est démarré automatiquement par la sentinelle (comportement par défaut) et est relancé si le package plante !</p>
<p align="left">Cependant, si votre package est destiné à être utilisé par un utilisateur comme une application Windows classique vous voudriez certainement ne pas la lancer automatiquement au démarrage de la sentinelle. Au contraire vous voudriez que ce soit l’utilisateur qui décide de la lancer en lançant un raccourci Windows par exemple sans devoir se connecter sur la Console de votre Constellation.</p>
<p align="left">Pour cela vous pouvez lancer la sentinelle en passant un ordre en paramètre :</p>
<p></p><pre class="crayon-plain-tag">Constellation.Sentinel.UI.exe &lt;action&gt; &lt;package&gt;</pre><p></p>
<p align="left">Les actions peuvent être :</p>
<ul>
<li>
<div align="left">Start</div>
</li>
<li>
<div align="left">Stop</div>
</li>
<li>
<div align="left">Restart</div>
</li>
<li>
<div align="left">Reload</div>
</li>
</ul>
<p align="left">Par exemple, créons un raccourci sur le bureau vers :</p>
<p></p><pre class="crayon-plain-tag">Constellation.Sentinel.UI.exe Reload MonPackageWPF</pre><p></p>
<p align="left">Ainsi dès que vous double-cliquerez sur ce raccourci, la sentinelle téléchargera la dernière version du package sur le serveur et lancera votre package (Reload) :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-175.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-152.png" alt="image" width="424" height="322" border="0" /></a></p>
<p align="left">Bien entendu, l’état du package sera automatiquement synchronisé dans la Constellation. Vous pourrez donc contrôler l’état du package depuis la Console par exemple.</p>
<p align="left">Par défaut, une sentinelle démarre tous les packages qui lui sont assignés. Si c’est un package destiné à être lancé manuellement par l’utilisateur, vous voudriez peut être ne pas lancer automatiquement le package. Vous pouvez donc définir l’attribut “autoStart” à false au niveau de la configuration de votre package.</p>
<p align="left">Aussi l’ordre d’arrêt d’un package doit provenir du hub de contrôle de Constellation, qui se chargera de communiquer l’ordre au package lui même (de s’arrêter) et à sa sentinelle (de tuer le package si il ne s’est pas arrêté dans le temps imparti).</p>
<p align="left">Seulement, dans un package UI de ce type, c’est à dire “application Windows classique”, l’utilisateur fermera naturellement l’application en cliquant sur la croix rouge en haut à droite !</p>
<p align="left">La sentinelle détectera la mort du processus du package alors qu’elle n’a pas eu l’ordre de Constellation d’arrêter le package ! Du point de vue de la sentinelle, c’est un arrêt brutal !</p>
<p align="left">Elle appliquera donc les options de récupération qui par défaut redémarre un package 30 secondes après un arrêt brutal :</p>
<p align="left"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-176.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border-width: 0px;" title="RecoveryOption par défaut" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-153.png" alt="RecoveryOption par défaut" width="424" height="46" border="0" /></a></p>
<p align="left">Les options par défaut sont définis dans la configuration de la Constellation :</p>
<p align="center"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-177.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" title="RecoveryOption par défaut" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-154.png" alt="RecoveryOption par défaut" width="424" height="137" border="0" /></a></p>
<p align="left">Dans notre cas, nous allons redéfinir ces options de récupération au niveau du package lui même pour ne pas redémarrer un package suite à un arrêt forcé et ne pas démarrer automatiquement notre package au démarrage. La configuration du package sera :</p>
<p align="left"><a href="https://developer.myconstellation.io/wp-content/uploads/2016/03/image-178.png"><img class="colorbox-1563"  loading="lazy" style="background-image: none; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; margin-right: auto; border-width: 0px;" title="Configuration d'un package UI" src="https://developer.myconstellation.io/wp-content/uploads/2016/03/image_thumb-155.png" alt="Configuration d'un package UI" width="424" height="134" border="0" /></a></p>
<p>The post <a rel="nofollow" href="https://developer.myconstellation.io/client-api/net-package-api/packages-ui-wpf-winform/">Créer des packages UI en Winform ou WPF</a> appeared first on <a rel="nofollow" href="https://developer.myconstellation.io">Constellation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://developer.myconstellation.io/client-api/net-package-api/packages-ui-wpf-winform/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Mise en cache de page à l’aide de Disk: Enhanced 

Served from: developer.myconstellation.io @ 2026-01-21 08:19:25 by W3 Total Cache
-->