mercredi 7 mars 2012

Planning Poker



Après le Poker Texas Hold'em, le Poker fermé, le strip Poker, le Poker face, voici le Planning Poker !
Loin d'être une nouvelle version que vous seriez susceptible de voir dans le projet James Bond, le Planning Poker est une méthode de planification des tâches lors d'un développement logiciel.
Imaginons 5 développeurs réunis autour d'un projet et accompagné de leur Project Owner (cf Scrum), qu'ils sont couchés dans des Fatboys en sirotant un café et munis chacun d'une main de 13 cartes existants sous plusieurs versions:
0 - 0.5 - 1 - 2 - 3 - 5 - 8 - 13 - 21 - 34 - 89 - ?
0 - 0.5 - 1 - 2 - 3 - 5 - 8 - 13 - 20 - 40 - 100 - ?

Le Project Owner pose une "User Stories" (Un module à implémenter dans le programme) et le but sera d'estimer le temps nécessaire à sa réalisation. Chaque développeur va alors prendre la carte représentant le nombre de jour qu'il pense nécessaire à la confection du module et la poser face cachée sur la table. On retourne les cartes et découvre les estimations de chaque membre. On discute alors entre développeurs pour comprendre pourquoi l'un a mit plus, et un autre moins, de façon à partager les informations et ainsi avoir un partage de connaissances. On recommence jusqu'à obtenir une même estimation pour chaque membre. En cas de conflit de plus de 5minutes, on fini par choisir la durée la plus longue et on passe à l' "User Stories" suivante.
La durée peut varier entre un "Young Programmer" et un "Senior Programmer" de façon impressionnante, il est donc impensable de baser la durée de développement sur l'un ou l'autre au dépend du second.

On a donc ainsi la possibilité d'estimer la durée des modules et donc le nombre d'entre eux à réaliser durant un sprint. Au final, l'équipe possède un plan des fonctionnalités, de leur durée estimée et de leur importance de façon à organiser leur travail pour un mieux comme on peut le voir dans cet exemple:
Le dernier point à retenir est le facteur de vélocité. C'est un facteur qui va permettre d'adapter l'estimation à la réalité car 100 unités de temps ne se feront pas en 100 unités de temps mais plutôt en 50 ou 80 suivant le niveau. Et ce pour la raison que l'on se base sur des estimations et que donc on ne tombera jamais juste.

mardi 6 mars 2012

Microsoft Innovation Center

Amis du jour, bonjour. Voilà presque un mois que je roule ma bosse à mon stage et je me suis dis qu'il serai peut-être judicieux de faire un petit article au sujet de l'entreprise qui m'accueille, j'ai nommé: Microsoft. Plus exactement le Microsoft Innovation Center de Mons.
Cette ASBL créée à partir de fonds publics en privés s'est fixé comme but, de développer l'IT sous différentes formes en région wallonne et d'encourager la création de sociétés associées à ce même domaine. Elle a également un but très louable qui est d'encadrer des étudiants en dernière année issus des écoles d'informatique de la région et de les mettre en relation avec des entreprises leur proposant un stage. C'est ainsi qu'en cette belle année 2012, on se retrouve à 27 stagiaires au sein de l'entreprise pour développer des projets divers et variés.
Mais vu que des images valent tous les mots du monde, je vous propose de visionner cette vidéo, tournée il y a peu dans nos locaux:



Comme dis précédemment, munis de différents projets nous tentons de mener à bien nos idées. Deux de ces projets relatifs à l'équipement Kinect et au robot d'Aldebaran Nao concernent 4 étudiants (dont je fais partie) et dont je vous propose de retrouver un article écrit par nos collègues de Technocité suivant ce lien.

Le Microsoft Innovation Center propose également un grand nombre de formations et de certification auxquelles vous pouvez vous inscrire ici : http://mic.fikket.be/

Retrouvez toutes nos vidéos sur YouTube : http://www.youtube.com/user/MICBELGIQUE

Le blog du MIC : http://blog.mic-belgique.be/
Le site officiel : http://www.mic-belgique.be/

lundi 5 mars 2012

Gestion des logs avec NLog

Aujourd'hui c'est lundi, c'est gratuit, dehors c'est le smog alors attaquons les logs.
Mais un log c'est quoi?
Et bien ça peut être bien des choses mais de façon simplifiée, c'est une façon de garder une trace de certains évènements qui se sont produits durant l'exécution du programme. On peut gérer ces logs de plusieurs façons mais les plus conventionnelles sont l'affichage dans un fichier texte ou dans la console.
On va dés lors, à certains moments dans notre code, placer des enregistrements du contenu de variables de façon à les analyser à posteriori.

On enregistre 3 types d'informations:
  • Le débugging: Ce qui va nous permettre en analysant les logs de voir ce qui génère les bugs et que l'on a pas su voir via le débogueur classique de Visual Studio par exemple.
  • Le tracing: On va consigner les évènements qui se sont produits dans un but de statistiques ou analyse de fonctionnement. On trouvera ainsi la trace d'une connexion à une base de donnée, le chargement d'un module, l'adresse d'une requête, ...
  • La gestion des erreurs: Une fois le logiciel fonctionnel, on va pouvoir consigner les évènements anormaux qui ont nuis au fonctionnement normal du programme. Un plantage, une requête échouée, un fichier inexistant, une écriture refusée, un serveur indisponible, ...

Bien vite indispensable, les logs doivent se faire au fur et à mesure de la conception du produit. Ainsi lors d'un plantage, une consultation des logs permet de voir la dernière action effectuée correctement et peut-être même celle qui a échoué, selon la façon que l'on a implémenté nos logs.
De façon à simplifier le travail, des outils sont mis à notre disposition, l'un d'eux est "NLog" que vous allez pouvoir télécharger via ce lien: ici
Un autre système de log se nomme "Log4Net" et est développé par Apache: ici
Des tutoriels sont disponibles sur leur site respectif et sont suffisant pour une structure simple.

Il est également intéressant de lire cet article sur la façon de reporter un bug de façon efficace en ce lieu
Et pour ceux qui pensent que le débogueur de Visual Studio ne sert qu'à mettre des points d'arrêts, voici deux liens possédant divers astuces pour vous faire gagner du temps: Ici et là.


System.Windows.MessageBox.Show("Enjoy !");

vendredi 2 mars 2012

Incorporer un texte dans une image

Aujourd'hui, petite astuce. Imaginez que vous possédez un panel d'images associées à des données mais que vu leur nombre il n'est pas toujours possible de créer une image différente pour chaque. Et bien la solution pourrai être de partir d'un corps commun et d'y ajouter le nom de la donnée de façon à pouvoir différencier les images une fois affichées dans le panel.



Tout d'abord, le traitement sur le texte à incorporer se fera via les lignes suivantes:
Font font = new Font("arial", 24, System.Drawing.FontStyle.Bold);
StringFormat format = new StringFormat {Alignment = StringAlignment.Center,
                                                    LineAlignment = StringAlignment.Center };

On extrait notre image commune et on la place dans une variable Image:
Image img = Image.FromFile("Lien vers le fichier image");
Image tmpImg = new Bitmap(img, img.Width, img.Height);
Graphics graphics = Graphics.FromImage(tmpImg);

Pour pouvoir contrôler l'endroit où le texte se situera sur l'image, on crée un rectangle (ou un point, un cercle, une ellipse, ...) dont les dimensions ne devront pas dépasser la résolution de l'image. Une petite marge permettra de ne pas voir le texte collé au bord. Ce qui donne pour une image en 300x180:
const float x = 10.0F;
const float y = 10.0F;
const float width = 280.0F;
const float height = 160.0F;
RectangleF drawRect = new RectangleF(x, y, width, height);

On place tous ces ingrédients en commun pour obtenir notre résultat:
graphics.DrawString("Mon superbe texte à incorporer", font, Brushes.White, drawRect, format);

Comme il serait dommage de devoir recréer ces images à chaque fois, il serait intéressant de stocker notre image dans un fichier temporaire duquel il sera plus simple d'en extraire la donnée:
tmpImg.Save("Lien vers le fichier à créer");


peace and prosperity!

mardi 28 février 2012

Scrum What The ... ?

Scrum est un site sur le rugby et ce qui s'y rapporte: Les dents cassées, les nuques brisées, les tonnes du boues, les contacts (qui tueraient un joueur de football en un instant), les barbus, les ... euh non, peut-être que non en fait. Car imaginer des informaticiens barbus d'accord, mais vous avez déjà vu de la boue dans un pc vous?

Donc en fait Scrum, en réalité, est un framework dédié à la gestion de projets. Cette méthodologie n'est pas une manière de développer mais plutôt de gérer le développement. Elle se base sur une série de principes simples: Une équipe de développeurs hétéroclite composée de 5 à 9 personnes environ, d'un "Project Owner" qui sera la voix du groupe et un "Scrum Master" qui lui n'est pas un développeur et dont le rôle est de faire tomber les obstacles pouvant ralentir le développement d'un produit.
Armé de ces collaborateurs, l'équipe va travailler sur des cycles de 1 mois durant lesquels un produit livrable sera créé. Durant cette période, des réunions quotidiennes, appelées "mêlée quotidienne" seront organisées et d'autres se feront en début et fin de cycle.


Mais qu'est ce que le rugby vient faire la dedans sérieux? Et bien simplement car l'ensemble des termes sont tirés du monde du rugby, probablement pour faire croire aux informaticiens qu'ils faisaient du sport chaque jour...

Pour d'avantages d'informations sur le sujet, je vous invite à lire les liens suivants: ici et là

et de passer le test: en ce lieu

jeudi 23 février 2012

MEF - Managed Extensibility Framework

Mais qu'est ce que c'est que cet MEF? Voilà à peu de chose prêt ce que je me suis demandé lorsque l'on m'en a parlé. Oui mais comment ça marche? Est la seconde question qui m'a tenu pendant des heures.

En fait, MEF est une chose très simple mais possédant peut de documentation claire, chose que je vais tenter de faire de façon à éviter à d'autres que moi de perdre un temps précieux à trouver de l'aide sur la toile.

Le but de MEF est d'importer des plugins de façon simple, qu'il se trouve dans le même projet ou sous forme d'un fichier ".dll" et c'est là que ça devient intéressant. Car la particularité de MEF comparé à une autre méthode est le fait que lors d'un appel d'un plugin, si il est là il sera traité, si ils sont plusieurs, ils le seront tous et si malheureusement la recherche ne donne rien cela ne déclanche aucunes erreurs c'est plutôt "Je vais charger des plugins. Oh mais je trouve rien là où ils devraient être. Bon ben tampis, je continue ma route". Son comportement est similaire à un bus qui se stop à un arrêt et qu'il y ai des passagers ou non, ça ne change rien pour lui.
Dans le cas présent, on va chercher des ".dll" dans un dossier "plugins" et y récupérer des donneés.
Dans votre projet et dans vos plugins, il faut rajouter la référence de Visual Studio 2010 "System.ComponentModel.Composition" car, bien que basé sur de l'opensource, le framework est désormais inclus dans le framework .Net.

Le code de notre dll est le suivant:
Plugin10.cs
using System.ComponentModel.Composition;
using GPIInterface;

namespace Plugin10
{
    [Export(typeof(IGrany))]
    public class Plugin10 : IGrany
    {
        public string PluginTitle(){
            return "Plugin numéro 10";
        }
        public string PluginDescription(){
            return "Ceci est le premier plugin";
        }
        public string PluginImageName(){
            return "10.jpg";
        }
        public string PluginName(){
            return "Plugin10";
        }
    }
}

Comment on peut le voir, le plugin implémente une interface qui défini simplement les méthodes qui doivent se trouver dans tous les plugins. Mais cela ne change rien à notre projet.
La partie la plus vitale est la ligne "[Export(typeof(IGrany))]" où l'on explique ce que l'on exporte et quelle interface est utilisée.
Le fichier qui importe devra posséder la ligne :
[ImportMany(AllowRecomposition = true)]
        public List<IGrany> Applications { get; set; }

Ensuite dans le même fichier, se trouve une méthode qui possède le code suivant:

MainWindow.cs
AggregateCatalog catalog = new AggregateCatalog(new DirectoryCatalog("Plugins"),
                new AssemblyCatalog(Assembly.GetExecutingAssembly()));
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
           
foreach (var apps in Applications)
{
   ArrayList plugin = new ArrayList();
   plugin.Insert(0, apps.PluginDescription());
   plugin.Insert(1, apps.PluginImageName());
   plugin.Insert(2, apps.PluginName());
   plugin.Insert(3, apps.PluginTitle());
   _dataPlugins.Add(plugin);
}

Les trois premières lignes sont indispensables pour aller chercher les plugins dans le dossier et le reste permet d'en extraire les données de façon indépendante à leur nombre.

On peut dés lors ajouter des dll en quantité dans le dossier et ils seront tous gérés sans modifications du code principale.

Pour une bonne explication sur les bases de MEF, je vous recommande la vidéo suivante: http://channel9.msdn.com/Events/TechDays/TechDays-2011-Belgium/TD029
Enjoy!

FluidKit - ElementFlow

Après avoir vu les transistions, arrêtons nous un instant sur une seconde capacité du FluidKit, l'ElementFlow. Cet élément permet de faire défiler des images de façon fluide et stylée sans avoir besoin de se plonger dans un code sans fin du Xaml.
Tout comme pour l'article précédent, il est indispensable d'ajouter la dll du kit avant tout opération.
Je ne vais pas détailler toutes les options de configuration disponible, mais me contenter de vous offrir une interface contenant l'éssentiel de ce que l'on peut attendre face à une libraire d'images, c'est à dire une interface de visualisation et un "slider".

Code Xaml
MainWindow.xaml

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:Controls="clr-namespace:FluidKit.Controls;assembly=FluidKit"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Samples="clr-namespace:Fluid_ElementFlow2" x:Class="Fluid_ElementFlow2.MainWindow"
        mc:Ignorable="d">
    <Window.Resources>
        <Samples:StringCollection x:Key="DataSource" />

        <DataTemplate x:Key="TestDataTemplate"
                      DataType="{x:Type sys:String}">
            <Border x:Name="ElementVisual"
                    Background="White"
                    Padding="5"
                    BorderThickness="5"
                    BorderBrush="LightGray"
                    Grid.Row="0">
                <Image Source="{Binding}"
                       Stretch="Fill" />
            </Border>
        </DataTemplate>

    </Window.Resources>
    <Grid Background="#FF1A1A1A" FocusManager.FocusedElement="{Binding ElementName=_elementFlow}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.5*" />
            <ColumnDefinition Width="0.5*" />
        </Grid.ColumnDefinitions>

        <StackPanel Orientation="Vertical"
                    Grid.Row="1"
                    Grid.Column="0"
                    Grid.ColumnSpan="2"
                    Margin="5">
            <Slider x:Name="_selectedIndexSlider"
                    Minimum="0"
                    Value="0"
                    ValueChanged="ChangeSelectedIndex" />
        </StackPanel>
        <Controls:ElementFlow x:Name="_elementFlow"
                              Grid.Row="0"
                              Grid.Column="0"
                              Grid.ColumnSpan="2"
                              ItemsSource="{DynamicResource DataSource}"
                              ItemTemplate="{DynamicResource TestDataTemplate}"
                              TiltAngle="45"
                              ItemGap="0.4"
                              FrontItemGap="1.0"
                              PopoutDistance="2.2"
                              ElementWidth="600"
                              ElementHeight="400"
                              Margin ="0,-120,0,0"
                              SelectedIndex="{Binding Value, ElementName=_selectedIndexSlider}">
            <Controls:ElementFlow.Layout>
                <Controls:CoverFlow />
            </Controls:ElementFlow.Layout>
            <Controls:ElementFlow.Background>
                <LinearGradientBrush EndPoint="0.5,1"
                                     StartPoint="0.5,0">
                    <GradientStop Color="#FF181818" />
                    <GradientStop Color="#FF7A7A7A"
                                  Offset="0.5" />
                    <GradientStop Color="#FF1A1A1A"
                                  Offset="1" />
                </LinearGradientBrush>
            </Controls:ElementFlow.Background>
            <Controls:ElementFlow.Camera>
                <PerspectiveCamera FieldOfView="50"
                                   Position="0,1,6"
                                   LookDirection="0,-1,-6" />
            </Controls:ElementFlow.Camera>
        </Controls:ElementFlow>

        <TextBlock x:Name="_elementTitle"
                           Foreground="White"
                           FontWeight="Bold"
                           VerticalAlignment="Top"
                           HorizontalAlignment="Center"
                           Margin="10,20,10,10"
                           Grid.Row="0"
                           Grid.Column="0"
                           Grid.ColumnSpan="2"
                           FontSize="30"
                           FontFamily="Arial"
                           Text=""/>
       
        <TextBlock x:Name="_osTitle"
                   Foreground="White"
                   FontWeight="Bold"
                   VerticalAlignment="Top"
                   HorizontalAlignment="Right"
                   Margin="10,10,10,10"
                   Grid.Row="0"
                   Grid.Column="1"
                   FontSize="30"
                   FontFamily="Times New Roman"
                   Text="Grany OS"/>
      </Grid>
</Window>




Code C#
MainWindow.cs

using System;
using System.IO;
using System.Windows;
using FluidKit.Controls;
using System.Collections.Generic;
using System.ComponentModel.Composition;

namespace Fluid_ElementFlow2
{
    public partial class MainWindow
    {
        private StringCollection _dataSource;

        [ImportMany(AllowRecomposition = true)]
        public List<IGrany> Applications { get; set; }
       
  public MainWindow()
  {
   InitializeComponent();
   Loaded += Window1Loaded;
  }

        /// <summary>
        /// Configuration of variables and environment
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
  private void Window1Loaded(object sender, RoutedEventArgs e)
  {
            _elementFlow.Layout = new CoverFlow();
            _dataSource = FindResource("DataSource") as StringCollection;
            AddCards(); // Loading of application's pictures
            _selectedIndexSlider.Maximum = _elementFlow.Items.Count - 1;
            _elementFlow.SelectedIndex = _elementFlow.Items.Count / 2;
  }

               
  /// <summary>
  /// Update informations during navigation between applications
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="args"></param>
        private void ChangeSelectedIndex(object sender, RoutedPropertyChangedEventArgs<double> args)
  {
            int index = (int)args.NewValue;
            string source = _dataSource[index];
            string[] titleWithExtension = source.Split(new Char[] { '\\' });
            string[] titleWithoutExtension = titleWithExtension[titleWithExtension.Length - 1].Split(new Char[] { '.' });
            _elementFlow.SelectedIndex = index;
            _elementTitle.Text = titleWithoutExtension[0];
  }
       
        /// <summary>
        /// Search all the plugins available
        /// </summary>
        private void AddCards()
        {
            string path = Directory.GetCurrentDirectory()+"\\Images";
            string[] fichiers = Directory.GetFiles(path);
            foreach (string fichier in fichiers)
            {
                _dataSource.Insert(_dataSource.Count, fichier);
            }
        }
    }
}



StringCollection.cs

using System.Collections.ObjectModel;
namespace GranyPluginsImplement
{
    public class StringCollection : ObservableCollection<string>
    {
        public StringCollection()
        {
        }
    }
}
 

Avec ces 3 fichiers, vous obtenez une interface simple et agréable que vous pourrez à votre guise personnaliser, le rendu étant le suivant:



Si vous sauhaitez d'avantages de possibilités de ElementFlow, je vous conseille de regarder l'exemple fourni avec le code source.