<repo_name>Omybot/GoBot<file_sep>/GoBot/GoBot/BoardContext/GameBoard.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using AStarFolder;
using Geometry.Shapes;
using GoBot.Strategies;
using GoBot.GameElements;
using GoBot.Devices;
using GoBot.Threading;

namespace GoBot.BoardContext
{
    public class GameBoard
    {
        private static Obstacles _obstacles;
        private static Color _myColor = ColorRightYellow;
        private static int _score;

        private static int _initialOpponentRadius { get; set; }
        private static int _currentOpponentRadius { get; set; }

        private static int _startZoneCounter = 0;
        private static bool _startZonePresence = false;
        private static bool _startZoneSizeReduction = false;
        private static ThreadLink _linkStartZone;

        private static Strategy _currentStrategy;
        private static AllGameElements _elements;
        private static List<IShape> _detections;

        private static Polygon _bounds;

        public GameBoard()
        {
            if (!Execution.DesignMode)
            {
                _elements = new AllGameElements();
                _initialOpponentRadius = 200;
                _currentOpponentRadius = _initialOpponentRadius;

                _obstacles = new Obstacles(Elements);
                CreateGraph(75);

                //Balise.PositionEnnemisActualisee += Balise_PositionEnnemisActualisee;

                Robots.MainRobot.UpdateGraph(_obstacles.FromAllExceptBoard);

                // TODOEACHYEAR Définir ici la zone correspondant au plateau où les detections d'adversaire sont autorisées (enlever les pentes par exemple)
                _bounds = new PolygonRectangle(new RealPoint(0, 0), 3000, 2000);

                StartDetection();

                if (Config.CurrentConfig.IsMiniRobot)
                {
                    _currentStrategy = new StrategyMini();
                }
                else
                {
                    _currentStrategy = new StrategyMatch();
                }
            }
        }

        public static Strategy Strategy
        {
            //TODO2020 gérer un démarrage de stratégie mieux défini (avec une publique ?)
            get { return _currentStrategy; }
            set { _currentStrategy = value; }
        }

        public static void AddObstacle(IShape shape)
        {
            _obstacles.AddObstacle(shape);
        }

        public static AllGameElements Elements
        {
            get { return _elements; }
        }

        public static List<IShape> Detections
        {
            get { return _detections; }
            set { _detections = value; }
        }

        public static Color MyColor
        {
            get { return _myColor; }
            set
            {
                if (_myColor != value)
                {
                    _myColor = value;
                    MyColorChange?.Invoke(null, null);
                    if (_obstacles != null) Robots.MainRobot.UpdateGraph(_obstacles.FromAllExceptBoard);


                    if (Config.CurrentConfig.IsMiniRobot)
                    {
                        _currentStrategy = new StrategyMini();
                    }
                    else
                    {
                        _currentStrategy = new StrategyMatch();
                    }
                }
            }
        }
        public static event EventHandler MyColorChange;

        public static int Score
        {
            get { return _score; }
            set { _score = value; ScoreChange?.Invoke(_score); }
        }

        public delegate void ScoreChangeDelegate(int score);
        public static event ScoreChangeDelegate ScoreChange;

        public static Color ColorLeftBlue { get { return Color.FromArgb(0, 91, 140); } }
        public static Color ColorRightYellow { get { return Color.FromArgb(247, 181, 0); } }
        public static Color ColorNeutral { get { return Color.White; } }

        /// <summary>
        /// Largeur de la table (mm)
        /// </summary>
        public static int Width { get { return 3000; } }

        /// <summary>
        /// Hauteur de la table (mm)
        /// </summary>
        public static int Height { get { return 2000; } }

        /// <summary>
        /// Largeur de la bordure de la table (mm)
        /// </summary>
        public static int BorderSize { get { return 22; } }

        /// <summary>
        /// Diamètre actuellement considéré pour un adversaire
        /// </summary>
        public static int OpponentRadius
        {
            get { return _currentOpponentRadius; }
            set { _currentOpponentRadius = value; } // TODO2020 une publique non ? Reduce...
        }

        /// <summary>
        /// Diamètre initiallement considéré pour un adversaire
        /// </summary>
        public static int OpponentRadiusInitial
        {
            get { return _initialOpponentRadius; }
        }

        /// <summary>
        /// Liste complète des obstacles fixes et temporaires
        /// </summary>
        public static IEnumerable<IShape> ObstaclesAll
        {
            get
            {
                return _obstacles.FromAll.ToList(); // Pour concretiser la liste
            }
        }

        /// <summary>
        /// Liste complète des obstacles fixes et temporaires
        /// </summary>
        public static IEnumerable<IShape> ObstaclesBoardConstruction
        {
            get
            {
                return _obstacles.FromBoardConstruction.ToList(); // Pour concretiser la liste
            }
        }

        /// <summary>
        /// Liste complète des obstacles vus par un lidart au niveau du sol
        /// </summary>
        public static IEnumerable<IShape> ObstaclesLidarGround
        {
            get
            {
                return _obstacles.FromBoard.Concat(Elements.AsVisibleObstacles).ToList(); // Pour concretiser la liste
            }
        }

        public static IEnumerable<IShape> ObstaclesOpponents
        {
            get
            {
                return _obstacles.FromDetection.ToList();
            }
        }

        public static bool IsInside(RealPoint p, int securityDistance = 0)
        {
            bool inside = _bounds.Contains(p);
            if (inside && securityDistance > 0)
                inside = !_bounds.Sides.Exists(o => o.Distance(p) < securityDistance);

            return inside;
        }

        public static void StartMatch()
        {
            _linkStartZone = ThreadManager.CreateThread(link => CheckStartZonePresence());
            _linkStartZone.StartInfiniteLoop(1000);
            _linkStartZone.Name = "Check zone départ";
        }

        private static void CheckStartZonePresence()
        {
            if (_startZonePresence)
                _startZoneCounter++;
            else
            {
                _startZoneCounter = 0;
                _startZoneSizeReduction = false;
            }

            _startZonePresence = false;

            if (_startZoneCounter >= 5)
                _startZoneSizeReduction = true;
        }

        public static void StartDetection()
        {
            if (AllDevices.LidarAvoid != null)
            {
                AllDevices.LidarAvoid.StartLoopMeasure();
                AllDevices.LidarAvoid.NewMeasure += LidarAvoid_NewMeasure;
            }
        }

        public static void SetOpponents(List<RealPoint> positions)
        {
            if (_obstacles != null && Strategy != null && Strategy.IsRunning)
            {
                // Positions ennemies signalées par la balise

                // TODOEACHYEAR Tester ICI si on veut devenir aveugle à la présence d'aversaire dans notre zone de départ au début du match
                if (Strategy != null && Strategy.TimeSinceBegin.TotalSeconds < 20)
                    positions = positions.Where(o => !StartZoneMe().Contains(o)).ToList();

                if (GameBoard.Strategy == null)
                {
                    // TODOEACHYEAR Tester ICI ce qu'il y a à tester en fonction de la position de l'ennemi AVANT de lancer le match
                }
                else
                {
                    // TODOEACHYEAR Tester ICI ce qu'il y a à tester en fonction de la position de l'ennemi PENDANT le match

                    Elements.SetOpponents(positions);

                    if (Robots.MainRobot.IsSpeedAdvAdaptable && positions.Count > 0)
                    {
                        double minOpponentDist = positions.Min(p => p.Distance(Robots.MainRobot.Position.Coordinates));
                        Robots.MainRobot.SpeedConfig.LineSpeed = SpeedWithOpponent(minOpponentDist, Config.CurrentConfig.ConfigRapide.LineSpeed);
                    }
                }

                _obstacles.SetDetections(positions.Select(p =>
                {
                    // TODOEACHYEAR C'est ICI qu'on s'amuse à rédurie le périmètre d'un robot adverse qui n'est pas sorti de sa zone de départ
                    if (StartZoneOpponent().Contains(p))
                    {
                        _startZonePresence = true;
                        if (_startZoneSizeReduction)
                            return new Circle(p, _currentOpponentRadius / 2);
                        else
                            return new Circle(p, _currentOpponentRadius);
                    }

                    return new Circle(p, _currentOpponentRadius);

                }).ToList());

                //_obstacles.SetDetections(positions.Select(p => new Circle(p, RayonAdversaire)).ToList());

                // TODO2020 : faudrait pouvoir mettre à jour le graph sans refaire les zones interdites de couleur (sauvegarder un résultat après application de la couleur ?)
                // parce que les carrés c'est beaucoup plus long que les ronds...
                Robots.MainRobot.UpdateGraph(_obstacles.FromAllExceptBoard);
                Robots.MainRobot.OpponentsTrajectoryCollision(_obstacles.FromDetection);
            }
        }

        public static IShape StartZoneMe()
        {
            if (GameBoard.MyColor == GameBoard.ColorRightYellow)
                return StartZoneRight();
            else
                return StartZoneLeft();
        }

        public static IShape StartZoneOpponent()
        {
            if (GameBoard.MyColor == GameBoard.ColorRightYellow)
                return StartZoneLeft();
            else
                return StartZoneRight();
        }

        private static IShape StartZoneLeft()
        {
            return new PolygonRectangle(new RealPoint(0, 300), 400, 900);
        }

        private static IShape StartZoneRight()
        {
            return new PolygonRectangle(new RealPoint(2550, 300), 400, 900);
        }

        public static void Init()
        {
        }

        private static void LidarAvoid_NewMeasure(List<RealPoint> measure)
        {
            List<RealPoint> pts = measure.Where(o => IsInside(o, 100)).ToList();
            pts = pts.Where(o => o.Distance(Robots.MainRobot.Position.Coordinates) > 300).ToList();

            List<List<RealPoint>> groups = pts.GroupByDistance(50);

            List<RealPoint> obstacles = new List<RealPoint>();

            foreach (List<RealPoint> group in groups)
            {
                if (DetectionIsOpponent(group))
                {
                    RealPoint center = group.GetBarycenter();
                    if (!obstacles.Exists(o => o.Distance(center) < 150))
                        obstacles.Add(center);
                }
            }

            SetOpponents(obstacles);
        }

        private static bool DetectionIsOpponent(List<RealPoint> group)
        {
            return group.Count > 2;// && group.GetContainingCircle().Radius < 100; // On pourrai ignorer les murs avec la contrainte de taille, mais c'est pratique de ne pas rentrer dans les murs finallement...
        }

        /// <summary>
        /// Teste si on point est contenu dans la table
        /// </summary>
        /// <param name="croisement">Point à tester</param>
        /// <returns></returns>
        public static bool Contains(RealPoint point)
        {
            return new PolygonRectangle(new RealPoint(0, 0), Height, Width).Contains(point);
        }

        /// <summary>
        /// Crée le graph du pathfinding.
        /// </summary>
        /// <param name="resolution">Distance (mm) entre chaque noeud du graph en X et Y</param>
        /// <param name="distanceLiaison">Distance (mm) jusqu'à laquelle les noeuds sont reliés par un arc. Par défaut on crée un graph minimal (liaison aux 8 points alentours : N/S/E/O/NE/NO/SE/SO)</param>
        private void CreateGraph(int resolution, double distanceLiaison = -1)
        {
            if (distanceLiaison == -1)
                distanceLiaison = Math.Sqrt((resolution * resolution) * 2) * 1.05; //+5% pour prendre large

            Robots.MainRobot.Graph = new Graph(resolution, distanceLiaison);

            // Création des noeuds
            for (int x = resolution / 2; x < Width; x += resolution)
                for (int y = resolution / 2; y < Height; y += resolution)
                    Robots.MainRobot.Graph.AddNode(new Node(x, y), _obstacles.FromBoard, Robots.MainRobot.Radius, true);
        }

        /// <summary>
        /// Retourne la vitesse à adopter en fonction de la proximité de l'adversaire
        /// </summary>
        /// <param name="opponentDist">DIstance de l'adveraire</param>
        /// <param name="maxSpeed">Vitesse de pointe maximum</param>
        /// <returns>Vitese maximale prenant en compte la proximité de l'adversaire</returns>
        private static int SpeedWithOpponent(double opponentDist, int maxSpeed)
        {
            double minPower = 0.20;
            double minDist = 250;
            double maxPowerDist = 600;
            double factor;

            if (opponentDist < minDist)
                factor = minPower;
            else
            {
                factor = Math.Min((Math.Pow(opponentDist - minDist, 2) / Math.Pow((maxPowerDist - minDist), 2)) * (1 - minPower) + minPower, 1);
                if (factor < 0.6)
                    factor = 0.6;
                else
                    factor = 1;
            }

            return (int)(factor * maxSpeed);
        }

        public static void RemoveVirtualBuoy(RealPoint center)
        {
            Buoy b = _elements.FindBuoy(center);
            if (b.IsVirtual)_elements.RemoveBuoy(b);
        }
    }
}
<file_sep>/GoBot/GoBot/EventsReplay.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Windows.Forms;
using System.Threading;

namespace GoBot
{

    /// <summary>
    /// Permet de sauvegarder l'historique des trames reçue ainsi que leur heure d'arrivée
    /// </summary>
    [Serializable]
    public class EventsReplay
    {
        /// <summary>
        /// Liste des trames
        /// </summary>
        public List<HistoLigne> Events { get; private set; }

        public EventsReplay()
        {
            Events = new List<HistoLigne>();
        }

        /// <summary>
        /// Ajoute une trame reçue avec l'heure définie
        /// </summary>
        public void AjouterEvent(HistoLigne _event)
        {
            lock (Events)
                Events.Add(_event);
        }

        /// <summary>
        /// Charge une sauvegarde de Replay
        /// </summary>
        /// <param name="nomFichier">Chemin du fichier</param>
        /// <returns>Vrai si la sauvegarde a été correctement chargée</returns>
        public bool Charger(String nomFichier)
        {
            try
            {
                XmlSerializer mySerializer = new XmlSerializer(typeof(List<HistoLigne>));
                using (FileStream myFileStream = new FileStream(nomFichier, FileMode.Open))
                    Events = (List<HistoLigne>)mySerializer.Deserialize(myFileStream);

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Sauvegarde l'ensemble des trames dans un fichier
        /// </summary>
        /// <param name="nomFichier">Chemin du fichier</param>
        /// <returns>Vrai si la sauvegarde s'est correctement déroulée</returns>
        public bool Sauvegarder(String nomFichier)
        {
            try
            {
                XmlSerializer mySerializer = new XmlSerializer(typeof(List<HistoLigne>));
                using (StreamWriter myWriter = new StreamWriter(nomFichier))
                    mySerializer.Serialize(myWriter, Events);

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Permet de simuler la réception des trames enregistrées en respectant les intervalles de temps entre chaque trame
        /// </summary>
        public void Rejouer()
        {
            for (int i = Events.Count - 1; i >= 0; i--)
            {
                Robots.DicRobots[Events[i].Robot].Historique.Log(Events[i].Message, Events[i].Type);

                if (i - 1 > 0)
                    Thread.Sleep(Events[i].Heure - Events[i - 1].Heure);
            }
        }

        public void Trier()
        {
            Events.Sort();
        }
    }
}<file_sep>/GoBot/GoBot/IHM/Panels/PanelActuatorsOnOff.cs
﻿using System;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class PanelActuatorsOnOff : UserControl
    {
        public PanelActuatorsOnOff()
        {
            InitializeComponent();
        }

        private void PanelActuatorsOnOff_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                int y = 20;

                foreach (ActuatorOnOffID actuator in Enum.GetValues(typeof(ActuatorOnOffID)))
                {
                    PanelActuatorOnOff panel = new PanelActuatorOnOff();
                    panel.SetBounds(5, y, grpActuatorsOnOff.Width - 10, panel.Height);
                    panel.SetActuator(actuator);
                    y += panel.Height;
                    grpActuatorsOnOff.Controls.Add(panel);
                }

                grpActuatorsOnOff.Height = y + 5;
                this.Height = grpActuatorsOnOff.Bottom + 3;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Strategies/StrategyRoundTrip.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Actionneurs;
using GoBot.BoardContext;

namespace GoBot.Strategies
{
    /// <summary>
    /// Stratégie qui consiste à faire des aller-retours sur la piste (pour homologuation d'évitement par exemple)
    /// </summary>
    class StrategyRoundTrip : Strategy
    {
        public override bool AvoidElements => false;

        protected override void SequenceBegin()
        {
            Robots.MainRobot.UpdateGraph(GameBoard.ObstaclesAll);

            //Plateau.Balise.VitesseRotation(250);

            // Sortir ICI de la zone de départ pour commencer

            Robots.MainRobot.MoveForward(200);
        }

        protected override void SequenceCore()
        {
            while (IsRunning)
            {
                while (!Robots.MainRobot.GoToPosition(new Position(0, new RealPoint(700, 1250)))) ;
                while (!Robots.MainRobot.GoToPosition(new Position(180, new RealPoint(3000 - 700, 1250)))) ;
            }
        }
    }
}
<file_sep>/GoBot/Composants/SwitchButton.cs
﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace Composants
{
    public partial class SwitchButton : UserControl
    {
        public SwitchButton()
        {
            InitializeComponent();
            _value = false;
            _isMirrored = true;
            Invalidate();
        }

        public delegate void ValueChangedDelegate(object sender, bool value);

        /// <summary>
        /// Se produit lorsque l'état du bouton change
        /// </summary>
        public event ValueChangedDelegate ValueChanged;

        private bool _value;
        private bool _isMirrored;

        private bool _isFocus;

        /// <summary>
        /// Retourne vrai ou faux selon l'état du composant
        /// </summary>
        public bool Value
        {
            get
            {
                return _value;
            }
            set
            {
                if (this._value != value)
                {
                    this._value = value;
                    Invalidate();
                    OnValueChanged();
                }
            }
        }

        /// <summary>
        /// Mettre à vrai pour activer le contrôle de droite à gauche au lieu de gauche à droite
        /// </summary>
        public bool Mirrored
        {
            get
            {
                return _isMirrored;
            }
            set
            {
                _isMirrored = value;
                Invalidate();
            }
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            Value = (!Value);
        }

        protected override void OnEnter(EventArgs e)
        {
            _isFocus = true;
            Invalidate();
            base.OnEnter(e);
        }

        protected override void OnLeave(EventArgs e)
        {
            _isFocus = false;
            Invalidate();
            base.OnLeave(e);
        }

        protected void OnValueChanged()
        {
            ValueChanged?.Invoke(this, _value);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            GraphicsPath path = new GraphicsPath();

            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            int w, h;
            bool ballOnLeft;

            w = Width - 1;
            h = Height - 1;

            path.AddLine(h / 2 + 1, 1, w - h / 2 - 1, 1);
            path.AddArc(new Rectangle(w - h - 1, 1, h - 2, h - 2), -90, 180);
            path.AddLine(w - h / 2 - 1, h - 1, h / 2 + 1, h - 1);
            path.AddArc(new Rectangle(1, 1, h - 2, h - 2), 90, 180);

            Rectangle rBall;

            if (_value)
            {
                Brush b = new LinearGradientBrush(new Rectangle(1, 1, w - 2, h - 2), Color.FromArgb(43, 226, 75), Color.FromArgb(36, 209, 68), 12);
                e.Graphics.FillPath(b, path);
                b.Dispose();

                Pen p = new Pen(Color.FromArgb(22, 126, 40));
                e.Graphics.DrawPath(p, path);
                p.Dispose();

                ballOnLeft = _isMirrored;
            }
            else
            {
                e.Graphics.FillPath(Brushes.WhiteSmoke, path);
                ballOnLeft = !_isMirrored;
                e.Graphics.DrawPath(Pens.LightGray, path);
            }

            if (ballOnLeft)
                rBall = new Rectangle(0, 0, h, h);
            else
                rBall = new Rectangle(w - h, 0, h, h);

            Brush bBall = new LinearGradientBrush(rBall, Color.WhiteSmoke, Color.LightGray, 100);
            e.Graphics.FillEllipse(bBall, rBall);
            bBall.Dispose();

            e.Graphics.DrawEllipse(_isFocus ? Pens.DodgerBlue : Pens.Gray, rBall);

            path.Dispose();

            //base.OnPaint(e);

        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);
        }

        private void SwitchButton_MouseClick(object sender, MouseEventArgs e)
        {
            Focus();
            Value = !Value;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/RealPoint.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using Geometry.Shapes.ShapesInteractions;

namespace Geometry.Shapes
{
    public class RealPoint : IShape, IShapeModifiable<RealPoint>
    {
        public const double PRECISION = 0.01;

        #region Attributs

        private double _x;
        private double _y;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Constructeur par défaut, les coordonnées seront (0, 0)
        /// </summary>
        public RealPoint()
        {
            _x = 0;
            _y = 0;
        }

        /// <summary>
        /// Constructeur par copie
        /// </summary>
        public RealPoint(RealPoint other)
        {
            _x = other._x;
            _y = other._y;
        }

        /// <summary>
        /// Construit des coordonnées selon x et y
        /// </summary>
        /// <param name="x">Abscisse</param>
        /// <param name="y">Ordonnée</param>
        public RealPoint(double x, double y)
        {
            _x = x;
            _y = y;
        }

        #endregion

        #region Propriétés

        /// <summary>
        /// Obtient ou définit la position sur l'axe des abscisses
        /// </summary>
        public double X { get { return _x; } set { _x = value; } }

        /// <summary>
        /// Obtient ou définit  la coordonnée sur l'axe des ordonnées
        /// </summary>
        public double Y { get { return _y; } set { _y = value; } }

        /// <summary>
        /// Obtient la surface du point (toujours 0)
        /// </summary>
        public double Surface { get { return 0; } }

        /// <summary>
        /// Obtient le barycentre du point (toujours lui même)
        /// </summary>
        public RealPoint Barycenter { get { return new RealPoint(this); } }

        #endregion

        #region Opérateurs & Surcharges
        
        public static bool operator ==(RealPoint a, RealPoint b)
        {
            if (a is null || b is null)
                return (a is null && b is null);
            else
                return (Math.Abs(a.X - b.X) < PRECISION && Math.Abs(a.Y - b.Y) < PRECISION);
        }

        public static bool operator !=(RealPoint a, RealPoint b)
        {
            return !(a == b);
        }

        public static RealPoint operator -(RealPoint a, RealPoint b)
        {
            return new RealPoint(a.X - b.X, a.Y - b.Y);
        }

        public static RealPoint operator +(RealPoint a, RealPoint b)
        {
            return new RealPoint(a.X + b.X, a.Y + b.Y);
        }

        public override string ToString()
        {
            return "{" + X.ToString("0.00") + " : " + Y.ToString("0.00") + "}";
        }

        public override bool Equals(object o)
        {
            RealPoint p = o as RealPoint;

            return (p != null) && (p == this);
        }

        public override int GetHashCode()
        {
            //return (int)X ^ (int)Y;
            return (int)(_x * _y);
            //return (int)(_x*10000 + _y);
        }

        public static implicit operator Point(RealPoint point)
        {
            return new Point((int)point.X, (int)point.Y);
        }

        public static implicit operator RealPoint(Point point)
        {
            return new RealPoint(point.X, point.Y);
        }

        public static implicit operator PointF(RealPoint point)
        {
            return new PointF((float)point.X, (float)point.Y);
        }

        public static implicit operator RealPoint(PointF point)
        {
            return new RealPoint(point.X, point.Y);
        }

        #endregion

        #region Distance

        /// <summary>
        /// Retourne la distance minimale entre le PointReel courant et la IForme donnée
        /// </summary>
        /// <param name="shape">IForme testée</param>
        /// <returns>Distance minimale</returns>
        public double Distance(IShape shape)
        {
            double output = 0;

            if (shape is RealPoint) output = RealPointWithRealPoint.Distance(this, shape as RealPoint);
            else if (shape is Segment) output = RealPointWithSegment.Distance(this, shape as Segment);
            else if (shape is Polygon) output = RealPointWithPolygon.Distance(this, shape as Polygon);
            else if(shape is Circle) output = RealPointWithCircle.Distance(this, shape as Circle);
            else if (shape is Line) output = RealPointWithLine.Distance(this, shape as Line);

            return output;
        }

        #endregion

        #region Contient

        /// <summary>
        /// Teste si le PointReel contient la IForme donnée
        /// </summary>
        /// <param name="shape">IForme testée</param>
        /// <returns>Vrai si le PointReel contient la IForme testée</returns>
        public bool Contains(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = RealPointWithRealPoint.Contains(this, shape as RealPoint);
            else if (shape is Segment) output = RealPointWithSegment.Contains(this, shape as Segment);
            else if (shape is Polygon) output = RealPointWithPolygon.Contains(this, shape as Polygon);
            else if (shape is Circle) output = RealPointWithCircle.Contains(this, shape as Circle);
            else if (shape is Line) output = RealPointWithLine.Contains(this, shape as Line);

            return output;
        }

        #endregion

        #region Croisement

        /// <summary>
        /// Teste si le PointReel courant croise la IForme donnée
        /// Pour un PointReel, on dit qu'il croise s'il se trouve sur le contour de la forme avec une marge de <c>PRECISION</c>
        /// </summary>
        /// <param name="shape">IForme testés</param>
        /// <returns>Vrai si le PointReel courant croise la IForme donnée</returns>
        public bool Cross(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = RealPointWithRealPoint.Cross(this, shape as RealPoint);
            else if (shape is Segment) output = RealPointWithSegment.Cross(this, shape as Segment);
            else if (shape is Polygon) output = RealPointWithPolygon.Cross(this, shape as Polygon);
            else if (shape is Circle) output =  RealPointWithCircle.Cross(this, shape as Circle);
            else if (shape is Line) output = RealPointWithLine.Cross(this, shape as Line);

            return output;
        }

        /// <summary>
        /// Retourne les points de croisements entre ce point et la forme donnée. Le croisement ne peut au mieux qu'être le point lui même.
        /// </summary>
        /// <param name="shape">Forme avec laquelle tester les croisements</param>
        /// <returns>Points de croisement</returns>
        public List<RealPoint> GetCrossingPoints(IShape shape)
        {
            List<RealPoint> output = new List<RealPoint>();

            if (shape is RealPoint) output = RealPointWithRealPoint.GetCrossingPoints(this, shape as RealPoint);
            else if (shape is Segment) output = RealPointWithSegment.GetCrossingPoints(this, shape as Segment);
            else if (shape is Polygon) output = RealPointWithPolygon.GetCrossingPoints(this, shape as Polygon);
            else if (shape is Circle) output = RealPointWithCircle.GetCrossingPoints(this, shape as Circle);
            else if (shape is Line) output = RealPointWithLine.GetCrossingPoints(this, shape as Line);

            return output;
        }

        #endregion

        #region Transformations

        /// <summary>
        /// Affecte les coordonnées passées en paramètre
        /// </summary>
        /// <param name="x">Abscisse</param>
        /// <param name="y">Ordonnée</param>
        public void Set(double x, double y)
        {
            _x = x;
            _y = y;
        }

        /// <summary>
        /// Affecte les coordonnées passées en paramètre
        /// </summary>
        /// <param name="x">Abscisse</param>
        /// <param name="y">Ordonnée</param>
        public void Set(RealPoint pos)
        {
            _x = pos.X;
            _y = pos.Y;
        }

        /// <summary>
        /// Retourne un point translaté des distances données
        /// </summary>
        /// <param name="dx">Déplacement sur l'axe X</param>
        /// <param name="dy">Déplacement sur l'axe Y</param>
        /// <returns>Point translaté</returns>
        public RealPoint Translation(double dx, double dy)
        {
            return new RealPoint(_x + dx, _y + dy);
        }

        /// <summary>
        /// Retourne un point qui a subit une rotation selon l'angle et le centre donné
        /// </summary>
        /// <param name="angle">Angle de rotation</param>
        /// <param name="rotationCenter">Centre de la rotation</param>
        /// <returns>Point ayant subit la rotation donnée</returns>
        public RealPoint Rotation(AngleDelta angle, RealPoint rotationCenter)
        {
            RealPoint newPoint = new RealPoint();
            newPoint.X = rotationCenter.X + angle.Cos * (this.X - rotationCenter.X) - angle.Sin * (this.Y - rotationCenter.Y);
            newPoint.Y = rotationCenter.Y + angle.Cos * (this.Y - rotationCenter.Y) + angle.Sin * (this.X - rotationCenter.X);
            return newPoint;
        }

        #endregion

        #region Peinture

        /// <summary>
        /// Dessine le point sur un Graphic
        /// </summary>
        /// <param name="g">Graphic sur lequel dessiner</param>
        /// <param name="outline">Pen pour dessiner le contour du point</param>
        /// <param name="fill">Brush pour le remplissage du point</param>
        /// <param name="scale">Echelle de conversion</param>
        public void Paint(Graphics g, Pen outline, Brush fill, WorldScale scale)
        {
            Point screenPosition = scale.RealToScreenPosition(this);

            Rectangle rect = new Rectangle(screenPosition.X - (int)outline.Width, screenPosition.Y - (int)outline.Width, (int)outline.Width * 2, (int)outline.Width * 2);

            if (fill != null)
                    g.FillEllipse(fill, rect);

            if (outline != null)
            {
                Pen tmp = new Pen(outline.Color);
                g.DrawEllipse(tmp, rect);
                tmp.Dispose();
            }
        }

        #endregion

        #region Statiques

        public static RealPoint Shift(RealPoint realPoint, double dx, double dy)
        {
            return new RealPoint(realPoint.X + dx, realPoint.Y + dy);
        }

        #endregion
    }
}
<file_sep>/GoBot/GeometryTester/TestCircle.cs
﻿using Geometry.Shapes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GeometryTester
{
    [TestClass]
    public class TestCircle
    {
        [TestMethod]
        public void TestConstructorStandard()
        {
            Circle c = new Circle(new RealPoint(10, 20), 30);

            Assert.AreEqual(10, c.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestConstructorZero()
        {
            Circle c = new Circle(new RealPoint(0, 0), 0);

            Assert.AreEqual(0, c.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(0, c.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(0, c.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestConstructorPositionNeg()
        {
            Circle c = new Circle(new RealPoint(-10, -20), 5);

            Assert.AreEqual(-10, c.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(-20, c.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(5, c.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestConstructorRadiusNeg()
        {
            Assert.ThrowsException<ArgumentException>(() => new Circle(new RealPoint(0, 0), -5));
        }

        [TestMethod]
        public void TestConstructorCopy()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = new Circle(c1);

            Assert.AreEqual(10, c2.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c2.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c2.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestTranslationStandard()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = c1.Translation(25, 35);

            Assert.AreEqual(10, c1.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c1.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c1.Radius, RealPoint.PRECISION);

            Assert.AreEqual(10 + 25, c2.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20 + 35, c2.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c2.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestTranslationZero()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = c1.Translation(0, 0);

            Assert.AreEqual(10, c1.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c1.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c1.Radius, RealPoint.PRECISION);

            Assert.AreEqual(10, c2.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c2.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c2.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestTranslationNeg()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = c1.Translation(-10, -20);

            Assert.AreEqual(10, c1.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c1.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c1.Radius, RealPoint.PRECISION);

            Assert.AreEqual(0, c2.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(0, c2.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c2.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestSurfaceStandard()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = new Circle(new RealPoint(0, 0), 30);
            Circle c3 = new Circle(new RealPoint(50, 150), 45);

            Assert.AreEqual(2827.43, c1.Surface, RealPoint.PRECISION);
            Assert.AreEqual(2827.43, c2.Surface, RealPoint.PRECISION);
            Assert.AreEqual(6361.73, c3.Surface, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestSurfaceZero()
        {
            Circle c = new Circle(new RealPoint(10, 20), 0);

            Assert.AreEqual(0, c.Surface, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestBarycenterStandard()
        {
            Circle c = new Circle(new RealPoint(10, 20), 10);

            Assert.AreEqual(10, c.Barycenter.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c.Barycenter.Y, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestBarycenterZero()
        {
            Circle c = new Circle(new RealPoint(0, 0), 10);

            Assert.AreEqual(0, c.Barycenter.X, RealPoint.PRECISION);
            Assert.AreEqual(0, c.Barycenter.Y, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestBarycenterNeg()
        {
            Circle c = new Circle(new RealPoint(-10, -20), 10);

            Assert.AreEqual(-10, c.Barycenter.X, RealPoint.PRECISION);
            Assert.AreEqual(-20, c.Barycenter.Y, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestRotationFromBarycenter()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = c1.Rotation(90);

            Assert.AreEqual(10, c1.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c1.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c1.Radius, RealPoint.PRECISION);

            Assert.AreEqual(10, c2.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c2.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c2.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestRotationFromZero()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = c1.Rotation(90, new RealPoint(0, 0));

            Assert.AreEqual(10, c1.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c1.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c1.Radius, RealPoint.PRECISION);

            Assert.AreEqual(-20, c2.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(10, c2.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c2.Radius, RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestRotationFromStandard()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = c1.Rotation(-90, new RealPoint(5, 5));

            Assert.AreEqual(10, c1.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(20, c1.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c1.Radius, RealPoint.PRECISION);

            Assert.AreEqual(20, c2.Center.X, RealPoint.PRECISION);
            Assert.AreEqual(0, c2.Center.Y, RealPoint.PRECISION);
            Assert.AreEqual(30, c2.Radius, RealPoint.PRECISION);
        }
    }
}<file_sep>/GoBot/Geometry/VolatileResult.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry
{
    /// <summary>
    /// Classe de stockage d'un résultat qui n'est calculé que quand nécessaire et stocké pour une future utilisation.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class VolatileResult<T>
    {
        private T _result;
        private CalculDelegate _calcul;
        private bool _computed;

        public delegate T CalculDelegate();

        /// <summary>
        /// Crée la valeur volatile en spécifiant la fonction de calcul pour obtenir le résultat
        /// </summary>
        /// <param name="calcul"></param>
        public VolatileResult(CalculDelegate calcul)
        {
            _calcul = calcul;
            _computed = false;
        }

        /// <summary>
        /// Obtient la valeur en la calculant automatiquement si c'est le premier appel depuis l'initialisation ou définit la valeur résultat.
        /// </summary>
        public T Value
        {
            get
            {
                if (!_computed)
                {
                    _result = _calcul.Invoke();
                    _computed = true;
                }
                return _result;
            }
            set
            {
                _result = value;
                _computed = true;
            }
        }

        /// <summary>
        /// Obtient si la valeur est actuellement définie ou non.
        /// </summary>
        public bool Computed
        {
            get
            {
                return _computed;
            }
        }

        /// <summary>
        /// Réinitialise la valeur calculée. La prochaine demande de valeur forcera le calcul.
        /// </summary>
        public void Reset()
        {
            _computed = false;
        }
    }
}
<file_sep>/GoBot/Geometry/AnglePosition.cs
﻿using System;

namespace Geometry
{
    public enum AngleType
    {
        Radian,
        Degre
    }

    public struct AnglePosition
    {

        #region Constantes

        public const double PRECISION = 0.01;

        #endregion

        #region Attributs

        private double _angle;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Construit un angle avec la valeur passée en paramètre
        /// </summary>
        /// <param name="angle">Angle de départ</param>
        public AnglePosition(double angle, AngleType type = AngleType.Degre)
        {
            if (type == AngleType.Degre)
                _angle = angle;
            else
                _angle = (double)(180 * angle / Math.PI);

            _angle = _angle % 360;

            if (_angle > 180)
                _angle = _angle - 360;
            if (_angle < -180)
                _angle = _angle + 360;
        }

        #endregion

        #region Trigonometrie

        public double Cos
        {
            get
            {
                return Math.Cos(InRadians);
            }
        }

        public double Sin
        {
            get
            {
                return Math.Sin(InRadians);
            }
        }

        public double Tan
        {
            get
            {
                return Math.Tan(InRadians);
            }
        }

        #endregion

        #region Proprietes

        /// <summary>
        /// Retourne l'angle en radians (-PI à +PI)
        /// </summary>
        public double InRadians
        {
            get
            {
                return (double)(_angle / 180 * Math.PI);
            }
        }

        /// <summary>
        /// Retourne l'angle en degrés (-180 à +180)
        /// </summary>
        public double InDegrees
        {
            get
            {
                return _angle;
            }
        }

        /// <summary>
        /// Retourne l'angle en degrés positif (0 à 360)
        /// </summary>
        public double InPositiveDegrees
        {
            get
            {
                if (_angle < 0)
                    return 360 + _angle;
                else
                    return _angle;
            }
        }

        /// <summary>
        /// Retourne l'angle en radians positif (0 à 2PI)
        /// </summary>
        public double InPositiveRadians
        {
            get
            {
                return InPositiveDegrees / 180 * Math.PI;
            }
        }

        #endregion

        #region Centre de l'arc

        public static AnglePosition Center(AnglePosition startAngle, AnglePosition endAngle)
        {
            AnglePosition a;

            if (startAngle.InPositiveDegrees < endAngle.InPositiveDegrees)
                a = new AnglePosition((startAngle.InPositiveDegrees + endAngle.InPositiveDegrees) / 2);
            else
                a = new AnglePosition((startAngle.InPositiveDegrees + endAngle.InPositiveDegrees) / 2 + 180);

            return a;
        }

        public static AnglePosition CenterSmallArc(AnglePosition a1, AnglePosition a2)
        {
            AnglePosition a;

            if (Math.Abs(a1.InPositiveDegrees - a2.InPositiveDegrees) < 180)
                return new AnglePosition((a1.InPositiveDegrees + a2.InPositiveDegrees) / 2);
            else
                a = new AnglePosition((a1.InPositiveDegrees + a2.InPositiveDegrees) / 2 + 180);

            return a;
        }

        public static AnglePosition CenterLongArc(AnglePosition a1, AnglePosition a2)
        {
            AnglePosition a;

            if (Math.Abs(a1.InPositiveDegrees - a2.InPositiveDegrees) > 180)
                a = new AnglePosition((a1.InPositiveDegrees + a2.InPositiveDegrees) / 2);
            else
                a = new AnglePosition((a1.InPositiveDegrees + a2.InPositiveDegrees) / 2 + 180);

            return a;
        }

        #endregion

        #region Autres calculs

        /// <summary>
        /// Retourne si l'angle est compris entre les angles données, c'est à dire qu'il se situe dans l'arc de cercle partant de startAngle vers endAngle
        /// Exemples : 
        /// 150° est entre 130° et 160° mais pas entre 160° et 130°
        /// 10° est entre 350° et 50° mais pas entre 50° et 350°
        /// </summary>
        /// <param name="startAngle">Angle de départ</param>
        /// <param name="endAngle">Angle d'arrivée</param>
        /// <returns>Vrai si l'angle est compris entre les deux angles</returns>
        public bool IsOnArc(AnglePosition startAngle, AnglePosition endAngle)
        {
            bool ok;

            if (startAngle.InPositiveDegrees < endAngle.InPositiveDegrees)
                ok = this.InPositiveDegrees >= startAngle.InPositiveDegrees && this.InPositiveDegrees <= endAngle.InPositiveDegrees;
            else
                ok = this.InPositiveDegrees == startAngle.InPositiveDegrees || this.InPositiveDegrees == endAngle.InPositiveDegrees || !this.IsOnArc(endAngle, startAngle);

            return ok;
        }

        #endregion

        #region Operateurs

        public static AnglePosition operator +(AnglePosition a1, AngleDelta a2)
        {
            return new AnglePosition(a1.InPositiveDegrees + a2.InDegrees, AngleType.Degre);
        }

        public static AnglePosition operator -(AnglePosition a1, AngleDelta a2)
        {
            return new AnglePosition(a1.InPositiveDegrees - a2.InDegrees, AngleType.Degre);
        }

        public static AngleDelta operator -(AnglePosition start, AnglePosition end)
        {
            double a = start.InDegrees - end.InDegrees;

            if (a > 180)
                a = -(360 - a);
            if (a < -180)
                a = 360 + a;

            return new AngleDelta(a);
        }

        public static bool operator ==(AnglePosition a1, AnglePosition a2)
        {
            return Math.Abs((a1 - a2).InDegrees) <= PRECISION;
        }

        public static bool operator !=(AnglePosition a1, AnglePosition a2)
        {
            return !(a1 == a2);
        }

        public static implicit operator AnglePosition(double angle)
        {
            return new AnglePosition(angle);
        }

        public static implicit operator double(AnglePosition angle)
        {
            return angle.InDegrees;
        }

        #endregion

        #region Overrides

        public override bool Equals(object obj)
        {
            try
            {
                return Math.Abs(((AnglePosition)obj)._angle - _angle) < PRECISION;
            }
            catch
            {
                return false;
            }
        }

        public override int GetHashCode()
        {
            return (int)(_angle * (1 / PRECISION));
        }

        public override string ToString()
        {
            return _angle.ToString("0.00") + "°";
        }

        #endregion

    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelActionneurGeneric.cs
﻿using System;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using System.Reflection;

namespace GoBot.IHM
{
    public partial class PanelActionneurGeneric : UserControl
    {
        private object obj;

        public PanelActionneurGeneric()
        {
            InitializeComponent();
        }

        public void SetObject(Object o)
        {
            Type t = o.GetType();
            obj = o;
            int i = 25;

            lblName.Text = t.Name;
            
            foreach (MethodInfo method in t.GetMethods().Where(m => m.Name.StartsWith("Do")).OrderBy(s => s.Name))
            {
                Button b = new Button();
                b.SetBounds(5, i, 120, 22);
                b.Tag = method;
                b.Text = method.Name.Substring(2);
                b.Click += b_Click;
                Controls.Add(b);
                i += 26;
            }
        }

        void b_Click(object sender, EventArgs e)
        {
            Button b = (Button)sender;
            Color oldColor = b.BackColor;

            b.BackColor = Color.LightGreen;
            Threading.ThreadManager.CreateThread(link =>
            {
                link.Name = b.Text;
                MethodInfo method = ((MethodInfo)((Button)sender).Tag);
                object[] parameters = method.GetParameters().Count() > 0 ? new Object[] { false } : null;
                method.Invoke(obj, parameters);
                b.InvokeAuto(() => b.BackColor = oldColor);
            }).StartThread();
        }

        private void PanelActionneurGeneric_Load(object sender, EventArgs e)
        {
            BorderStyle = BorderStyle.None;
        }
    }
}
<file_sep>/GoBot/GoBot/PathFinding/Arc.cs
using System;

namespace AStarFolder
{
    public class Arc
    {
        private Node _startNode;
        private Node _endNode;

        private bool _passable;
        private double _length;
        
        public Arc(Node Start, Node End)
        {
            StartNode = Start;
            EndNode = End;
            _length = _startNode.Position.Distance(_endNode.Position);
            _passable = true;
        }

        public Node StartNode
        {
            set
            {
                if(_startNode != null) _startNode.OutgoingArcs.Remove(this);
                _startNode = value;
                _startNode.OutgoingArcs.Add(this);
            }
            get { return _startNode; }
        }
        
        public Node EndNode
        {
            set
            {
                if (_endNode != null) _endNode.IncomingArcs.Remove(this);
                _endNode = value;
                _endNode.IncomingArcs.Add(this);
            }
            get { return _endNode; }
        }
        
        public bool Passable
        {
            set { _passable = value; }
            get { return _passable; }
        }
        
        public double Length
        {
            get
            {
                return _length;
            }
        }
        
        virtual public double Cost
        {
            get { return Math.Sqrt(_length); }
        }
        
        public override string ToString()
        {
            return _startNode.ToString() + "-->" + _endNode.ToString();
        }
        
        public override bool Equals(object o)
        {
            Arc arc = (Arc)o;
            
            return arc != null && _startNode.Equals(arc._startNode) && _endNode.Equals(arc._endNode);
        }
        
        public override int GetHashCode()
        {
            return _startNode.GetHashCode() + _endNode.GetHashCode();
        }
    }
}

<file_sep>/GoBot/GoBot/Robots/Robot.cs
﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Threading;

using AStarFolder;

using Geometry;
using Geometry.Shapes;

using GoBot.Actionneurs;
using GoBot.Actions;
using GoBot.BoardContext;
using GoBot.PathFinding;
using GoBot.Threading;

namespace GoBot
{
    public abstract class Robot
    {
        // Communication
        public Board AsservBoard { get; protected set; }
        public Historique Historique { get; protected set; }
        public double BatterieVoltage { get; set; }
        public double BatterieIntensity { get; set; }

        // Constitution
        public IDRobot IDRobot { get; private set; }
        public string Name { get; private set; }

        public Robot(IDRobot id, string name)
        {
            IDRobot = id;
            Name = name;

            SpeedConfig = new SpeedConfig(500, 1000, 1000, 500, 1000, 1000);
            AsserStats = new AsserStats();
            IsSpeedAdvAdaptable = true;

            foreach (MotorID moteur in Enum.GetValues(typeof(MotorID)))
                MotorState.Add(moteur, false);

            foreach (SensorOnOffID o in Enum.GetValues(typeof(SensorOnOffID)))
                SensorsOnOffValue.Add(o, false);

            foreach (ActuatorOnOffID o in Enum.GetValues(typeof(ActuatorOnOffID)))
                ActuatorOnOffState.Add(o, false);

            foreach (SensorColorID o in Enum.GetValues(typeof(SensorColorID)))
                SensorsColorValue.Add(o, Color.Black);

            AnalogicPinsValue = new Dictionary<Board, List<double>>();
            AnalogicPinsValue.Add(Board.RecIO, new List<double>());
            AnalogicPinsValue.Add(Board.RecMove, new List<double>());

            for (int i = 0; i < 9; i++)
            {
                AnalogicPinsValue[Board.RecIO].Add(0);
                AnalogicPinsValue[Board.RecMove].Add(0);
            }

            NumericPinsValue = new Dictionary<Board, List<Byte>>();
            NumericPinsValue.Add(Board.RecIO, new List<byte>());
            NumericPinsValue.Add(Board.RecMove, new List<byte>());

            for (int i = 0; i < 3 * 2; i++)
            {
                NumericPinsValue[Board.RecIO].Add(0);
                NumericPinsValue[Board.RecMove].Add(0);
            }

            BatterieVoltage = 0;
            Graph = null;
            TrajectoryFailed = false;
            TrajectoryCutOff = false;
            TrajectoryRunning = null;
        }

        public virtual void ShowMessage(String message1, String message2)
        {

        }

        public override string ToString()
        {
            return Name;
        }

        #region Pinout

        public Dictionary<Board, List<double>> AnalogicPinsValue { get; protected set; }
        public Dictionary<Board, List<byte>> NumericPinsValue { get; protected set; }

        public abstract void ReadAnalogicPins(Board board, bool waitEnd = true);
        public abstract void ReadNumericPins(Board board, bool waitEnd = true);

        #endregion

        #region Management

        public abstract void Init();
        public abstract void DeInit();
        public abstract void EnablePower(bool on);
        public abstract List<double>[] DiagnosticCpuPwm(int valuesCount);
        public abstract List<int>[] DiagnosticPID(int steps, SensAR sens, int valuesCount);
        public abstract List<int>[] DiagnosticLine(int distance, SensAR sens);

        #endregion

        #region Moves

        public SpeedConfig SpeedConfig { get; protected set; }
        public List<Position> PositionsHistorical { get; protected set; }
        public AsserStats AsserStats { get; protected set; }
        public bool AsserEnable { get; protected set; }
        public abstract Position Position { get; protected set; }
        public RealPoint PositionTarget { get; protected set; }
        public bool IsInLineMove { get; protected set; }
        public bool IsSpeedAdvAdaptable { get; protected set; }


        public delegate void PositionChangedDelegate(Position position);
        public event PositionChangedDelegate PositionChanged;

        protected void OnPositionChanged(Position position)
        {
            PositionChanged?.Invoke(position);
        }


        public virtual void MoveForward(int distance, bool waitEnd = true)
        {
            if (distance > 0)
                AsserStats.ForwardMoves.Add(distance);
            else
                AsserStats.BackwardMoves.Add(-distance);
        }

        public virtual void MoveBackward(int distance, bool waitEnd = true)
        {
            if (distance < 0)
                AsserStats.ForwardMoves.Add(distance);
            else
                AsserStats.BackwardMoves.Add(-distance);
        }

        public void Move(int distance, bool waitEnd = true)
        {
            if (distance > 0)
                MoveForward(distance, waitEnd);
            else
                MoveBackward(-distance, waitEnd);
        }

        public virtual void PivotLeft(AngleDelta angle, bool waitEnd = true)
        {
            AsserStats.LeftRotations.Add(angle);
        }

        public virtual void Pivot(AngleDelta angle, bool waitEnd = true)
        {
            if (angle > 0)
                PivotLeft(angle, waitEnd);
            else
                PivotRight(-angle, waitEnd);
        }

        public virtual void PivotRight(AngleDelta angle, bool waitEnd = true)
        {
            AsserStats.RightsRotations.Add(angle);
        }

        public void SetSpeedSlow()
        {
            SpeedConfig.SetParams(Config.CurrentConfig.ConfigLent);

            IsSpeedAdvAdaptable = false;
        }

        public void SetSpeedVerySlow()
        {
            SpeedConfig.SetParams(100, 500, 500, 100, 500, 500);

            IsSpeedAdvAdaptable = false;
        }

        public void SetSpeedVeryFast()
        {
            SpeedConfig.SetParams(800, 2500, 2500, 800, 2500, 2500);

            IsSpeedAdvAdaptable = false;
        }

        public void SetSpeedFast()
        {
            SpeedConfig.SetParams(Config.CurrentConfig.ConfigRapide);

            IsSpeedAdvAdaptable = true;
        }

        public void GoToAngle(AnglePosition angle, double marge = 0)
        {
            AngleDelta diff = angle - Position.Angle;
            if (Math.Abs(diff.InDegrees) > marge)
            {
                if (diff.InDegrees > 0)
                    PivotRight(diff.InDegrees);
                else
                    PivotLeft(-diff.InDegrees);
            }
        }

        public bool GoToPosition(Position dest)
        {
            Historique.Log("Lancement pathfinding pour aller en " + dest.ToString(), TypeLog.PathFinding);

            Trajectory traj = PathFinder.ChercheTrajectoire(Graph, GameBoard.ObstaclesAll, GameBoard.ObstaclesOpponents, Position, dest, Radius, Robots.MainRobot.Width / 2);

            if (traj == null)
                return false;

            RunTrajectory(traj);

            return !TrajectoryCutOff && !TrajectoryFailed;
        }

        public abstract void PolarTrajectory(SensAR sens, List<RealPoint> points, bool waitEnd = true);
        public abstract void Stop(StopMode mode = StopMode.Smooth);
        public abstract void Turn(SensAR sensAr, SensGD sensGd, int radius, AngleDelta angle, bool waitEnd = true);
        public abstract void SetAsservOffset(Position newPosition);
        public virtual void Recalibration(SensAR sens, bool waitEnd = true, bool sendOffset = false)
        {
            int angle = 0;

            double toleranceXY = 30;
            double toleranceTheta = 3;

            double dist = (sens == SensAR.Avant ? Robots.MainRobot.LenghtFront : Robots.MainRobot.LenghtBack);

            if (waitEnd && sendOffset)
            {
                if (Position.Angle.IsOnArc(0 - toleranceTheta, 0 + toleranceTheta))
                    angle = 0;
                else if (Position.Angle.IsOnArc(90 - toleranceTheta, 90 + toleranceTheta))
                    angle = 90;
                else if (Position.Angle.IsOnArc(180 - toleranceTheta, 180 + toleranceTheta))
                    angle = 180;
                else if (Position.Angle.IsOnArc(270 - toleranceTheta, 270 + toleranceTheta))
                    angle = 270;

                if (Position.Coordinates.X < dist + toleranceXY && Position.Coordinates.X > dist - toleranceTheta)
                    SetAsservOffset(new Position(angle, new RealPoint(dist, Position.Coordinates.Y)));
                else if (Position.Coordinates.X > 3000 - dist)
                    SetAsservOffset(new Position(angle, new RealPoint(3000 - dist, Position.Coordinates.Y)));
                else if (Position.Coordinates.Y < dist)
                    SetAsservOffset(new Position(angle, new RealPoint(Position.Coordinates.X, dist)));
                else if (Position.Coordinates.Y > 2000 - dist)
                    SetAsservOffset(new Position(angle, new RealPoint(Position.Coordinates.X, 2000 - dist)));
            }
        }
        public abstract void SendPID(int p, int i, int d);
        public abstract void SendPIDCap(int p, int i, int d);
        public abstract void SendPIDSpeed(int p, int i, int d);

        #endregion

        #region PathFinding

        public Graph Graph { get; set; }
        public bool TrajectoryFailed { get; protected set; }
        public bool TrajectoryCutOff { get; protected set; }
        public Trajectory TrajectoryRunning { get; protected set; }

        public bool IsFarEnough(IShape target, IShape toAvoid, int margin = 0)
        {
            Type typeForme1 = target.GetType();
            Type typeForme2 = toAvoid.GetType();
            bool can;

            if (typeForme1.IsAssignableFrom(typeof(Segment)))
                if (typeForme2.IsAssignableFrom(typeof(Segment)))
                    can = ((Segment)target).Distance((Segment)toAvoid) > Radius + margin;
                else
                    can = ((Segment)target).Distance(toAvoid) > Radius + margin;
            else if (typeForme1.IsAssignableFrom(typeof(Circle)) && typeForme1.IsAssignableFrom(typeof(RealPoint)))
            {
                // très opportuniste
                RealPoint c = ((Circle)target).Center;
                RealPoint p = (RealPoint)toAvoid;
                double dx = c.X - p.X;
                double dy = c.Y - p.Y;

                can = dx * dx + dy * dy > margin * margin;
            }
            else
                can = target.Distance(toAvoid) > Radius + margin;

            return can;
        }

        public bool OpponentsTrajectoryCollision(IEnumerable<IShape> opponents)
        {
            bool ok = true;

            if (TrajectoryCutOff)
                ok = false;

            if (ok)
            {
                try
                {
                    // Teste si le chemin en cours de parcours est toujours franchissable
                    if (TrajectoryRunning != null && TrajectoryRunning.Lines.Count > 0)
                    {
                        List<Segment> segmentsTrajectoire = new List<Segment>();
                        // Calcule le segment entre nous et notre destination (permet de ne pas considérer un obstacle sur un tronçon déjà franchi)
                        Segment toNextPoint = new Segment(Position.Coordinates, new RealPoint(TrajectoryRunning.Lines[0].EndPoint));
                        segmentsTrajectoire.Add(toNextPoint);

                        for (int iSegment = 1; iSegment < TrajectoryRunning.Lines.Count; iSegment++)
                        {
                            segmentsTrajectoire.Add(TrajectoryRunning.Lines[iSegment]);
                        }

                        foreach (IShape opponent in opponents)
                        {
                            foreach (Segment segment in segmentsTrajectoire)
                            {
                                // Marge de 30mm pour être plus permissif sur le passage et ne pas s'arreter dès que l'adversaire approche
                                if (!IsFarEnough(toNextPoint, opponent, -30))
                                {
                                    // Demande de génération d'une nouvelle trajectoire
                                    Historique.Log("Trajectoire coupée, annulation", TypeLog.PathFinding);
                                    TrajectoryCutOff = true;
                                    TrajectoryRunning = null;

                                    if (IsInLineMove)
                                        Stop();
                                    ok = false;
                                    break;
                                }
                            }

                            if (!ok)
                                break;
                        }
                    }
                }
                catch (Exception)
                {
                    ok = false;
                }
            }

            return ok;
        }

        public void UpdateGraph(IEnumerable<IShape> obstacles)
        {
            lock (Graph)
            {
                foreach (Arc arc in Graph.Arcs)
                    arc.Passable = true;

                foreach (Node node in Graph.Nodes)
                    node.Passable = true;

                foreach (IShape obstacle in obstacles)
                {
                    // On ne désactive pas les arcs unitairement parce qu'on considère qu'ils sont trop courts pour qu'un arc non franchissable raccord 2 points franchissables
                    // Donc on ne teste que les noeuds non franchissables
                    for (int i = 0; i < Graph.Nodes.Count; i++)
                    {
                        Node n = Graph.Nodes[i];

                        if (n.Passable)
                        {
                            if (!IsFarEnough(obstacle, n.Position))
                            {
                                n.Passable = false;
                                // Désactivation des arcs connectés aux noeuds désactivés = 10 fois plus rapide que tester les arcs
                                foreach (Arc a in n.IncomingArcs) a.Passable = false;
                                foreach (Arc a in n.OutgoingArcs) a.Passable = false;
                            }
                        }
                    }
                }
            }
        }

        public bool RunTrajectory(Trajectory traj)
        {
            TrajectoryRunning = traj;
            TimeSpan estimatedDuration = traj.GetDuration(this);
            Stopwatch sw = Stopwatch.StartNew();

            TrajectoryCutOff = false;
            TrajectoryFailed = false;

            foreach (IAction action in traj.ConvertToActions(this))
            {
                if (!Execution.Shutdown)
                {
                    action.Executer();

                    if (TrajectoryCutOff || TrajectoryFailed)
                        break;

                    if (action is ActionAvance || action is ActionRecule)
                    {
                        Historique.Log("Noeud atteint " + TrajectoryRunning.Points[0].X.ToString("0") + ":" + TrajectoryRunning.Points[0].Y.ToString("0"), TypeLog.PathFinding);
                        TrajectoryRunning.RemovePoint(0);
                    }
                }
            }

            if (!Execution.Shutdown)
            {
                TrajectoryRunning = null;

                if (!TrajectoryCutOff && !TrajectoryFailed)
                {
                    Historique.Log("Trajectoire parcourue en " + (sw.ElapsedMilliseconds / 1000.0).ToString("0.0") + "s (durée théorique : " + (estimatedDuration.TotalSeconds).ToString("0.0") + "s)", TypeLog.PathFinding);

                    return true;
                }

                if (TrajectoryFailed)
                {
                    Historique.Log("Echec du parcours de la trajectoire (dérapage, blocage...)", TypeLog.PathFinding);

                    return false;
                }
            }

            return false;
        }

        #endregion

        #region Sensors

        public bool StartTriggerEnable { get; protected set; } = false;
        public Dictionary<SensorOnOffID, bool> SensorsOnOffValue { get; } = new Dictionary<SensorOnOffID, bool>();
        public Dictionary<SensorColorID, Color> SensorsColorValue { get; } = new Dictionary<SensorColorID, Color>();


        public delegate void SensorOnOffChangedDelegate(SensorOnOffID sensor, bool state);
        public event SensorOnOffChangedDelegate SensorOnOffChanged;

        public delegate void SensorColorChangedDelegate(SensorColorID sensor, Color color);
        public event SensorColorChangedDelegate SensorColorChanged;

        protected void OnSensorOnOffChanged(SensorOnOffID sensor, bool state)
        {
            SensorsOnOffValue[sensor] = state;
            SensorOnOffChanged?.Invoke(sensor, state);
        }

        protected void OnSensorColorChanged(SensorColorID sensor, Color color)
        {
            SensorsColorValue[sensor] = color;
            SensorColorChanged?.Invoke(sensor, color);
        }


        public abstract bool ReadSensorOnOff(SensorOnOffID sensor, bool waitEnd = true);
        public abstract Color ReadSensorColor(SensorColorID sensor, bool waitEnd = true);
        public abstract String ReadLidarMeasure(LidarID lidar, int timeout, out Position refPosition);
        public abstract bool ReadStartTrigger();

        public void EnableStartTrigger()
        {
            StartTriggerEnable = true;
        }


        #endregion

        #region Actuators

        public Dictionary<ActuatorOnOffID, bool> ActuatorOnOffState { get; } = new Dictionary<ActuatorOnOffID, bool>();
        public Dictionary<MotorID, bool> MotorState { get; } = new Dictionary<MotorID, bool>();


        public abstract void SetActuatorOnOffValue(ActuatorOnOffID actuator, bool on);

        public virtual void SetMotorAtPosition(MotorID motor, int position, bool waitEnd = false)
        {
            //Historique.AjouterAction(new ActionMoteur(this, position, motor));
        }

        public virtual void SetMotorAtOrigin(MotorID motor, bool waitEnd = false)
        {
            //TODO historique ?
        }

        public virtual void MotorWaitEnd(MotorID moteur)
        {

        }

        public virtual void SetMotorSpeed(MotorID moteur, SensGD sens, int vitesse)
        {
            if (MotorState.ContainsKey(moteur))
                MotorState[moteur] = vitesse == 0 ? false : true;
            Historique.AjouterAction(new ActionMoteur(this, vitesse, moteur));
        }

        public virtual void SetMotorReset(MotorID moteur)
        {
        }

        public virtual void SetMotorStop(MotorID moteur, StopMode mode)
        {
        }

        public virtual void SetMotorAcceleration(MotorID moteur, int acceleration)
        {
            Historique.AjouterAction(new ActionMoteur(this, acceleration, moteur));
        }

        public void ActuatorsStore()
        {
            Robots.MainRobot.SetAsservOffset(new Position(0, new RealPoint(1500, 1000)));

            // TODOEACHYEAR Lister les actionneurs à ranger pour préparer un match

            ThreadManager.CreateThread(link => { Actionneur.ElevatorLeft.DoStoreActuators(); }).StartThread();
            ThreadManager.CreateThread(link => { Actionneur.ElevatorRight.DoStoreActuators(); }).StartThread();

            Actionneur.FingerLeft.DoPositionHide();
            Actionneur.FingerRight.DoPositionHide();

            Actionneur.ElevatorLeft.DoGrabHide();
            Actionneur.ElevatorRight.DoGrabHide();

            Actionneur.Flags.DoCloseLeft();
            Actionneur.Flags.DoCloseRight();

            Actionneur.Lifter.DoStoreAll();
            Actionneur.Lifter.DoLifterPositionStore();
            Thread.Sleep(150);
            Actionneur.Lifter.DoTilterPositionStore();

            Thread.Sleep(1000);

            Devices.AllDevices.CanServos.DisableAll();
        }

        public void ActuatorsDeploy()
        {
            // TODOEACHYEAR Lister les actionneurs à déployer
        }

        #endregion

        #region Shape

        public double LenghtFront { get; private set; }
        public double LenghtBack { get; private set; }
        public double LenghtTotal => LenghtBack + LenghtFront;
        public double LenghtSquare => Math.Max(LenghtBack, LenghtFront) * 2;
        public double Width { get; private set; }
        public double Radius { get; private set; }
        public double Diameter => Radius * 2;
        public double WheelSpacing { get; private set; }

        public void SetDimensions(double width, double lenghtFront, double lenghtBack, double wheelSpacing, double diameter)
        {
            LenghtFront = lenghtFront;
            LenghtBack = lenghtBack;
            Width = width;
            WheelSpacing = wheelSpacing;
            Radius = diameter / 2;
        }

        public IShape GetBounds()
        {
            IShape contact = new PolygonRectangle(new RealPoint(Position.Coordinates.X - LenghtBack / 2, Position.Coordinates.Y - Width / 2), LenghtBack + LenghtFront, Width);
            contact = contact.Rotation(new AngleDelta(Position.Angle));

            return contact;
        }

        #endregion

    }
}
<file_sep>/TestShapes/MainForm.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Threading;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace TestShapes
{
    public partial class MainForm : Form
    {
        private enum ShapeMode
        {
            None,
            Rectangle,
            Circle,
            CircleFromCenter,
            Segment,
            Line,
            Zoom
        }

        private List<IShape> _shapes;
        private WorldScale _worldScale;

        private bool _barycenterVisible = true;
        private bool _crossingPointsVisible = true;
        private bool _crossingColorVisible = true;
        private bool _containedColorVisible = true;
        private bool _gridVisible = true;
        private bool _axesVisible = false;

        private ShapeMode _shapeMode;
        private RealPoint _startPoint;
        private IShape _currentShape;

        private PolygonRectangle _rectZoomOrg, _rectZoomFinal;

        ThreadLink _moveLoop;
        double _moveVertical, _moveHorizontal;
        double _moveZoom;

        public MainForm()
        {
            InitializeComponent();

            ThreadManager.Init();

            _shapeMode = ShapeMode.Rectangle;

            _shapes = new List<IShape>();

            _shapes.Add(new Line(new RealPoint(10, 10), new RealPoint(10, 100)));
            _shapes.Add(new Line(new RealPoint(20, 20), new RealPoint(100, 20)));

            //_shapes.Add(new Segment(new RealPoint(66.5, 9.9), new RealPoint(13.5, 84.2)));
            //_shapes.Add(new Segment(new RealPoint(46.6, 44), new RealPoint(30, 44)));

            //_shapes.Add(new Circle(new RealPoint(200, 200), 30));
            //_shapes.Add(new Circle(new RealPoint(250, 180), 30));
            //_shapes.Add(new Segment(new RealPoint(10, 10), new RealPoint(300, 300)));
            //_shapes.Add(new PolygonRectangle(new RealPoint(250, 250), 300, 50));
            //_shapes.Add(new Circle(new RealPoint(260, 500), 30));
            //_shapes.Add(new PolygonRectangle(new RealPoint(230, 400), 300, 40));
            //_shapes.Add(new Circle(new RealPoint(320, 420), 15));

            //_shapes.Add(new Circle(new RealPoint(100, 200), 15));
            //_shapes.Add(new Circle(new RealPoint(100, 200), 30));
            //_shapes.Add(new Circle(new RealPoint(100, 200), 45));
            //_shapes.Add(new Circle(new RealPoint(100, 200), 60));

            //_shapes.Add(new Circle(new RealPoint(80, 350), 15));
            //_shapes.Add(new Circle(new RealPoint(80 + 14, 350), 30));
            //_shapes.Add(new Circle(new RealPoint(80 + 14 + 14, 350), 45));
            //_shapes.Add(new Circle(new RealPoint(80 + 14 + 14 + 14, 350), 60));

            //_shapes.Add(new Circle(new RealPoint(80, 500), 15));
            //_shapes.Add(new Circle(new RealPoint(80 + 14, 500), 30));
            //_shapes.Add(new Circle(new RealPoint(80 + 14 + 14, 500), 45));
            //_shapes.Add(new Circle(new RealPoint(80 + 14 + 14 + 14, 500), 60));
            //_shapes.Add(new Segment(new RealPoint(80, 500), new RealPoint(180, 500)));

            //_shapes.Add(new PolygonRectangle(new RealPoint(230, 400), 300, 40).Rotation(45));
        }

        #region Peinture

        private void picWorld_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.TranslateTransform(0, +picWorld.Height);
            e.Graphics.ScaleTransform(1, -1);

            DrawGrid(e.Graphics);
            DrawAxes(e.Graphics);

            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            List<IShape> allShapes = new List<IShape>(_shapes);
            if (_currentShape != null) allShapes.Add(_currentShape);

            foreach (IShape shape in allShapes)
            {
                bool isContained = false;
                bool isCrossed = false;

                foreach (IShape otherShape in allShapes)
                {
                    if (otherShape != shape)
                    {
                        if (shape.Cross(otherShape))
                        {
                            isCrossed = true;
                            shape.GetCrossingPoints(otherShape).ForEach(p => DrawCrossingPoint(p, e.Graphics));
                        }
                        isContained = isContained || otherShape.Contains(shape);
                    }
                }

                DrawShape(shape, isCrossed, isContained, e.Graphics);
                DrawBarycenter(shape.Barycenter, e.Graphics);
            }

            if (_shapeMode == ShapeMode.Zoom && _rectZoomOrg != null)
            {
                if (_rectZoomOrg != null)
                {
                    Pen pen = new Pen(Color.Black);
                    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                    _rectZoomOrg.Paint(e.Graphics, Color.LightGray, 1, Color.Transparent, _worldScale);
                    pen.Dispose();
                }
                if (_rectZoomFinal != null)
                {
                    Pen pen = new Pen(Color.Black);
                    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                    _rectZoomFinal.Paint(e.Graphics, Color.DarkGray, 1, Color.Transparent, _worldScale);
                    pen.Dispose();
                }
            }
        }

        private void DrawAxes(Graphics g)
        {
            if (_axesVisible)
            {
                new Segment(new RealPoint(0, 0), new RealPoint(0, 150)).Paint(g, Color.Black, 3, Color.Transparent, _worldScale);
                new Segment(new RealPoint(0, 150), new RealPoint(5, 145)).Paint(g, Color.Black, 2, Color.Transparent, _worldScale);
                new Segment(new RealPoint(0, 150), new RealPoint(-5, 145)).Paint(g, Color.Black, 2, Color.Transparent, _worldScale);

                new Segment(new RealPoint(0, 0), new RealPoint(150, 0)).Paint(g, Color.Black, 3, Color.Transparent, _worldScale);
                new Segment(new RealPoint(150, 0), new RealPoint(145, 5)).Paint(g, Color.Black, 2, Color.Transparent, _worldScale);
                new Segment(new RealPoint(150, 0), new RealPoint(145, -5)).Paint(g, Color.Black, 2, Color.Transparent, _worldScale);

                RealPoint arc1 = _worldScale.RealToScreenPosition(new RealPoint(-80, -80));
                RealPoint arc2 = _worldScale.RealToScreenPosition(new RealPoint(80, 80));

                Rectangle r = new Rectangle((int)arc1.X, (int)arc1.Y, (int)(arc2.X - arc1.X), (int)(arc2.Y - arc1.Y));

                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                g.DrawArc(Pens.Black, r, 0, 90);
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;

                RealPoint arrow1 = new RealPoint(1, 80);
                RealPoint arrow2 = new RealPoint(6, 85);
                RealPoint arrow3 = new RealPoint(6, 75);

                new Segment(arrow1, arrow2).Paint(g, Color.Black, 1, Color.Black, _worldScale);
                new Segment(arrow1, arrow3).Paint(g, Color.Black, 1, Color.Black, _worldScale);

                new RealPoint(0, 0).Paint(g, Color.Black, 3, Color.Black, _worldScale);
            }
        }

        private void DrawBarycenter(RealPoint pt, Graphics g)
        {
            if (_barycenterVisible)
            {
                pt?.Paint(g, Color.Black, 3, Color.Blue, _worldScale);
            }
        }

        private void DrawCrossingPoint(RealPoint pt, Graphics g)
        {
            if (_crossingPointsVisible)
            {
                pt.Paint(g, Color.Black, 5, Color.Red, _worldScale);
            }
        }

        private void DrawShape(IShape shape, bool isCrossed, bool isContained, Graphics g)
        {
            Color outlineColor = Color.Black;
            Color fillColor = Color.Transparent;

            if (_crossingColorVisible)
            {
                if (isCrossed)
                    outlineColor = Color.Red;
                else
                    outlineColor = Color.Green;
            }

            if (_containedColorVisible)
            {
                if (isContained)
                    fillColor = Color.FromArgb(100, Color.Green);
            }

            shape.Paint(g, outlineColor, 1, fillColor, _worldScale);
        }

        private void DrawGrid(Graphics g)
        {
            if (_gridVisible)
            {
                for (int i = -_worldScale.OffsetX / 10 * 10; i < picWorld.Width + _worldScale.OffsetX; i += 10)
                    new Segment(new RealPoint(i, -10000), new RealPoint(i, 10000)).Paint(g, Color.WhiteSmoke, 1, Color.Transparent, _worldScale);

                for (int i = -_worldScale.OffsetY / 10 * 10; i < picWorld.Height + _worldScale.OffsetY; i += 10)
                    new Segment(new RealPoint(-10000, i), new RealPoint(10000, i)).Paint(g, Color.WhiteSmoke, 1, Color.Transparent, _worldScale);

                new Segment(new RealPoint(0, -10000), new RealPoint(0, 10000)).Paint(g, Color.Black, 1, Color.Transparent, _worldScale);
                new Segment(new RealPoint(-10000, 0), new RealPoint(10000, 0)).Paint(g, Color.Black, 1, Color.Transparent, _worldScale);

                //for (int i = 0; i < picWorld.Width; i += 10)
                //    g.DrawLine(Pens.WhiteSmoke, i, 0, i, picWorld.Height);

                //for (int i = 0; i < picWorld.Height; i += 10)
                //    g.DrawLine(Pens.WhiteSmoke, 0, i, picWorld.Width, i);

                //for (int i = 0; i < picWorld.Width; i += 100)
                //    g.DrawLine(Pens.LightGray, i, 0, i, picWorld.Height);

                //for (int i = 0; i < picWorld.Height; i += 100)
                //    g.DrawLine(Pens.LightGray, 0, i, picWorld.Width, i);
            }
        }

        #endregion

        #region Actions IHM

        private void btnBarycenter_ValueChanged(object sender, bool value)
        {
            _barycenterVisible = value;
            picWorld.Invalidate();
        }

        private void btnCrossing_ValueChanged(object sender, bool value)
        {
            _crossingColorVisible = value;
            picWorld.Invalidate();
        }

        private void btnCrossingPoints_ValueChanged(object sender, bool value)
        {
            _crossingPointsVisible = value;
            picWorld.Invalidate();
        }

        private void btnContained_ValueChanged(object sender, bool value)
        {
            _containedColorVisible = value;
            picWorld.Invalidate();
        }

        private void btnGrid_ValueChanged(object sender, bool value)
        {
            _gridVisible = value;
            picWorld.Invalidate();
        }

        #endregion

        private RealPoint PicCoordinates()
        {
            RealPoint pt = picWorld.PointToClient(Cursor.Position);

            RealPoint output = new RealPoint();
            output.X = (-_worldScale.OffsetX + pt.X) * _worldScale.Factor;
            output.Y = ( + (picWorld.Height - pt.Y) - _worldScale.OffsetY) * _worldScale.Factor;

            Console.WriteLine(_worldScale.OffsetY + " / " + pt.Y);
            return output;
        }

        private void picWorld_MouseDown(object sender, MouseEventArgs e)
        {
            _startPoint = PicCoordinates();

            if (_shapeMode == ShapeMode.Zoom)
            {
                _rectZoomOrg = new PolygonRectangle(_startPoint, 1, 1);
            }
            else
            {
                _currentShape = BuildCurrentShape(_shapeMode, _startPoint, _startPoint);
            }

            picWorld.Invalidate();
        }

        private void picWorld_MouseMove(object sender, MouseEventArgs e)
        {
            RealPoint pos = PicCoordinates();
            lblPosition.Text = "X = " + pos.X.ToString().PadLeft(4) + ": Y = " + pos.Y.ToString().PadLeft(4);

            if (_startPoint != null)
            {
                if (_shapeMode == ShapeMode.Zoom)
                {
                    double dx = pos.X - _startPoint.X;
                    double dy = pos.Y - _startPoint.Y;
                    double finalDx = dx;
                    double finalDy = dy;

                    _rectZoomOrg = new PolygonRectangle(_startPoint, dx, dy);

                    if (Math.Abs(dx / dy) > Math.Abs(picWorld.Width / picWorld.Height))
                        finalDx = Math.Sign(dx) * (picWorld.Width * (Math.Abs(dy) / picWorld.Height));
                    else
                        finalDy = Math.Sign(dy) * (picWorld.Height * (Math.Abs(dx) / picWorld.Width));

                    _rectZoomFinal = new PolygonRectangle(RealPoint.Shift(_startPoint, -(finalDx - dx) / 2, -(finalDy - dy) / 2), finalDx, finalDy);
                }
                else if (_shapeMode != ShapeMode.None)
                {
                    _currentShape = BuildCurrentShape(_shapeMode, _startPoint, pos);
                    lblItem.Text = _currentShape.GetType().Name + " : " + _currentShape.ToString();
                }

                picWorld.Invalidate();
            }
        }

        private void picWorld_MouseUp(object sender, MouseEventArgs e)
        {
            if (_startPoint != null)
            {
                if (_shapeMode == ShapeMode.Zoom)
                {
                    double dx = _rectZoomFinal.Points.Max(pt => pt.X) - _rectZoomFinal.Points.Min(pt => pt.X);
                    //dx = _worldScale.RealToScreenDistance(dx);
                    WorldScale finalScale = CreateWorldScale(dx / picWorld.Width, _rectZoomFinal.Barycenter);

                    _moveZoom = (_worldScale.Factor - finalScale.Factor) / 20;
                    _moveHorizontal = -finalScale.ScreenToRealDistance((finalScale.OffsetX - _worldScale.OffsetX) / 20);
                    _moveVertical = -finalScale.ScreenToRealDistance((finalScale.OffsetY - _worldScale.OffsetY) / 20);

                    _step = 0;
                    ThreadManager.CreateThread(o => ZoomStepParam()).StartLoop(20, 40);
                    //_worldScale = finalScale;

                    _startPoint = null;
                    _rectZoomFinal = null;
                    _rectZoomOrg = null;
                    _shapeMode = ShapeMode.None;
                    btnZoom.Checked = false;
                }
                else if (_shapeMode != ShapeMode.None)
                {
                    _shapes.Add(BuildCurrentShape(_shapeMode, _startPoint, PicCoordinates()));
                    lblItem.Text = "";

                    _currentShape = null;
                    _startPoint = null;
                }

                picWorld.Invalidate();
            }
        }

        private static IShape BuildCurrentShape(ShapeMode mode, RealPoint startPoint, RealPoint endPoint)
        {
            IShape output = null;

            switch (mode)
            {
                case ShapeMode.Rectangle:
                    output = new PolygonRectangle(startPoint, endPoint.X - startPoint.X, endPoint.Y - startPoint.Y);
                    break;
                case ShapeMode.Circle:
                    output = new Circle(new Segment(startPoint, endPoint).Barycenter, startPoint.Distance(endPoint) / 2);
                    break;
                case ShapeMode.CircleFromCenter:
                    output = new Circle(startPoint, startPoint.Distance(endPoint));
                    break;
                case ShapeMode.Segment:
                    output = new Segment(startPoint, endPoint);
                    break;
                case ShapeMode.Line:
                    output = new Line(startPoint, endPoint);
                    break;
            }

            return output;
        }

        private void btnRectangle_CheckedChanged(object sender, EventArgs e)
        {
            if (btnRectangle.Checked) _shapeMode = ShapeMode.Rectangle;
        }

        private void btnCircle_CheckedChanged(object sender, EventArgs e)
        {
            if (btnCircle.Checked) _shapeMode = ShapeMode.Circle;
        }

        private void btnCircleFromCenter_CheckedChanged(object sender, EventArgs e)
        {
            if (btnCircleFromCenter.Checked) _shapeMode = ShapeMode.CircleFromCenter;
        }

        private void btnSegment_CheckedChanged(object sender, EventArgs e)
        {
            if (btnSegment.Checked) _shapeMode = ShapeMode.Segment;
        }

        private void btnLine_CheckedChanged(object sender, EventArgs e)
        {
            if (btnLine.Checked) _shapeMode = ShapeMode.Line;
        }

        private void btnErase_Click(object sender, EventArgs e)
        {
            _shapes.Clear();
            picWorld.Invalidate();
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            if (_shapes.Count > 0)
            {
                _shapes.RemoveAt(_shapes.Count - 1);
                picWorld.Invalidate();
            }
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            _worldScale = new WorldScale(1, picWorld.Width / 2, picWorld.Height / 2);
        }

        private void picWorld_SizeChanged(object sender, EventArgs e)
        {
            _worldScale = new WorldScale(1, picWorld.Width / 2, picWorld.Height / 2);

            //SetScreenSize(picWorld.Size);
            picWorld.Invalidate();
        }

        private void btnAxes_ValueChanged(object sender, bool value)
        {
            _axesVisible = value;
            picWorld.Invalidate();
        }

        private void btnZoomPlus_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(o => ZoomPlus()).StartLoop(20, 20);
        }

        private void btnZoomMinus_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(o => ZoomMinus()).StartLoop(20, 20);
        }

        private void btnZoom_CheckedChanged(object sender, EventArgs e)
        {
            if (btnZoom.Checked) _shapeMode = ShapeMode.Zoom;
        }

        private WorldScale CreateWorldScale(double mmPerPixel, RealPoint center)
        {
            Console.WriteLine(center.ToString());
            int x = (int)(picWorld.Width / 2 - center.X / mmPerPixel);
            int y = (int)(picWorld.Height / 2 - center.Y / mmPerPixel);

            return new WorldScale(mmPerPixel, x, y);
        }

        private RealPoint GetCenter()
        {
            return _worldScale.ScreenToRealPosition(new RealPoint(picWorld.Width / 2, picWorld.Height / 2));
        }

        private void btnUp_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(o => ZoomUp()).StartLoop(20, 20);
        }

        private void btnDown_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(o => ZoomDown()).StartLoop(20, 20);
        }

        private void btnRight_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(o => ZoomRight()).StartLoop(20, 20);
        }

        private void btnLeft_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(o => ZoomLeft()).StartLoop(20, 20);
        }

        private void ZoomPlus()
        {
            _worldScale = CreateWorldScale(_worldScale.Factor * 0.97, GetCenter());
            picWorld.Invalidate();
        }

        private void ZoomMinus()
        {
            _worldScale = CreateWorldScale(_worldScale.Factor * (1 / 0.97), GetCenter());
            picWorld.Invalidate();
        }

        private void ZoomUp()
        {
            double dx = 0;
            double dy = _worldScale.ScreenToRealDistance(picWorld.Height / 60f);

            _worldScale = CreateWorldScale(_worldScale.Factor, RealPoint.Shift(GetCenter(), dx, dy));
            picWorld.Invalidate();
        }

        private void ZoomDown()
        {
            double dx = 0;
            double dy = -_worldScale.ScreenToRealDistance(picWorld.Height / 60f);

            _worldScale = CreateWorldScale(_worldScale.Factor, RealPoint.Shift(GetCenter(), dx, dy));
            picWorld.Invalidate();
        }

        private void ZoomLeft()
        {
            double dx = -_worldScale.ScreenToRealDistance(picWorld.Height / 60f);
            double dy = 0;

            _worldScale = CreateWorldScale(_worldScale.Factor, RealPoint.Shift(GetCenter(), dx, dy));
            picWorld.Invalidate();
        }

        private void ZoomRight()
        {
            double dx = _worldScale.ScreenToRealDistance(picWorld.Height / 60f);
            double dy = 0;

            _worldScale = CreateWorldScale(_worldScale.Factor, RealPoint.Shift(GetCenter(), dx, dy));
            picWorld.Invalidate();
        }

        private void ZoomStep()
        {
            double dx = _worldScale.ScreenToRealDistance(picWorld.Height / 60f) * _moveHorizontal;
            double dy = _worldScale.ScreenToRealDistance(picWorld.Height / 60f) * _moveVertical;

            _worldScale = CreateWorldScale(_worldScale.Factor + _moveZoom, RealPoint.Shift(GetCenter(), dx, dy));
            //_worldScale = CreateWorldScale(_worldScale.Factor + _moveZoom, GetCenter());

            picWorld.Invalidate();
        }

        int _step;

        private void ZoomStepParam()
        {
            if(_step < 20)
                _worldScale = CreateWorldScale(_worldScale.Factor, RealPoint.Shift(GetCenter(), _moveHorizontal, _moveVertical));
            else if(_step < 40)
                _worldScale = CreateWorldScale(_worldScale.Factor - _moveZoom, GetCenter());

            _step++;
            if(_step == 40)
            {
                _moveHorizontal = 0;
                _moveVertical = 0;
                _moveZoom = 0;
            }

            picWorld.Invalidate();
        }

        private void MainForm_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)
            {
                _moveVertical = 0;
            }
            else if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
            {
                _moveHorizontal = 0;
            }

            if (_moveVertical == 0 && _moveHorizontal == 0 && _moveLoop != null)
            {
                _moveLoop.Cancel();
                _moveLoop.WaitEnd();
                _moveLoop = null;
            }
        }

        private void btnOrigin_Click(object sender, EventArgs e)
        {
            _worldScale = CreateWorldScale(_worldScale.Factor, new RealPoint());
            picWorld.Invalidate();
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (keyData == Keys.Down)
                _moveVertical = -1;
            else if (keyData == Keys.Up)
                _moveVertical = 1;
            else if (keyData == Keys.Left)
                _moveHorizontal = -1;
            else if (keyData == Keys.Right)
                _moveHorizontal = 1;

            if ((_moveVertical != 0 || _moveHorizontal != 0) && _moveLoop == null)
            {
                _moveLoop = ThreadManager.CreateThread(o => ZoomStep());
                _moveLoop.StartInfiniteLoop(20);
            }

            return base.ProcessCmdKey(ref msg, keyData);
        }
    }
}

<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/RealPointWithLine.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class RealPointWithLine
    {
        public static bool Contains(RealPoint containingPoint, Line containedLine)
        {
            // Un point fini ne peut pas contenir une droite infinie
            return false;
        }

        public static bool Cross(RealPoint point, Line line)
        {
            return LineWithRealPoint.Cross(line, point);
        }

        public static double Distance(RealPoint point, Line line)
        {
            return LineWithRealPoint.Distance(line, point);
        }

        public static List<RealPoint> GetCrossingPoints(RealPoint point, Line line)
        {
            return LineWithRealPoint.GetCrossingPoints(line, point);
        }
    }
}
<file_sep>/GoBot/Composants/TextBoxPlus.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;

namespace Composants
{
    public partial class TextBoxPlus : TextBox
    {
        // Fonctionnalités supplémentaires :
        //      - Texte par défaut affiché en gris si aucun texte, disparition au focus
        //              DefaultText = "Exemple";
        //      - Mode erreur pour afficher le TextBox en rouge clair et police rouge. Disparition au focus.
        //              ErrorMode = true;
        //      - Mode numérique : n'accepte que les entiers
        //              TextMode = TextModeEnum.Numeric;
        //      - Mode décimal : n'accepte que les nombres décimaux
        //              TextMode = TextModeEnum.Decimal;

        private String defaultText = "";
        private bool errorMode = false;

        public TextModeEnum TextMode { get; set; } = TextModeEnum.Text;

        public enum TextModeEnum
        {
            Text,
            Numeric,
            Decimal
        }

        /// <summary>
        /// Texte affiché par défaut si aucun autre texte n'est saisi
        /// </summary>
        public String DefaultText 
        {
            get
            {
                return defaultText;
            }
            set
            {
                defaultText = value;
                if (Text == "")
                {
                    Text = defaultText;
                    ForeColor = Color.LightGray;
                }
            }
        }

        public TextBoxPlus()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Obtient ou définit si le mode erreur est activé
        /// </summary>
        public bool ErrorMode
        {
            get 
            { 
                return errorMode; 
            }
            set
            {
                errorMode = value;

                if (errorMode)
                {
                    ForeColor = Color.Red;
                    BackColor = Color.Salmon;
                }
                else
                {
                    BackColor = Color.White;
                    if (Text == DefaultText)
                        ForeColor = Color.LightGray;
                    else
                        ForeColor = Color.Black;
                }
            }
        }

        private void TextBoxPlus_Enter(object sender, EventArgs e)
        {
            ErrorMode = false;
            if (Text == DefaultText)
            {
                ForeColor = Color.Black;
                Text = "";
            }
        }

        private void TextBoxPlus_Leave(object sender, EventArgs e)
        {
            if (Text == "")
            {
                ForeColor = Color.LightGray;
                Text = DefaultText;
            }
        }

        private void TextBoxPlus_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (TextMode == TextModeEnum.Text)
                return;

            // Si la caractère tapé est numérique
            if (char.IsNumber(e.KeyChar))
                if (e.KeyChar == '²')
                    e.Handled = true; // Si c'est un '²', on gère l'evenement.
                else
                    return;

            // Si le caractère tapé est un caractère de "controle" (Enter, backspace, ...), on laisse passer
            else if (char.IsControl(e.KeyChar) || (e.KeyChar == '.' && this.TextMode != TextModeEnum.Numeric))
                e.Handled = false;
            else
                e.Handled = true; 
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/UDP/UdpFrameDecoder.cs
﻿using System;
using System.Collections.Generic;

namespace GoBot.Communications.UDP
{
    static class UdpFrameDecoder
    {
        public static String Decode(Frame trame)
        {
            return GetMessage(trame);
        }

        /// <summary>
        /// Récupère le message explicite associé à une fonction du protocole.
        /// Les paramètres sont signalés par des accolades indiquant la position dans la trame des octets correspondant à la valeur du paramètre.
        /// Si les paramètres sont fournis, leur valeur remplacent les paramètres entre accolades.
        /// </summary>
        /// <param name="function"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static String GetMessage(UdpFrameFunction function, List<uint> parameters = null)
        {
            String output = "";

            switch (function)
            {
                case UdpFrameFunction.DemandeTension:
                    output = "Demande tension";
                    break;
                case UdpFrameFunction.RetourTension:
                    output = "Retour tension {0-1}V";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, (parameters[0] / 100f).ToString("0.00"));
                    }

                    break;
                case UdpFrameFunction.DemandeLidar:
                    output = "Demande LIDAR";
                    break;
                case UdpFrameFunction.ReponseLidar:
                    output = "Reponse LIDAR";
                    break;
                case UdpFrameFunction.EnvoiUart2:
                    output = "Envoi UART2";
                    break;
                case UdpFrameFunction.RetourUart2:
                    output = "Retour UART2";
                    break;
                case UdpFrameFunction.DemandeValeursNumeriques:
                    output = "Demande valeurs des ports numériques";
                    break;
                case UdpFrameFunction.RetourValeursNumeriques:
                    output = "Retour ports numériques : {0}_{1} / {2}_{3} / {4}_{5}";
                    if (parameters != null)
                    {
                        for (int i = 0; i < 6; i++)
                            output = ReplaceParam(output, Convert.ToString(parameters[i], 2).PadLeft(8, '0'));
                    }
                    break;

                case UdpFrameFunction.Debug:
                    output = "Debug {0}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.TestConnexion:
                    output = "Test connexion";
                    break;
                case UdpFrameFunction.DemandeCapteurOnOff:
                    output = "Demande capteur {0}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((SensorOnOffID)parameters[0]));
                    }
                    break;
                case UdpFrameFunction.RetourCapteurOnOff:
                    output = "Retour capteur {0} : {1}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((SensorOnOffID)parameters[0]));
                        output = ReplaceParam(output, NameFinder.GetName(parameters[1] > 0));
                    }
                    break;
                case UdpFrameFunction.DemandeValeursAnalogiques:
                    output = "Demande valeurs analogiques";
                    break;
                case UdpFrameFunction.RetourValeursAnalogiques:
                    output = "Retour valeurs analogiques : {0}V / {1}V / {2}V / {3}V / {4}V / {5}V / {6}V / {7}V / {8}V";
                    if (parameters != null)
                    {
                        for (int i = 0; i < 8; i++)
                        {
                            output = ReplaceParam(output, (parameters[i] * 0.0008056640625).ToString("0.0000") + "V");
                        }
                    }
                    break;
                case UdpFrameFunction.DemandeCapteurCouleur:
                    output = "Demande capteur couleur {0}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((SensorColorID)parameters[0]));
                    }
                    break;
                case UdpFrameFunction.RetourCapteurCouleur:
                    output = "Retour capteur couleur {0} : {1}-{2}-{3}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((SensorColorID)parameters[0]));
                        output = ReplaceParam(output, parameters[1].ToString("000"));
                        output = ReplaceParam(output, parameters[2].ToString("000"));
                        output = ReplaceParam(output, parameters[3].ToString("000"));
                    }
                    break;
                case UdpFrameFunction.DemandePositionCodeur:
                    output = "Demande position codeur {0}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((CodeurID)parameters[0]));
                    }
                    break;
                case UdpFrameFunction.RetourPositionCodeur:
                    output = "Retour position codeur {0} : {1-2-3-4}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((CodeurID)parameters[0]));
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case UdpFrameFunction.PilotageOnOff:
                    output = "Pilote actionneur on off {0} : {1}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((ActuatorOnOffID)parameters[0]));
                        output = ReplaceParam(output, NameFinder.GetName(parameters[1] > 0));
                    }
                    break;
                case UdpFrameFunction.MoteurPosition:
                    output = "Pilote moteur {0} position {1-2}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((MotorID)parameters[0]));
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case UdpFrameFunction.MoteurVitesse:
                    output = "Pilote moteur {0} vitesse {2-3} vers la {1}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((MotorID)parameters[0]));
                        output = ReplaceParam(output, parameters[1].ToString());
                        output = ReplaceParam(output, ((SensGD)parameters[2]).ToString().ToLower());
                    }
                    break;
                case UdpFrameFunction.MoteurAccel:
                    output = "Pilote moteur {0} accélération {1-2}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((MotorID)parameters[0]));
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case UdpFrameFunction.MoteurStop:
                    output = "Moteur {0} stoppé {1}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((MotorID)parameters[0]));
                        output = ReplaceParam(output, ((StopMode)parameters[1]).ToString());
                    }
                    break;
                case UdpFrameFunction.MoteurOrigin:
                    output = "Envoi du moteur {0} à l'origine";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((MotorID)parameters[0]));
                    }
                    break;
                case UdpFrameFunction.MoteurResetPosition:
                    output = "Moteur {0} réintialisé à 0";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((MotorID)parameters[0]));
                    }
                    break;
                case UdpFrameFunction.MoteurFin:
                    output = "Moteur {0} arrivé à destination";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((MotorID)parameters[0]));
                    }
                    break;
                case UdpFrameFunction.MoteurBlocage:
                    output = "Moteur {0} bloqué";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, NameFinder.GetName((MotorID)parameters[0]));
                    }
                    break;
                case UdpFrameFunction.Deplace:
                    output = "{0} sur {1-2} mm";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, (SensAR)parameters[0] == SensAR.Avant ? "Avance" : "Recule");
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case UdpFrameFunction.Pivot:
                    output = "Pivot {0} sur {1-2}°";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((SensGD)parameters[0]).ToString().ToLower());
                        output = ReplaceParam(output, (parameters[1] / 100.0).ToString());
                    }
                    break;
                case UdpFrameFunction.Virage:
                    output = "Virage {0} {1} de {4-5}° sur un rayon de {2-3}mm";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((SensAR)parameters[0]).ToString().ToLower());
                        output = ReplaceParam(output, ((SensGD)parameters[1]).ToString().ToLower());
                        output = ReplaceParam(output, (parameters[2] / 100.0).ToString());
                        output = ReplaceParam(output, parameters[3].ToString());
                    }
                    break;
                case UdpFrameFunction.Stop:
                    output = "Stop {0}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((StopMode)parameters[0]).ToString().ToLower());
                    }
                    break;
                case UdpFrameFunction.Recallage:
                    output = "Recallage {0}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((SensAR)parameters[0]).ToString().ToLower());
                    }
                    break;
                case UdpFrameFunction.TrajectoirePolaire:
                    output = "Trajectoire polaire {0} sur {1-2} points";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((SensAR)parameters[0]).ToString().ToLower());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case UdpFrameFunction.FinRecallage:
                    output = "Fin de recallage";
                    break;
                case UdpFrameFunction.FinDeplacement:
                    output = "Fin de déplacement";
                    break;
                case UdpFrameFunction.Blocage:
                    output = "Blocage !!!";
                    break;
                case UdpFrameFunction.AsserDemandePositionCodeurs:
                    output = "Demande position codeurs déplacement";
                    break;
                case UdpFrameFunction.AsserRetourPositionCodeurs:
                    output = "Retour position codeurs déplacement : {0} valeurs";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.AsserEnvoiConsigneBrutePosition:
                    output = "Envoi consigne brute : {1-2} pas en {0}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                        output = ReplaceParam(output, ((SensAR)parameters[0]).ToString().ToLower());
                    }
                    break;
                case UdpFrameFunction.DemandeChargeCPU_PWM:
                    output = "Demande charge CPU PWM";
                    break;
                case UdpFrameFunction.RetourChargeCPU_PWM:
                    output = "Retour charge CPU PWM : {0} valeurs";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.AsserIntervalleRetourPosition:
                    output = "Intervalle de retour de la position : {0}ms";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, (parameters[0] * 10).ToString());
                    }
                    break;
                case UdpFrameFunction.AsserDemandePositionXYTeta:
                    output = "Demande position X Y Teta";
                    break;
                case UdpFrameFunction.AsserRetourPositionXYTeta:
                    output = "Retour position X = {0-1}mm Y = {2-3}mm Teta = {4-5}°";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, (parameters[0] / 10.0).ToString());
                        output = ReplaceParam(output, (parameters[1] / 10.0).ToString());
                        output = ReplaceParam(output, (parameters[2] / 100.0).ToString());
                    }
                    break;
                case UdpFrameFunction.AsserVitesseDeplacement:
                    output = "Vitesse ligne : {0-1}mm/s";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.AsserAccelerationDeplacement:
                    output = "Accélération ligne : {0-1}mm/s² au début, {2-3}mm/s² à la fin";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.AsserVitessePivot:
                    output = "Vitesse pivot : {0-1}mm/s";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.AsserAccelerationPivot:
                    output = "Accélération pivot : {0-1}mm/s²";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.AsserPID:
                    output = "Asservissement P={0-1} I={2-3} D={4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                        output = ReplaceParam(output, parameters[2].ToString());
                    }
                    break;
                case UdpFrameFunction.AsserEnvoiPositionAbsolue:
                    output = "Envoi position absolue : X={0-1}mm Y={2-3}mm Teta={4-5}°";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                        output = ReplaceParam(output, (parameters[2] / 100.0).ToString());
                    }
                    break;
                case UdpFrameFunction.AsserPIDCap:
                    output = "Asservissement cap P={0-1} I={2-3} D={4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case UdpFrameFunction.AsserPIDVitesse:
                    output = "Asservissement vitesse P={0-1} I={2-3} D={4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case UdpFrameFunction.EnvoiUart1:
                    output = "Envoi UART {0} caractères";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.RetourUart1:
                    output = "Réception UART {0} caractères";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                case UdpFrameFunction.EnvoiCAN:
                    output = "Envoi message CAN";
                    break;
                case UdpFrameFunction.ReponseCAN:
                    output = "Reception message CAN";
                    break;
                case UdpFrameFunction.DemandeCouleurEquipe:
                    output = "Demande couleur équipe";
                    break;
                case UdpFrameFunction.RetourCouleurEquipe:
                    output = "Retour couleur équipe : {0}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString());
                    }
                    break;
                default:
                    output = "Inconnu";
                    break;
            }

            return output;
        }

        /// <summary>
        /// Retourne le message contenu dans une trame dans un texte explicite avec les paramètres décodés
        /// </summary>
        /// <param name="trame">Trame à décoder</param>
        /// <returns>Message sur le contnu de la trame</returns>
        public static String GetMessage(Frame trame)
        {
            String output = "";

            output = GetMessage((UdpFrameFunction)trame[1]);
            output = GetMessage((UdpFrameFunction)trame[1], GetParameters(output, trame));

            return output;
        }

        /// <summary>
        /// Retourne la liste des valeurs des paramètres d'une trame selon le format donné
        /// </summary>
        /// <param name="format">Format du décodage sous la forme "Message valeur = {0-1}" équivaut à un paramètre situé sur les 1er et 2eme octets.</param>
        /// <param name="frame">Trame dans laquelle lire les valeurs</param>
        /// <returns>Liste des valeurs brutes des paramètres</returns>
        private static List<uint> GetParameters(String format, Frame frame)
        {
            List<uint> parameters = new List<uint>();
            String subParameter;

            for (int iChar = 0; iChar < format.Length; iChar++)
            {
                if (format[iChar] == '{')
                {
                    iChar++;
                    parameters.Add(0);

                    while (format[iChar] != '}')
                    {
                        subParameter = "";
                        while (format[iChar] != '-' && format[iChar] != '}')
                        {
                            subParameter += format[iChar];
                            iChar++;
                        }
                        parameters[parameters.Count - 1] = parameters[parameters.Count - 1] * 256 + frame[int.Parse(subParameter) + 2];
                        if (format[iChar] == '-')
                            iChar++;
                    }
                }
            }

            return parameters;
        }

        /// <summary>
        /// Remplace le premier paramètre entre accolades par le message donné
        /// </summary>
        /// <param name="input">Chaine contenant le message entier paramétré</param>
        /// <param name="msg">Message à écrire à la place du premier paramètre</param>
        /// <returns>Chaine contenant le message avec le paramètre remplacé</returns>
        private static String ReplaceParam(String input, String msg)
        {
            int iStart = input.IndexOf("{");
            int iEnd = input.IndexOf("}");

            String param = input.Substring(iStart, iEnd - iStart + 1);
            String output = input.Replace(param, msg);

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/Robots/RobotSimu.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using Geometry;
using System.Timers;
using Geometry.Shapes;
using System.Threading;
using GoBot.Actions;
using System.Drawing;
using System.Diagnostics;
using GoBot.Threading;
using GoBot.BoardContext;

namespace GoBot
{
    class RobotSimu : Robot
    {
        private Random _rand;
        private Semaphore _lockMove;
        private bool _highResolutionAsservissement;

        private Position _destination;
        private double _currentSpeed;
        private SensGD _sensPivot;
        private SensAR _sensMove;
        private bool _inRecalibration;
        private ThreadLink _linkAsserv, _linkLogPositions;

        private Position _currentPosition;

        private Stopwatch _asserChrono;
        private int _currentPolarPoint;
        private List<RealPoint> _polarTrajectory;

        private long _lastTimerTick;

        public override Position Position
        {
            get { return _currentPosition; }
            protected set { _currentPosition = value; _destination = value; }
        }

        public RobotSimu(IDRobot idRobot) : base(idRobot, "Robot Simu")
        {
            _highResolutionAsservissement = true;
            _rand = new Random();

            _linkAsserv = ThreadManager.CreateThread(link => Asservissement());
            _linkLogPositions = ThreadManager.CreateThread(link => LogPosition());

            _lockMove = new Semaphore(1, 1);
            _sensMove = SensAR.Avant;

            _inRecalibration = false;
            _asserChrono = null;
            _currentPolarPoint = -1;

            BatterieVoltage = 25;
        }

        public override void Init()
        {
            Historique = new Historique(IDRobot);
            PositionsHistorical = new List<Position>();
            Position = new Position(GoBot.Recalibration.StartPosition);

            PositionTarget = null;

            _linkAsserv.StartInfiniteLoop(new TimeSpan(0, 0, 0, 0, _highResolutionAsservissement ? 1 : 16));
            _linkLogPositions.StartInfiniteLoop(new TimeSpan(0, 0, 0, 0, 100));
        }

        public override void DeInit()
        {
            _linkAsserv.Cancel();
            _linkLogPositions.Cancel();

            _linkAsserv.WaitEnd();
            _linkLogPositions.WaitEnd();
        }

        public override string ReadLidarMeasure(LidarID lidar, int timeout, out Position refPosition)
        {
            refPosition = new Position(_currentPosition);

            // Pas de simulation de LIDAR

            return "";
        }

        private void LogPosition()
        {
            _linkLogPositions.RegisterName();

            lock (PositionsHistorical)
            {
                PositionsHistorical.Add(new Position(Position.Angle, new RealPoint(Position.Coordinates.X, Position.Coordinates.Y)));

                while (PositionsHistorical.Count > 1200)
                    PositionsHistorical.RemoveAt(0);
            }
        }

        private double GetCurrentLineBreakDistance()
        {
            return (_currentSpeed * _currentSpeed) / (2 * SpeedConfig.LineDeceleration);
        }

        private double GetCurrentAngleBreakDistance()
        {
            return (_currentSpeed * _currentSpeed) / (2 * SpeedConfig.PivotDeceleration);
        }

        private double DistanceBetween(List<RealPoint> points, int from, int to)
        {
            double distance = 0;

            for (int i = from + 1; i <= to; i++)
                distance += points[i - 1].Distance(points[i]);

            return distance;
        }

        private void Asservissement()
        {
            _linkAsserv.RegisterName();

            // Calcul du temps écoulé depuis la dernière mise à jour de la position
            double interval = 0;

            if (_asserChrono != null)
            {
                long currentTick = _asserChrono.ElapsedMilliseconds;
                interval = currentTick - _lastTimerTick;
                _lastTimerTick = currentTick;
            }
            else
                _asserChrono = Stopwatch.StartNew();

            if (interval > 0)
            {
                _lockMove.WaitOne();

                if (_currentPolarPoint >= 0)
                {
                    double distanceBeforeNext = Position.Coordinates.Distance(_polarTrajectory[_currentPolarPoint]);
                    double distanceBeforeEnd = distanceBeforeNext;

                    distanceBeforeEnd += DistanceBetween(_polarTrajectory, _currentPolarPoint, _polarTrajectory.Count - 1);

                    if (distanceBeforeEnd > GetCurrentAngleBreakDistance())
                        _currentSpeed = Math.Min(SpeedConfig.LineSpeed, _currentSpeed + SpeedConfig.LineAcceleration / (1000.0 / interval));
                    else
                        _currentSpeed = _currentSpeed - SpeedConfig.LineDeceleration / (1000.0 / interval);

                    double distanceToRun = _currentSpeed / (1000.0 / interval);

                    double distanceTested = distanceBeforeNext;

                    bool changePoint = false;
                    while (distanceTested < distanceToRun && _currentPolarPoint < _polarTrajectory.Count - 1)
                    {
                        _currentPolarPoint++;
                        distanceTested += _polarTrajectory[_currentPolarPoint - 1].Distance(_polarTrajectory[_currentPolarPoint]);
                        changePoint = true;
                    }

                    Segment segment;
                    Circle circle;

                    if (changePoint)
                    {
                        segment = new Segment(_polarTrajectory[_currentPolarPoint - 1], _polarTrajectory[_currentPolarPoint]);
                        circle = new Circle(_polarTrajectory[_currentPolarPoint - 1], distanceToRun - (distanceTested - _polarTrajectory[_currentPolarPoint - 1].Distance(_polarTrajectory[_currentPolarPoint])));
                    }
                    else
                    {
                        segment = new Segment(Position.Coordinates, _polarTrajectory[_currentPolarPoint]);
                        circle = new Circle(Position.Coordinates, distanceToRun);
                    }

                    RealPoint newPos = segment.GetCrossingPoints(circle)[0];
                    AngleDelta a = -Maths.GetDirection(newPos, _polarTrajectory[_currentPolarPoint]).angle;
                    _currentPosition = new Position(new AnglePosition(a), newPos);
                    OnPositionChanged(Position);

                    if (_currentPolarPoint == _polarTrajectory.Count - 1)
                    {
                        _currentPolarPoint = -1;
                        _destination.Copy(Position);
                    }
                }
                else
                {
                    bool needLine = _destination.Coordinates.Distance(Position.Coordinates) > 0;
                    bool needAngle = Math.Abs(_destination.Angle - Position.Angle) > 0.01;

                    if (needAngle)
                    {
                        AngleDelta diff = Math.Abs(_destination.Angle - Position.Angle);

                        double speedWithAcceleration = Math.Min(SpeedConfig.PivotSpeed, _currentSpeed + SpeedConfig.PivotAcceleration / (1000.0 / interval));
                        double remainingDistanceWithAcceleration = CircleArcLenght(WheelSpacing, diff) - (_currentSpeed + speedWithAcceleration) / 2 / (1000.0 / interval);

                        if (remainingDistanceWithAcceleration > DistanceFreinage(speedWithAcceleration))
                        {
                            double distParcourue = (_currentSpeed + speedWithAcceleration) / 2 / (1000.0 / interval);
                            AngleDelta angleParcouru = (360 * distParcourue) / (Math.PI * WheelSpacing);

                            _currentSpeed = speedWithAcceleration;

                            Position.Angle += (_sensPivot.Factor() * angleParcouru);
                        }
                        else if (_currentSpeed > 0)
                        {
                            double speedWithDeceleration = Math.Max(0, _currentSpeed - SpeedConfig.PivotDeceleration / (1000.0 / interval));
                            double distParcourue = (_currentSpeed + speedWithDeceleration) / 2 / (1000.0 / interval);
                            AngleDelta angleParcouru = (360 * distParcourue) / (Math.PI * WheelSpacing);

                            _currentSpeed = speedWithDeceleration;

                            Position.Angle += (_sensPivot.Factor() * angleParcouru);
                        }
                        else
                        {
                            Position.Copy(_destination);
                        }

                        OnPositionChanged(Position);
                    }
                    else if (needLine)
                    {
                        double speedWithAcceleration = Math.Min(SpeedConfig.LineSpeed, _currentSpeed + SpeedConfig.LineAcceleration / (1000.0 / interval));
                        double remainingDistanceWithAcceleration = Position.Coordinates.Distance(_destination.Coordinates) - (_currentSpeed + speedWithAcceleration) / 2 / (1000.0 / interval);

                        // Phase accélération ou déccélération
                        if (remainingDistanceWithAcceleration > DistanceFreinage(speedWithAcceleration))
                        {
                            double distance = (_currentSpeed + speedWithAcceleration) / 2 / (1000.0 / interval);
                            _currentSpeed = speedWithAcceleration;

                            Position.Move(distance * _sensMove.Factor());
                        }
                        else if (_currentSpeed > 0)
                        {
                            double speedWithDeceleration = Math.Max(0, _currentSpeed - SpeedConfig.LineDeceleration / (1000.0 / interval));
                            double distance = Math.Min(_destination.Coordinates.Distance(Position.Coordinates), (_currentSpeed + speedWithDeceleration) / 2 / (1000.0 / interval));
                            _currentSpeed = speedWithDeceleration;

                            Position.Move(distance * _sensMove.Factor());
                        }
                        else
                        {
                            // Si on est déjà à l'arrêt on force l'équivalence de la position avec la destination.

                            Position.Copy(_destination);
                            IsInLineMove = false;
                        }

                        OnPositionChanged(Position);
                    }
                }

                _lockMove.Release();
            }
        }

        private double DistanceFreinage(Double speed)
        {
            return (speed * speed) / (2 * SpeedConfig.LineDeceleration);
        }

        private double CircleArcLenght(double diameter, AngleDelta arc)
        {
            return Math.Abs(arc.InDegrees) / 360 * Math.PI * diameter;
        }

        public override void MoveForward(int distance, bool wait = true)
        {
            base.MoveForward(distance, wait);

            IsInLineMove = true;

            if (distance > 0)
            {
                if (!_inRecalibration)
                    Historique.AjouterAction(new ActionAvance(this, distance));
                _sensMove = SensAR.Avant;
            }
            else
            {
                if (!_inRecalibration)
                    Historique.AjouterAction(new ActionRecule(this, -distance));
                _sensMove = SensAR.Arriere;
            }

            _destination = new Position(Position.Angle, new RealPoint(Position.Coordinates.X + distance * Position.Angle.Cos, Position.Coordinates.Y + distance * Position.Angle.Sin));

            // TODO2018 attente avec un sémaphore ?
            if (wait)
                while ((Position.Coordinates.X != _destination.Coordinates.X ||
                    Position.Coordinates.Y != _destination.Coordinates.Y) && !Execution.Shutdown)
                    Thread.Sleep(10);
        }

        public override void MoveBackward(int distance, bool wait = true)
        {
            MoveForward(-distance, wait);
        }

        public override void PivotLeft(AngleDelta angle, bool wait = true)
        {
            base.PivotLeft(angle, wait);

            angle = Math.Round(angle, 2);
            Historique.AjouterAction(new ActionPivot(this, angle, SensGD.Gauche));

            _destination = new Position(Position.Angle - angle, new RealPoint(Position.Coordinates.X, Position.Coordinates.Y));
            _sensPivot = SensGD.Gauche;

            if (wait)
                while (Position.Angle != _destination.Angle)
                    Thread.Sleep(10);
        }

        public override void PivotRight(AngleDelta angle, bool wait = true)
        {
            base.PivotRight(angle, wait);

            angle = Math.Round(angle, 2);
            Historique.AjouterAction(new ActionPivot(this, angle, SensGD.Droite));

            _destination = new Position(Position.Angle + angle, new RealPoint(Position.Coordinates.X, Position.Coordinates.Y));
            _sensPivot = SensGD.Droite;

            if (wait)
                while (Position.Angle != _destination.Angle)
                    Thread.Sleep(10);
        }

        public override void Stop(StopMode mode)
        {
            Historique.AjouterAction(new ActionStop(this, mode));

            _lockMove.WaitOne();

            if (mode == StopMode.Smooth)
            {
                Position nouvelleDestination = new Position(Position.Angle, new RealPoint(_currentPosition.Coordinates.X, _currentPosition.Coordinates.Y));

                if (IsInLineMove)
                {
                    if (_sensMove == SensAR.Avant)
                        nouvelleDestination.Move(GetCurrentLineBreakDistance());
                    else
                        nouvelleDestination.Move(-GetCurrentLineBreakDistance());
                }

                _destination = nouvelleDestination;
            }
            else if (mode == StopMode.Abrupt)
            {
                _currentSpeed = 0;
                _destination = Position;
            }
            _lockMove.Release();
        }

        public override void Turn(SensAR sensAr, SensGD sensGd, int rayon, AngleDelta angle, bool wait = true)
        {
            // TODO2018
        }

        public override void PolarTrajectory(SensAR sens, List<RealPoint> points, bool wait = true)
        {
            _polarTrajectory = points;
            _currentPolarPoint = 0;

            while (wait && _currentPolarPoint != -1)
                Thread.Sleep(10);
        }

        public override void SetAsservOffset(Position newPosition)
        {
            Position = new Position(newPosition.Angle, newPosition.Coordinates);
            PositionTarget?.Set(Position.Coordinates);

            OnPositionChanged(Position);
        }

        public override void Recalibration(SensAR sens, bool wait = true, bool sendOffset = false)
        {
            _inRecalibration = true;

            Historique.AjouterAction(new ActionRecallage(this, sens));

            if (wait)
                RecalProcedure(sens);
            else
                ThreadManager.CreateThread(link => RecalProcedure(sens)).StartThread();

            base.Recalibration(sens, wait, sendOffset);
        }

        private void RecalProcedure(SensAR sens)
        {
            int realAccel = SpeedConfig.LineAcceleration;
            int realDecel = SpeedConfig.LineAcceleration;

            SpeedConfig.LineAcceleration = 50000;
            SpeedConfig.LineDeceleration = 50000;

            IShape contact = GetBounds();

            double lenght = sens == SensAR.Arriere ? LenghtBack : LenghtFront;

            while (Position.Coordinates.X - lenght > 0 &&
                Position.Coordinates.X + lenght < GameBoard.Width &&
                Position.Coordinates.Y - lenght > 0 &&
                Position.Coordinates.Y + lenght < GameBoard.Height)// &&
                                                                   //!GameBoard.ObstaclesAll.ToList().Exists(o => o.Cross(contact))) // TODO ça marche pas on dirait le test de recallage sur les obstacles
            {
                if (sens == SensAR.Arriere)
                    MoveBackward(1);
                else
                    MoveForward(1);

                contact = GetBounds();
            }

            SpeedConfig.LineAcceleration = realAccel;
            SpeedConfig.LineDeceleration = realDecel;

            _inRecalibration = false;
        }

        public override bool ReadSensorOnOff(SensorOnOffID sensor, bool wait = true)
        {
            // TODO
            return true;
        }

        public override Color ReadSensorColor(SensorColorID sensor, bool wait = true)
        {
            ColorPlus c = Color.White;
            int i = DateTime.Now.Millisecond + DateTime.Now.Second * 1000 + DateTime.Now.Minute * 60 * 1000 + DateTime.Now.Hour * 60 * 60 * 1000;
            int max = 999 + 59 * 1000 + 59 * 60 * 1000 + 23 * 60 * 60 * 1000;

            i %= (15 * 1000);
            max %= (15 * 1000);

            int steps = 10;
            int maxColor = 255 * steps;

            i = (int)Math.Floor(i / (max / (float)maxColor));

            switch (i / 255)
            {
                case 0:
                    c = ColorPlus.FromRgb(255, i % 255, 0); break;
                case 1:
                    c = ColorPlus.FromRgb(255 - i % 255, 255, 0); break;
                case 2:
                    c = ColorPlus.FromRgb(0, 255, i % 255); break;
                case 3:
                    c = ColorPlus.FromRgb(0, 255 - i % 255, 255); break;
                case 4:
                    c = ColorPlus.FromRgb(i % 255, 0, 255); break;
                case 5:
                    c = ColorPlus.FromRgb(255, 0, 255 - i % 255); break;
                case 6:
                    c = ColorPlus.FromRgb(255 - i % 255, 0, 0); break;
                case 7:
                    c = ColorPlus.FromRgb(i % 255, i % 255, i % 255); break;
                case 8:
                    c = ColorPlus.FromRgb(255 - i % 255 / 2, 255 - i % 255 / 2, 255 - i % 255 / 2); break;
                case 9:
                    c = ColorPlus.FromHsl(0, i % 255 / 255f, 0.5); break;
            }

            OnSensorColorChanged(sensor, c);
            return SensorsColorValue[sensor];
        }

        public override void SendPID(int p, int i, int d)
        {
            // TODO
        }

        public override void SendPIDCap(int p, int i, int d)
        {
            // TODO
        }

        public override void SendPIDSpeed(int p, int i, int d)
        {
            // TODO
        }

        public override void SetActuatorOnOffValue(ActuatorOnOffID actuator, bool on)
        {
            // TODO
            Historique.AjouterAction(new ActionOnOff(this, actuator, on));
        }

        public override void SetMotorAtPosition(MotorID motor, int vitesse, bool wait)
        {
            base.SetMotorAtPosition(motor, vitesse);
        }

        public override void SetMotorSpeed(MotorID motor, SensGD sens, int speed)
        {
            base.SetMotorSpeed(motor, sens, speed);
        }

        public override void SetMotorAcceleration(MotorID motor, int acceleration)
        {
            base.SetMotorAcceleration(motor, acceleration);
        }

        public override void EnablePower(bool on)
        {
            // TODO
            if (!on)
            {
                /*VitesseDeplacement = 0;
                AccelerationDeplacement = 0;
                VitessePivot = 0;
                AccelerationPivot = 0;*/
            }
        }

        public override bool ReadStartTrigger()
        {
            return true;
        }

        public override List<int>[] DiagnosticPID(int steps, SensAR sens, int pointsCount)
        {
            List<int>[] output = new List<int>[2];
            output[0] = new List<int>();
            output[1] = new List<int>();

            for (double i = 0; i < pointsCount; i++)
            {
                output[0].Add((int)(Math.Sin((i + DateTime.Now.Millisecond) / 100.0 * Math.PI) * steps * 10000000 / (i * i * i)));
                output[1].Add((int)(Math.Sin((i + DateTime.Now.Millisecond) / 100.0 * Math.PI) * steps * 10000000 / (i * i * i) + 10));
            }

            return output;
        }

        public override List<int>[] DiagnosticLine(int distance, SensAR sens)
        {
            List<int>[] output = new List<int>[2];
            output[0] = new List<int>();
            output[1] = new List<int>();

            RealPoint startPos = new RealPoint(Position.Coordinates);

            MoveForward(distance, false);

            while ((Position.Coordinates.X != _destination.Coordinates.X ||
                Position.Coordinates.Y != _destination.Coordinates.Y) && !Execution.Shutdown)
            {
                double dist = startPos.Distance(Position.Coordinates);
                output[0].Add((int)(dist * 1000));
                output[1].Add((int)(dist * 1000));
                Thread.Sleep(1);
            }

            return output;
        }

        public override List<double>[] DiagnosticCpuPwm(int pointsCount)
        {
            List<double> cpuLoad, pwmLeft, pwmRight;

            cpuLoad = new List<double>();
            pwmLeft = new List<double>();
            pwmRight = new List<double>();

            for (int i = 0; i < pointsCount; i++)
            {
                cpuLoad.Add((Math.Sin(i / (double)pointsCount * Math.PI * 2) / 2 + 0.5) * 0.2 + _rand.NextDouble() * 0.2 + 0.3);
                pwmLeft.Add(Math.Sin((DateTime.Now.Millisecond + i + DateTime.Now.Second * 1000) % 1500 / (double)1500 * Math.PI * 2) * 3800 + (_rand.NextDouble() - 0.5) * 400);
                pwmRight.Add(Math.Sin((DateTime.Now.Millisecond + i + DateTime.Now.Second * 1000) % 5000 / (double)5000 * Math.PI * 2) * 3800 + (_rand.NextDouble() - 0.5) * 400);
            }

            Thread.Sleep(pointsCount);

            return new List<double>[3] { cpuLoad, pwmLeft, pwmRight };
        }

        public override void ReadAnalogicPins(Board board, bool wait)
        {
            List<double> values = Enumerable.Range(1, 9).Select(o => o + _rand.NextDouble()).ToList();
            AnalogicPinsValue[board] = values;
        }

        public override void ReadNumericPins(Board board, bool wait)
        {
            for (int i = 0; i < 3 * 2; i++)
                NumericPinsValue[board][i] = (byte)((DateTime.Now.Second * 1000 + DateTime.Now.Millisecond) / 60000.0 * 255);
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/FramesLog.cs
﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Xml.Serialization;

namespace GoBot.Communications
{
    /// <summary>
    /// Association d'une trame et de son heure de réception
    /// </summary>
    public class TimedFrame : IComparable
    {
        /// <summary>
        /// Trame contenant les données
        /// </summary>
        public Frame Frame { get; set; }
        
        /// <summary>
        /// Date de la trame
        /// </summary>
        public DateTime Date { get; set; }

        /// <summary>
        /// Vrai si la trame a été reçue, faux si elle a été envoyée
        /// </summary>
        public bool IsInputFrame { get; set; }

        public TimedFrame(Frame frame, DateTime date, bool input = true)
        {
            Frame = frame;
            Date = date;
            IsInputFrame = input;
        }

        int IComparable.CompareTo(object obj)
        {
            return Date.CompareTo(((TimedFrame)obj).Date);
        }

        public void Export(StreamWriter writer)
        {
            writer.WriteLine(Date.ToString("dd/MM/yyyy hh:mm:ss.fff"));
            writer.WriteLine(Frame);
            writer.WriteLine(IsInputFrame);
        }

        public static TimedFrame Import(StreamReader reader)
        {
            DateTime date = DateTime.ParseExact(reader.ReadLine(), "dd/MM/yyyy hh:mm:ss.fff", null);
            Frame frame = new Frame(reader.ReadLine());
            Boolean isInput = Boolean.Parse(reader.ReadLine());

            return new TimedFrame(frame, date, isInput);
        }
    }

    /// <summary>
    /// Permet de sauvegarder l'historique des trames reçue ainsi que leur heure d'arrivée
    /// </summary>
    public class FramesLog
    {
        /// <summary>
        /// Extension du fichier de sauvegarde
        /// </summary>
        public static String FileExtension { get; } = ".tlog";

        /// <summary>
        /// Liste des trames
        /// </summary>
        public List<TimedFrame> Frames { get; private set; }

        public FramesLog()
        {
            Frames = new List<TimedFrame>();
        }

        /// <summary>
        /// Ajoute une trame reçue avec l'heure actuelle
        /// </summary>
        /// <param name="frame">Trame à ajouter</param>
        public void AddFrame(Frame frame, bool isInput, DateTime? date = null)
        {
            if (!date.HasValue)
                date = DateTime.Now;

            lock (Frames)
            {
                Frames.Add(new TimedFrame(frame, date.Value, isInput));
            }
        }

        /// <summary>
        /// Charge une sauvegarde de Replay
        /// </summary>
        /// <param name="fileName">Chemin du fichier</param>
        /// <returns>Vrai si la sauvegarde a été correctement chargée</returns>
        public bool Import(String fileName)
        {
            try
            {
                StreamReader reader = new StreamReader(fileName);

                int version = int.Parse(reader.ReadLine().Split(':')[1]);

                lock (Frames)
                {
                    while (!reader.EndOfStream)
                        Frames.Add(TimedFrame.Import(reader));
                }

                reader.Close();

                return true;
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Erreur lors de la lecture de " + this.GetType().Name + " : " + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// Sauvegarde l'ensemble des trames dans un fichier
        /// </summary>
        /// <param name="fileName">Chemin du fichier</param>
        /// <returns>Vrai si la sauvegarde s'est correctement déroulée</returns>
        public bool Export(String fileName)
        {
            try
            {
                StreamWriter writer = new StreamWriter(fileName);

                writer.WriteLine("Format:1");

                lock (Frames)
                {
                    foreach (TimedFrame frame in Frames)
                        frame.Export(writer);
                }

                writer.Close();

                return true;
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Erreur lors de l'enregistrement de " + this.GetType().Name + " : " + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// Permet de simuler la réception des trames enregistrées en respectant les intervalles de temps entre chaque trame
        /// </summary>
        public void ReplayInputFrames()
        {
            // Attention ça ne marche que pour l'UDP !
            for (int i = 0; i < Frames.Count;i++)
            {
                if (Frames[i].IsInputFrame)
                    Connections.UDPBoardConnection[UDP.UdpFrameFactory.ExtractBoard(Frames[i].Frame)].OnFrameReceived(Frames[i].Frame);

                if (i - 1 > 0)
                    Thread.Sleep(Frames[i].Date - Frames[i - 1].Date);
            }
        }

        /// <summary>
        /// Trie les trames par heure de réception
        /// </summary>
        public void Sort()
        {
            lock (Frames)
            {
                Frames.Sort();
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Elements/ConnectionDetails.cs
﻿using GoBot.Communications;
using GoBot.Communications.UDP;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class ConnectionDetails : UserControl
    {
        private UDPConnection _connection;

        public ConnectionDetails()
        {
            InitializeComponent();
        }

        public UDPConnection Connection
        {
            get
            {
                return _connection;
            }
            set
            {
                _connection = value;
                if(_connection != null)
                {
                    _grpConnection.Text = Connections.GetUDPBoardByConnection(_connection).ToString();
                    _lblIP.Text = _connection.IPAddress.ToString();
                    _lblInputPort.Text = _connection.InputPort.ToString();
                    _lblOutputPort.Text = _connection.OutputPort.ToString();
                }
            }
        }
    }
}
<file_sep>/GoBot/Composants/FocusablePanel.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;

namespace Composants
{
    public partial class FocusablePanel : UserControl
    {
        private bool Listening { get; set; }

        public delegate void KeyPressedDelegate(PreviewKeyDownEventArgs e);

        /// <summary>
        /// Se produit lorsque qu'une touche est appuyée
        /// </summary>
        public event KeyPressedDelegate KeyPressed;

        public FocusablePanel()
        {
            BackColor = Color.LightGray;
            InitializeComponent();
            Listening = true;
        }

        protected override void OnEnter(EventArgs e)
        {
            BackColor = Color.FromArgb(72, 216, 251);
            base.OnEnter(e);
        }

        protected override void OnLeave(EventArgs e)
        {
            BackColor = Color.LightGray;
            base.OnLeave(e);
        }

        protected override void  OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {
            e.IsInputKey = true;
            if (Listening)
            {
                Listening = false;
                KeyPressed(e);
            }
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            Listening = true;
            base.OnKeyUp(e);
        }
    }
}
<file_sep>/GoBot/GoBot/Movements/MovementFlags.cs
﻿using GoBot.Actionneurs;
using GoBot.BoardContext;
using GoBot.GameElements;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace GoBot.Movements
{
    class MovementFlags : Movement
    {
        public override bool CanExecute => true;

        public override int Score => 10;

        public override double Value => 0;

        public override GameElement Element => null;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => GameBoard.ColorNeutral;

        protected override void MovementBegin()
        {
        }

        protected override bool MovementCore()
        {
            Actionneur.Flags.DoOpenLeft();
            Actionneur.Flags.DoOpenRight();

            return true;
        }

        protected override void MovementEnd()
        {
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Elements/Fenetre.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class Fenetre : Form
    {
        public Fenetre(Control control)
        {
            Control = control;
            Controls.Add(control);
            control.SetBounds(0, 0, control.Width, control.Height);
            InitializeComponent();
            this.Width = control.Width + 10;
            this.Height = control.Height + 30;
        }

        public Control Control
        {
            get;
            private set;
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/Deplacement/ActionAvance.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionAvance : ITimeableAction
    {
        private int distance;
        private Robot robot;

        public ActionAvance(Robot r, int dist)
        {
            robot = r;
            distance = dist;
        }

        public override String ToString()
        {
            return robot.Name + " avance de " + distance + "mm";
        }

        void IAction.Executer()
        {
            robot.MoveForward(distance);
        }

        public System.Drawing.Image Image
        {
            get 
            { 
                return GoBot.Properties.Resources.UpGreen16;  
            }
        }

        public TimeSpan Duration
        {
            get
            {
                return robot.SpeedConfig.LineDuration(distance);
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/PolygonTriangle.cs
﻿using System;
using System.Collections.Generic;

namespace Geometry.Shapes
{
    /// <summary>
    /// Polygone à 3 côtés.
    /// </summary>
    public class PolygonTriangle : Polygon
    {
        /// <summary>
        /// Construit un triangle à partir de ses 3 sommets
        /// </summary>
        /// <param name="p1">Sommet 1</param>
        /// <param name="p2">Sommet 2</param>
        /// <param name="p3">Sommet 3</param>
        public PolygonTriangle(RealPoint p1, RealPoint p2, RealPoint p3)
            : base(new List<RealPoint>() { p1, p2, p3 })
        {
            // L'interet du triangle c'est qu'il est simple de calculer son aire et son barycentre et qu'on s'en sert pour calculer ceux de polygones quelconques
        }

        /// <summary>
        /// COnstruit un triangle à partir de ses 3 cotés
        /// </summary>
        /// <param name="p1">Coté 1</param>
        /// <param name="p2">Coté 2</param>
        /// <param name="p3">Coté 3</param>
        public PolygonTriangle(Segment s1, Segment s2, Segment s3)
            : base(new List<Segment>() { s1, s2, s3 })
        {
            if (s1.EndPoint != s2.StartPoint || s2.EndPoint != s3.StartPoint || s3.EndPoint != s1.StartPoint)
                throw new Exception("Triangle mal formé");
        }

        public PolygonTriangle(PolygonTriangle other) : base(other)
        {

        }

        protected override double ComputeSurface()
        {
            Segment seg = new Segment(Points[0], Points[1]);
            double height = seg.Distance(Points[2]);
            double width = seg.Length;
            return height * width / 2;
        }

        protected override RealPoint ComputeBarycenter()
        {
            RealPoint output = null;

            if (Points[0] == Points[1] && Points[0] == Points[2])
                output = new RealPoint(Points[0]);
            else if (Points[0] == Points[1])
                output = new Segment(Points[0], Points[2]).Barycenter;
            else if (Points[0] == Points[2])
                output = new Segment(Points[1], Points[2]).Barycenter;
            else if (Points[1] == Points[2])
                output = new Segment(Points[0], Points[1]).Barycenter;
            else
            {
                Line d1 = new Line(new Segment(Points[0], Points[1]).Barycenter, Points[2]);
                Line d2 = new Line(new Segment(Points[1], Points[2]).Barycenter, Points[0]);

                output = d1.GetCrossingPoints(d2)[0];
            }

            return output;
        }
    }
}
<file_sep>/GoBot/Geometry/Matrix.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry
{
    public static class Matrix
    {
        public static T[,] Transpose<T>(T[,] source)
        {
            int rowsCount = source.GetLength(0);
            int colsCount = source.GetLength(1);

            T[,] target = new T[colsCount, rowsCount];

            for (int iRow = 0; iRow < rowsCount; iRow++)
                for (int iCol = 0; iCol < colsCount; iCol++)
                    target[iCol, iRow] = source[iRow, iCol];

            return target;
        }

        public static double[,] Multiply(double[,] m1, double[,] m2)
        {
            int rowsCount1 = m1.GetLength(0);
            int colsCount1 = m1.GetLength(1);
            int colsCount2 = m2.GetLength(1);

            double[,] res = new double[rowsCount1, rowsCount1];

            for (int iRow = 0; iRow < rowsCount1; iRow++)
                for (int iCol2 = 0; iCol2 < colsCount2; iCol2++)
                    for (int iCol1 = 0; iCol1 < colsCount1; iCol1++)
                        res[iRow, iCol2] += m1[iRow, iCol1] * m2[iCol1, iCol2];

            return res;
        }

        public static double[] Multiply(double[,] m1, double[] m2)
        {
            double[] res = new double[m1.GetLength(0)];

            for (int iRow = 0; iRow < res.Length; iRow++)
                for (int iCol = 0; iCol < m2.Length; iCol++)
                    res[iRow] += m1[iRow, iCol] * m2[iCol];

            return res;
        }

        public static double Determinant2x2(double m00, double m01, double m10, double m11)
        {
            return ((m00 * m11) - (m10 * m01));
        }

        public static double Determinant2x2(double[,] m)
        {
            return Determinant2x2(m[0, 0], m[0, 1], m[1, 0], m[1, 1]);
        }

        public static double Determinant3x3(double[,] m)
        {
            var a = m[0, 0] * Determinant2x2(m[1, 1], m[1, 2], m[2, 1], m[2, 2]);
            var b = m[0, 1] * Determinant2x2(m[1, 0], m[1, 2], m[2, 0], m[2, 2]);
            var c = m[0, 2] * Determinant2x2(m[1, 0], m[1, 1], m[2, 0], m[2, 1]);

            return a - b + c;
        }

        public static double[,] Inverse3x3(double[,] m)
        {
            double d = (1 / Determinant3x3(m));

            return new double[3, 3]
            {
                {
                    +Determinant2x2(m[1,1], m[1,2], m[2,1], m[2,2]) * d,//[0,0]
				    -Determinant2x2(m[0,1], m[0,2], m[2,1], m[2,2]) * d,//[1,0]
				    +Determinant2x2(m[0,1], m[0,2], m[1,1], m[1,2]) * d,//[2,0]
			    },
                {
                    -Determinant2x2(m[1,0], m[1,2], m[2,0], m[2,2]) * d,//[0,1]
				    +Determinant2x2(m[0,0], m[0,2], m[2,0], m[2,2]) * d,//[1,1]
				    -Determinant2x2(m[0,0], m[0,2], m[1,0], m[1,2]) * d,//[2,1]
			    },
                {
                    +Determinant2x2(m[1,0], m[1,1], m[2,0], m[2,1]) * d,//[0,2]
				    -Determinant2x2(m[0,0], m[0,1], m[2,0], m[2,1]) * d,//[1,2]
				    +Determinant2x2(m[0,0], m[0,1], m[1,0], m[1,1]) * d,//[2,2]
			    }
            };
        }
    }
}
<file_sep>/GoBot/GoBot/Movements/MovementLightHouse.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Actionneurs;
using GoBot.BoardContext;
using GoBot.GameElements;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;

namespace GoBot.Movements
{
    class MovementLightHouse : Movement
    {
        LightHouse _lightHouse;

        public MovementLightHouse(LightHouse lighthouse)
        {
            _lightHouse = lighthouse;

            Positions.Add(new Position(-90, new RealPoint(_lightHouse.Position.X, 275)));
        }

        public override bool CanExecute
        { get
            {
                Buoy b = _lightHouse.Position.X < 1500 ? GameBoard.Elements.FindBuoy(new Geometry.Shapes.RealPoint(300, 400)) : GameBoard.Elements.FindBuoy(new Geometry.Shapes.RealPoint(2700, 400));
                return !_lightHouse.Enable && !b.IsAvailable;
            }
        }

        public override int Score => (10 + 3);

        public override double Value => 1.2;

        public override GameElement Element => _lightHouse;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => _lightHouse.Owner;

        protected override void MovementBegin()
        {
        }

        protected override bool MovementCore()
        {
            Robot.SetSpeedVerySlow();
            Robot.Recalibration(SensAR.Avant, true, true);
            Actionneur.ElevatorLeft.DoPushLight();
            Actionneur.ElevatorRight.DoPushLight();
            Thread.Sleep(750);
            _lightHouse.Enable = true;
            Actionneur.ElevatorLeft.DoPushInsideFast();
            Actionneur.ElevatorRight.DoPushInsideFast();
            Robot.SetSpeedFast();
            Robots.MainRobot.MoveBackward(100);

            return true;
        }

        protected override void MovementEnd()
        {
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/CircleWithPolygon.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class CircleWithPolygon
    {
        public static bool Contains(Circle circleContaining, Polygon polygonContained)
        {
            // Pour contenir un polygone il suffit de contenir tous ses cotés

            return  polygonContained.Sides.TrueForAll(s => circleContaining.Contains(s));
        }

        public static double Distance(Circle circle, Polygon polygon)
        {
            // Si une des forme contient l'autre ou qu'elles se croisent, la distance est de 0.
            // Sinon c'est la distance minimale à tous les segments.

            double distance;

            if (circle.Cross(polygon) || polygon.Contains(circle) || circle.Contains(polygon))
                distance = 0;
            else
                distance = polygon.Sides.Min(s => s.Distance(circle));
            
            return distance;
        }
        
        public static bool Cross(Circle circle, Polygon polygon)
        {
            // Pour croiser un polygone il suffit de croiser un de ses cotés
            
            return polygon.Sides.Exists(s => circle.Cross(s));
        }
        
        public static List<RealPoint> GetCrossingPoints(Circle circle, Polygon polygon)
        {
            // Croisement du cercle avec tous les segments du polygone

            return polygon.Sides.SelectMany(s => s.GetCrossingPoints(circle)).Distinct().ToList();
        }
    }
}
<file_sep>/GoBot/GoBot/AsserStats.cs
﻿using Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot
{
    public class AsserStats
    {
        /// <summary>
        /// Liste des distances parcourures en marche avant
        /// </summary>
        public List<int> ForwardMoves { get; protected set; }

        /// <summary>
        /// Liste des distances parcourures en marche arrière
        /// </summary>
        public List<int> BackwardMoves { get; protected set; }

        /// <summary>
        /// Liste des angle parcourus en pivot gauche
        /// </summary>
        public List<AngleDelta> LeftRotations { get; protected set; }

        /// <summary>
        /// Liste des angle parcourus en pivot droit
        /// </summary>
        public List<AngleDelta> RightsRotations { get; protected set; }

        public AsserStats()
        {
            ForwardMoves = new List<int>();
            BackwardMoves = new List<int>();
            LeftRotations = new List<AngleDelta>();
            RightsRotations = new List<AngleDelta>();
        }

        public TimeSpan CalculDuration(SpeedConfig config, Robot robot)
        {
            TimeSpan totalDuration = new TimeSpan();

            foreach (int dist in ForwardMoves.Union(BackwardMoves))
                totalDuration += config.LineDuration(dist);
            foreach (AngleDelta ang in LeftRotations.Union(RightsRotations))
                totalDuration += config.PivotDuration(ang, robot.WheelSpacing);

            return totalDuration;
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/ActionOnOff.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionOnOff : IAction
    {
        private bool on;
        ActuatorOnOffID actionneur;
        private Robot robot;

        public ActionOnOff(Robot r, ActuatorOnOffID ac, bool _on)
        {
            robot = r;
            on = _on;
            actionneur = ac;
        }

        public override String ToString()
        {
            return robot + " change " + NameFinder.GetName(actionneur) + " à " + (on ? "on" : "off");
        }

        void IAction.Executer()
        {
            robot.SetActuatorOnOffValue(actionneur, on);
        }

        public System.Drawing.Image Image
        {
            get 
            { 
                return GoBot.Properties.Resources.Motor16;  
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/PagesPanda/PagePandaActuators.cs
﻿using GoBot.Actionneurs;
using GoBot.GameElements;
using GoBot.Threading;
using System;
using System.Threading;
using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PagePandaActuators : UserControl
    {
        private ThreadLink _linkFingerRight, _linkFingerLeft;
        private bool _flagRight, _flagLeft;
        private bool _clamp1, _clamp2, _clamp3, _clamp4, _clamp5;
        private bool _grabberLeft, _grabberRight;

        public PagePandaActuators()
        {
            InitializeComponent();
            _grabberLeft = true;
            _grabberRight = true;
        }

        private void PagePandaActuators_Load(object sender, System.EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                btnTrap.Focus();
            }
        }

        private void btnFingerRight_Click(object sender, EventArgs e)
        {
            if (_linkFingerRight == null)
            {
                _linkFingerRight = Threading.ThreadManager.CreateThread(link => Actionneurs.Actionneur.FingerRight.DoDemoGrab(link));
                _linkFingerRight.StartThread();
            }
            else
            {
                _linkFingerRight.Cancel();
                _linkFingerRight.WaitEnd();
                _linkFingerRight = null;
            }
        }

        private void btnFingerLeft_Click(object sender, EventArgs e)
        {
            if (_linkFingerLeft == null)
            {
                _linkFingerLeft = Threading.ThreadManager.CreateThread(link => Actionneurs.Actionneur.FingerLeft.DoDemoGrab(link));
                _linkFingerLeft.StartThread();
            }
            else
            {
                _linkFingerLeft.Cancel();
                _linkFingerLeft.WaitEnd();
                _linkFingerLeft = null;
            }
        }

        private void btnFlagLeft_Click(object sender, EventArgs e)
        {
            if (!_flagLeft)
                Actionneur.Flags.DoOpenLeft();
            else
                Actionneur.Flags.DoCloseLeft();

            _flagLeft = !_flagLeft;
        }

        private void btnLeftPickup_Click(object sender, EventArgs e)
        {
            Actionneur.ElevatorLeft.DoDemoPickup();
        }

        private void btnLeftDropoff_Click(object sender, EventArgs e)
        {
            Actionneur.ElevatorRight.DoDemoDropoff();
        }

        private void btnRightPickup_Click(object sender, EventArgs e)
        {
            Actionneur.ElevatorRight.DoDemoPickup();
        }

        private void btnRightDropoff_Click(object sender, EventArgs e)
        {
            Actionneur.ElevatorRight.DoDemoDropoff();
        }

        private void btnSearchGreen_Click(object sender, EventArgs e)
        {
            Actionneur.ElevatorRight.DoSearchBuoy(Buoy.Green);
        }

        private void btnSearchRed_Click(object sender, EventArgs e)
        {
            Actionneur.ElevatorLeft.DoSearchBuoy(Buoy.Red);
        }

        private void btnGrabberRight_Click(object sender, EventArgs e)
        {
            _grabberRight = !_grabberRight;

            if (_grabberRight)
            {
                Actionneur.ElevatorRight.DoGrabOpen();
                Actionneur.ElevatorRight.Armed = true;
                btnGrabberRight.Image = Properties.Resources.GrabberRightOpened;
            }
            else
            {
                Actionneur.ElevatorRight.DoGrabClose();
                btnGrabberRight.Image = Properties.Resources.GrabberRightClosed;
            }
        }

        private void btnGrabberLeft_Click(object sender, EventArgs e)
        {
            _grabberLeft = !_grabberLeft;

            if (_grabberLeft)
            {
                Actionneur.ElevatorLeft.DoGrabOpen();
                btnGrabberLeft.Image = Properties.Resources.GrabberLeftOpened;
            }
            else
            {
                Actionneur.ElevatorLeft.DoGrabClose();
                btnGrabberLeft.Image = Properties.Resources.GrabberLeftClosed;
            }
        }

        private void btnClamp1_Click(object sender, EventArgs e)
        {
            _clamp1 = !_clamp1;

            if (_clamp1)
            {
                Config.CurrentConfig.ServoClamp1.SendPosition(Config.CurrentConfig.ServoClamp1.PositionOpen);
                btnClamp1.Image = Properties.Resources.Unlock64;
            }
            else
            {
                Config.CurrentConfig.ServoClamp1.SendPosition(Config.CurrentConfig.ServoClamp1.PositionClose);
                btnClamp1.Image = Properties.Resources.Lock64;
            }
        }

        private void btnClamp2_Click(object sender, EventArgs e)
        {
            _clamp2 = !_clamp2;

            if (_clamp2)
            {
                Config.CurrentConfig.ServoClamp2.SendPosition(Config.CurrentConfig.ServoClamp2.PositionOpen);
                btnClamp2.Image = Properties.Resources.Unlock64;
            }
            else
            {
                Config.CurrentConfig.ServoClamp2.SendPosition(Config.CurrentConfig.ServoClamp2.PositionClose);
                btnClamp2.Image = Properties.Resources.Lock64;
            }
        }

        private void btnClamp3_Click(object sender, EventArgs e)
        {
            _clamp3 = !_clamp3;

            if (_clamp3)
            {
                Config.CurrentConfig.ServoClamp3.SendPosition(Config.CurrentConfig.ServoClamp3.PositionOpen);
                btnClamp3.Image = Properties.Resources.Unlock64;
            }
            else
            {
                Config.CurrentConfig.ServoClamp3.SendPosition(Config.CurrentConfig.ServoClamp3.PositionClose);
                btnClamp3.Image = Properties.Resources.Lock64;
            }
        }

        private void btnClamp4_Click(object sender, EventArgs e)
        {
            _clamp4 = !_clamp4;

            if (_clamp4)
            {
                Config.CurrentConfig.ServoClamp4.SendPosition(Config.CurrentConfig.ServoClamp4.PositionOpen);
                btnClamp4.Image = Properties.Resources.Unlock64;
            }
            else
            {
                Config.CurrentConfig.ServoClamp4.SendPosition(Config.CurrentConfig.ServoClamp4.PositionClose);
                btnClamp4.Image = Properties.Resources.Lock64;
            }
        }

        private void btnClamp5_Click(object sender, EventArgs e)
        {
            _clamp5 = !_clamp5;

            if (_clamp5)
            {
                Config.CurrentConfig.ServoClamp5.SendPosition(Config.CurrentConfig.ServoClamp5.PositionOpen);
                btnClamp5.Image = Properties.Resources.Unlock64;
            }
            else
            {
                Config.CurrentConfig.ServoClamp5.SendPosition(Config.CurrentConfig.ServoClamp5.PositionClose);
                btnClamp5.Image = Properties.Resources.Lock64;
            }
        }

        private void btnFlagRight_Click(object sender, EventArgs e)
        {
            if (!_flagRight)
                Actionneur.Flags.DoOpenRight();
            else
                Actionneur.Flags.DoCloseRight();

            _flagRight = !_flagRight;
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelPositionables.cs
﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Reflection;

using GoBot.Actionneurs;

namespace GoBot.IHM
{
    public partial class PanelPositionables : UserControl
    {
        public Dictionary<String, PropertyInfo> _dicProperties;

        public PanelPositionables()
        {
            InitializeComponent();
        }

        private void PanelPositionables_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                cboPositionables.Items.AddRange(Config.Positionnables.ToArray());
            }
        }

        private void cboPositionables_SelectedValueChanged(object sender, EventArgs e)
        {
            Positionable positionnable = (Positionable)cboPositionables.SelectedItem;

            PropertyInfo[] properties = positionnable.GetType().GetProperties();

            List<String> positionsName = new List<string>();
            _dicProperties = new Dictionary<string, PropertyInfo>();

            foreach (PropertyInfo property in properties)
            {
                if (property.Name != "ID")
                {
                    positionsName.Add(Config.PropertyNameToScreen(property) + " : " + property.GetValue(positionnable, null));
                    _dicProperties.Add(positionsName[positionsName.Count - 1], property);
                }
            }

            cboPositions.Items.Clear();
            cboPositions.Items.AddRange(positionsName.ToArray());

            trkPosition.Min = positionnable.Minimum;
            trkPosition.Max = positionnable.Maximum;

            btnSend.Enabled = true;
            btnSave.Enabled = false;
        }

        private void cboPositions_SelectedValueChanged(object sender, EventArgs e)
        {
            String[] tab = ((String)(cboPositions.SelectedItem)).Split(new char[]{':'});

            String position = tab[0].Trim();
            int valeur = Convert.ToInt32(tab[1].Trim());

            numPosition.Value = valeur;

            btnSave.Enabled = true;
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            String[] tab = ((String)(cboPositions.SelectedItem)).Split(new char[] { ':' });

            String position = tab[0].Trim().ToLower();
            int valeur = Convert.ToInt32(tab[1].Trim());
            
            if (MessageBox.Show(this, "Êtes vous certain de vouloir sauvegarder la position " + position + " de l'actionneur " + cboPositionables.Text.ToLower() + " à " + numPosition.Value + " (anciennement " + valeur + ") ?", "Confirmation", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
            {
                int index = cboPositions.SelectedIndex;

                _dicProperties[(String)cboPositions.SelectedItem].SetValue((Positionable)cboPositionables.SelectedItem, (int)numPosition.Value, null);
                cboPositionables_SelectedValueChanged(null, null);

                cboPositions.SelectedIndex = index;

                Config.Save();
            }
        }

        private void trkPosition_TickValueChanged(object sender, double value)
        {
            numPosition.Value = (decimal)value;
            SendPosition();
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            SendPosition();
        }

        private void SendPosition()
        {
            Positionable positionnable = (Positionable)cboPositionables.SelectedItem;
            positionnable?.SendPosition((int)numPosition.Value);
        }
    }
}
<file_sep>/GoBot/GoBot/Movements/MovementGroundedZone.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Actionneurs;
using GoBot.GameElements;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;

namespace GoBot.Movements
{
    class MovementGroundedZone : Movement
    {
        GroundedZone _zone;

        public override bool CanExecute => !Actionneur.Lifter.Loaded && Element.IsAvailable;

        public override int Score => 0;

        public override double Value => 1;

        public override GameElement Element => _zone;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => Element.Owner;

        public MovementGroundedZone(GroundedZone zone) : base()
        {
            _zone = zone;

            Positions.Add(new Position(180, new RealPoint(_zone.Position.X - 70 - 225, zone.Position.Y)));
            Positions.Add(new Position(0, new RealPoint(_zone.Position.X + 70 + 225, zone.Position.Y)));
            Positions.Add(new Position(90, new RealPoint(_zone.Position.X, zone.Position.Y + 70 + 225)));
        }

        protected override void MovementBegin()
        {
            // rien
        }

        protected override bool MovementCore()
        {
            Actionneur.Lifter.DoSequencePickup();
            Actionneur.Lifter.Load = _zone.Buoys.Select(b => b.Color).ToList();
            _zone.IsAvailable = false;
            _zone.Buoys.ForEach(b => b.IsAvailable = false);
            Robots.MainRobot.MoveForward(100);

            return true;
        }

        protected override void MovementEnd()
        {
            // rien
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PagePower.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;
using System.Timers;

namespace GoBot.IHM.Pages
{
    public partial class PagePower : UserControl
    {
        private System.Timers.Timer _timerVoltage;
        private bool _loaded;

        public PagePower()
        {
            InitializeComponent();
            _loaded = false;
        }

        public void StartGraph()
        {
            ctrlGraphic.NamesVisible = true;
            ctrlGraphic.MinLimit = 20;
            ctrlGraphic.MaxLimit = 26;
            ctrlGraphic.ScaleMode = Composants.GraphPanel.ScaleType.FixedIfEnough;
            ctrlGraphic.LimitsVisible = true;

            _timerVoltage = new System.Timers.Timer(1000);
            _timerVoltage.Elapsed += new ElapsedEventHandler(timerTension_Elapsed);
            _timerVoltage.Start();
        }

        private void PanelAlimentation_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                numBatHighToAverage.Value = (decimal)Config.CurrentConfig.BatterieRobotVert;
                numBatAverageToLow.Value = (decimal)Config.CurrentConfig.BatterieRobotOrange;
                numBatLowToVeryLow.Value = (decimal)Config.CurrentConfig.BatterieRobotRouge;
                numBatVeryLowToAbsent.Value = (decimal)Config.CurrentConfig.BatterieRobotCritique;

                batAbsent.CurrentState = Composants.Battery.State.Absent;
                batVeryLow.CurrentState = Composants.Battery.State.VeryLow;
                batLow.CurrentState = Composants.Battery.State.Low;
                batAverage.CurrentState = Composants.Battery.State.Average;
                batHigh.CurrentState = Composants.Battery.State.High;

                _loaded = true;
            }
        }

        void timerTension_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (Execution.Shutdown)
                return;

            this.InvokeAuto(() =>
            {
                ctrlGraphic.AddPoint("Tension", Robots.MainRobot.BatterieVoltage, Color.DodgerBlue, true);
                ctrlGraphic.AddPoint("High", Config.CurrentConfig.BatterieRobotVert, Color.LimeGreen);
                ctrlGraphic.AddPoint("Average", Config.CurrentConfig.BatterieRobotOrange, Color.Orange);
                ctrlGraphic.AddPoint("Low", Config.CurrentConfig.BatterieRobotRouge, Color.Firebrick);
                ctrlGraphic.AddPoint("VeryLow", Config.CurrentConfig.BatterieRobotCritique, Color.Black);

                ctrlGraphic.DrawCurves();
            });
        }

        private void numBat_ValueChanged(object sender, EventArgs e)
        {
            if (_loaded)
            {
                Config.CurrentConfig.BatterieRobotVert = (double)numBatHighToAverage.Value;
                Config.CurrentConfig.BatterieRobotOrange = (double)numBatAverageToLow.Value;
                Config.CurrentConfig.BatterieRobotRouge = (double)numBatLowToVeryLow.Value;
                Config.CurrentConfig.BatterieRobotCritique = (double)numBatVeryLowToAbsent.Value;

                Config.Save();
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Utils/LimitedQueue.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GoBot.Utils
{
    class LimitedQueue<T> : Queue<T>
    {
        private int _limit;

        public LimitedQueue(int limit)
        {
            _limit = limit;
        }

        public int Limit
        {
            get
            {
                return _limit;
            }
            set
            {
                _limit = value;
            }
        }

        public new void Enqueue(T val)
        {
            base.Enqueue(val);
            while (this.Count > _limit) base.Dequeue();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Elements/PanelSensorOnOff.cs
﻿using System.Drawing;
using System.Windows.Forms;

using GoBot.Threading;

namespace GoBot.IHM
{
    public partial class PanelSensorOnOff : UserControl
    {
        private SensorOnOffID _sensor;
        private ThreadLink _linkPolling;

        public PanelSensorOnOff()
        {
            InitializeComponent();
        }

        private void PanelSensorOnOff_Load(object sender, System.EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                Robots.MainRobot.SensorOnOffChanged += MainRobot_SensorOnOffChanged;
            }
        }

        private void MainRobot_SensorOnOffChanged(SensorOnOffID sensor, bool state)
        {
            if (sensor == _sensor && _linkPolling != null)  ledState.Color = state ? Color.LimeGreen : Color.Red;
        }

        public void SetSensor(SensorOnOffID sensor)
        {
            _sensor = sensor;
            lblName.Text = NameFinder.GetName(sensor).Substring(0, 1).ToUpper() + NameFinder.GetName(sensor).Substring(1);
        }

        private void btnOnOff_ValueChanged(object sender, bool value)
        {
            if(value)
            {
                ledState.Color = Robots.MainRobot.ReadSensorOnOff(_sensor) ? Color.LimeGreen : Color.Red;
                _linkPolling = ThreadManager.CreateThread(link => PollingSensor());
                _linkPolling.RegisterName(nameof(PollingSensor) + " " + lblName.Text);
                _linkPolling.StartInfiniteLoop(50);
            }
            else
            {
                _linkPolling.Cancel();
                _linkPolling.WaitEnd();
                _linkPolling = null;
                ledState.Color = Color.Gray;
            }
        }

        private void PollingSensor()
        {
            Robots.MainRobot.ReadSensorOnOff(_sensor, false);
        }
    }
}
<file_sep>/GoBot/GoBot/Logs/LogConsole.cs
﻿using System;
using System.IO;

namespace GoBot.Logs
{
    public class LogConsole : ILog
    {
        public LogConsole()
        {
            Console.WriteLine("Begin of log");
        }

        public void Write(String message)
        {
            Console.WriteLine(DateTime.Now.ToString("dd:MM:yyyy hh:mm:ss:ffff\t") + message);
        }

        public void Close()
        {
            Console.WriteLine("End of log");
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/Deplacement/ActionVirage.cs
﻿using Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionVirage : IAction
    {
        private int distance;
        private AngleDelta angle;
        private Robot robot;
        private SensGD sensGD;
        private SensAR sensAR;

        public ActionVirage(Robot r, int dist, AngleDelta a, SensAR ar, SensGD gd)
        {
            robot = r;
            distance = dist;
            angle = a;
            sensAR = ar;
            sensGD = gd;
        }

        public override String ToString()
        {
            return robot.Name + " tourne " + distance + "mm " + angle + "° " + sensAR.ToString().ToLower() + " " + sensGD.ToString().ToLower();
        }

        void IAction.Executer()
        {
            robot.Turn(sensAR, sensGD, distance, angle);
        }

        public System.Drawing.Image Image
        {
            get
            {
                if (sensAR == SensAR.Avant)
                {
                    if (sensGD == SensGD.Droite)
                        return GoBot.Properties.Resources.BottomToRigth16;
                    else
                        return GoBot.Properties.Resources.BottomToLeft16;
                }
                else
                {
                    if (sensGD == SensGD.Droite)
                        return GoBot.Properties.Resources.TopToRigth16;
                    else
                        return GoBot.Properties.Resources.TopToLeft16;
                }
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageEnvoiUdp.cs
﻿using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Net;

using GoBot.Communications;
using GoBot.Communications.UDP;
using GoBot.Communications.CAN;
using GoBot.Actionneurs;
using System.Diagnostics;

namespace GoBot.IHM.Pages
{
    public partial class PageEnvoiUdp : UserControl
    {
        public PageEnvoiUdp()
        {
            InitializeComponent();
        }

        private void btnEnvoyer_Click(object sender, EventArgs e)
        {
            Frame trame = null;
            try
            {
                trame = new Frame(txtTrame.Text);
            }
            catch (Exception)
            {
                txtTrame.ErrorMode = true;
                return;
            }

            if (boxMove.Checked)
                Connections.ConnectionMove.SendMessage(trame);
            if (boxIO.Checked)
                Connections.ConnectionIO.SendMessage(trame);
            if (boxCan.Checked)
                Connections.ConnectionCan.SendMessage(trame);
        }

        private void PanelEnvoiUdp_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                switchBoutonMove.Value = true;
                switchBoutonIO.Value = true;

                foreach (UDPConnection conn in Connections.AllConnections.OfType<UDPConnection>())
                {
                    ConnectionDetails details = new ConnectionDetails();
                    details.Connection = conn;
                    _pnlConnections.Controls.Add(details);
                }

                IPAddress[] adresses = Dns.GetHostAddresses(Dns.GetHostName());

                bool ipTrouvee = false;
                foreach (IPAddress ip in adresses)
                {
                    if (ip.ToString().Length > 7)
                    {
                        String ipString = ip.ToString().Substring(0, 7);
                        if (ipString == "10.1.0.")
                        {
                            lblMonIP.Text = ip.ToString();
                            ipTrouvee = true;
                        }
                    }
                }

                if (!ipTrouvee)
                {
                    lblMonIP.Text = "Incorrecte";
                    lblMonIP.ForeColor = Color.Red;
                }
            }
        }
        
        private void btnDebug_Click(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            int val = Convert.ToInt16(btn.Tag);

            if (boxMove.Checked)
            {
                Frame trame = UdpFrameFactory.Debug(Board.RecMove, val);
                Connections.ConnectionMove.SendMessage(trame);
            }
            if (boxIO.Checked)
            {
                Frame trame = UdpFrameFactory.Debug(Board.RecIO, val);
                Connections.ConnectionIO.SendMessage(trame);
            }
        }

        private System.Windows.Forms.Timer timerTestConnexion;
        private void btnSendTest_Click(object sender, EventArgs e)
        {
            if (btnSendTest.Text == "Envoyer")
            {
                btnSendTest.Text = "Stop";

                timerTestConnexion = new System.Windows.Forms.Timer();
                timerTestConnexion.Interval = (int)numIntervalleTest.Value;
                timerTestConnexion.Tick += new EventHandler(timerTestConnexion_Tick);

                timerTestConnexion.Start();
            }
            else
            {
                btnSendTest.Text = "Envoyer";
                timerTestConnexion.Stop();
            }
        }

        void timerTestConnexion_Tick(object sender, EventArgs e)
        {
            if (boxMove.Checked)
            {
                Frame trame = UdpFrameFactory.TestConnexion(Board.RecMove);
                Connections.ConnectionMove.SendMessage(trame);
            }
            if (boxIO.Checked)
            {
                Frame trame = UdpFrameFactory.TestConnexion(Board.RecIO);
                Connections.ConnectionIO.SendMessage(trame);
            }
        }

        private void switchBoutonConnexion_ValueChanged(object sender, bool value)
        {
            Connections.EnableConnection[Board.RecMove] = switchBoutonMove.Value;
            Connections.EnableConnection[Board.RecIO] = switchBoutonIO.Value;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/LineWithLine.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    public static class LineWithLine
    {
        public static bool Contains(Line containingLine, Line containedLine)
        {
            // Contenir une droite revient à avoir la même équation

            return (containingLine == containedLine);
        }

        public static bool Cross(Line line1, Line line2)
        {
            // Deux droites se croisent forcément si elles n'ont pas la même pente ou qu'elles sont toutes les deux verticales

            return !line1.IsParallel(line2);
        }
        
        public static double Distance(Line line1, Line line2)
        {
            double distance;

            if (!line1.IsParallel(line2))
            {
                // Si les droites se croisent la distance est de 0
                distance = 0;
            }
            else
            {
                // Sinon elles sont parrallèles donc on trace une perdendiculaire et une mesure la distance entre les croisements avec les 2 droites

                Line perpendicular = line1.GetPerpendicular(new RealPoint(0, 0));

                RealPoint p1 = line1.GetCrossingPoints(perpendicular)[0];
                RealPoint p2 = line2.GetCrossingPoints(perpendicular)[0];

                distance = p1.Distance(p2);
            }

            return distance;
        }

        public static List<RealPoint> GetCrossingPoints(Line line1, Line line2)
        {
            List<RealPoint> output = new List<RealPoint>();
            
            if (!line1.IsParallel(line2))
            {
                // Résolution de l'équation line1 = line2

                output.Add(new RealPoint
                {
                    X = (line2.B * line1.C - line2.C * line1.B) / (line2.C * line1.A - line2.A * line1.C),
                    Y = (line2.A * line1.B - line2.B * line1.A) / (line2.A * line1.C - line2.C * line1.A),
                });
            }

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/ConnectionChecker.cs
﻿using System;
using System.Timers;

namespace GoBot.Communications
{
    public class ConnectionChecker
    {
        /// <summary>
        /// Date de la dernière notification de connexion établie
        /// </summary>
        protected DateTime LastAliveDate { get; set; }
        /// <summary>
        /// Date du dernier envoi de test de connexion
        /// </summary>
        protected DateTime LastTestDate { get; set; }
        /// <summary>
        /// Timer qui envoie les tests de connexion et vérifie l'état de la connexion
        /// </summary>
        protected Timer CheckTimer { get; set; }

        /// <summary>
        /// Vrai si le timer de vérification est lancé
        /// </summary>
        public bool Started { get; protected set; }
        /// <summary>
        /// Connexion surveillée
        /// </summary>
        public Connection AttachedConnection { get; protected set; }
        /// <summary>
        /// Vrai si la connexion est actuellement établie
        /// </summary>
        public bool Connected { get; protected set; }

        /// <summary>
        /// Intervalle de vérification de la connexion
        /// </summary>
        public int Interval
        {
            get { return (int)CheckTimer.Interval; }
            set { CheckTimer.Interval = value; }
        }

        public delegate void ConnectionChangeDelegate(Connection sender, bool connected);
        /// <summary>
        /// Event appelé quand la connexion est trouvée ou perdue.
        /// </summary>
        public event ConnectionChangeDelegate ConnectionStatusChange;

        public delegate void SendConnectionTestDelegate(Connection sender);
        /// <summary>
        /// Event appelé à chaque tick pour actualiser la connexion. Abonner à cet event une fonction qui aura pour conséquence un appel à NotifyAlive si la connexion est établie.
        /// </summary>
        public event SendConnectionTestDelegate SendConnectionTest;
        
        /// <summary>
        /// Crée un ConnectionChecker en spécifiant la connexion surveillée et l'intervalle entre chaque vérification.
        /// </summary>
        public ConnectionChecker(Connection conn, int interval = 2000)
        {
            AttachedConnection = conn;
            LastAliveDate = DateTime.Now - new TimeSpan(0, 1, 0);
            LastTestDate = DateTime.Now;

            Started = false;

            CheckTimer = new Timer();
            CheckTimer.Interval = interval;
            CheckTimer.Elapsed += new ElapsedEventHandler(CheckTimer_Elapsed);

            Connected = false;
        }

        /// <summary>
        /// Démarre la surveillance.
        /// </summary>
        public void Start()
        {
            Started = true;
            CheckConnection();
            CheckTimer.Start();
        }

        /// <summary>
        /// Permet d'effectuer une vérification de la connexion en mettant à jour par rapport au dernier test de connexion puis en en envoyant un nouveau.
        /// Cette fonction est appelée périodiquement par le timer et peut également être appelée manuellement.
        /// </summary>
        public void CheckConnection()
        {
            UpdateStatus();
            SendConnectionTest?.Invoke(AttachedConnection); 
        }

        /// <summary>
        /// Mets à jour l'état de la connexion en fonction du dernier test effectué et de la dernière notification de retour reçue.
        /// </summary>
        public void UpdateStatus()
        {
            TimeSpan authorizedDelay = new TimeSpan(0, 0, 0, 0, (int)(CheckTimer.Interval * 0.5));
            TimeSpan effectiveDelay = LastTestDate - LastAliveDate;

            if (Connected && effectiveDelay > authorizedDelay)
            {
                Connected = false;
                ConnectionStatusChange?.Invoke(AttachedConnection, Connected);
            }

            else if (!Connected && effectiveDelay < authorizedDelay)
            {
                Connected = true;
                ConnectionStatusChange?.Invoke(AttachedConnection, Connected);
            }

            LastTestDate = DateTime.Now;
        }

        /// <summary>
        /// Spécifie que la connexion est bien établie à la date actuelle.
        /// </summary>
        public void NotifyAlive()
        {
            LastAliveDate = DateTime.Now;
            UpdateStatus();
        }

        /// <summary>
        /// Fonction appelée à chaque tick du timer.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            CheckConnection();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Forms/FormConfirm.cs
﻿using System;
using System.Windows.Forms;

using GoBot.Threading;

namespace GoBot.IHM.Forms
{
    public partial class FormConfirm : Form
    {
        ThreadLink _link;
        int _countdown;

        public FormConfirm()
        {
            InitializeComponent();
        }

        private void btnStay_Click(object sender, EventArgs e)
        {
            _link.Cancel();
            DialogResult = DialogResult.No;
            Close();
        }

        private void btnQuit_Click(object sender, EventArgs e)
        {
            _link.Cancel();
            DialogResult = DialogResult.Yes;
            Close();
        }

        private void FormConfirm_Shown(object sender, EventArgs e)
        {
            btnTrap.Focus();
            _countdown = 3;
            btnStay.Text = "Rester (" + _countdown + ")";
            _link = ThreadManager.CreateThread(link => Countdown());
            _link.StartLoop(1000, _countdown + 2);
        }

        private void Countdown()
        {
            if (!_link.Cancelled)
            {
                if (_countdown < 0)
                {
                    _link.Cancel();
                    btnStay.InvokeAuto(() => btnStay.PerformClick());
                }
                else
                {
                    btnStay.InvokeAuto(() => btnStay.Text = "Rester (" + _countdown + ")");
                }

                _countdown -= 1;
            }
        }

        private void FormConfirm_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                if (Screen.PrimaryScreen.Bounds.Width > 1024)
                {
                    this.FormBorderStyle = FormBorderStyle.FixedSingle;
                    this.WindowState = FormWindowState.Normal;
                }
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/CircleWithLine.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    public static class CircleWithLine
    {
        public static bool Contains(Circle containingCircle, Line containedLine)
        {
            // Un cercle ne peut pas contenir une droite...

            return false;
        }

        public static double Distance(Circle circle, Line line)
        {
            // Distance de la ligne jusqu'au centre du cercle - son rayon (Si négatif c'est que ça croise, donc distance 0)

            return Math.Max(0, line.Distance(circle.Center) - circle.Radius);
        }

        public static bool Cross(Circle circle, Line line)
        {
            // Si une droite croise le cercle, c'est que le point de la droite le plus proche du centre du cercle est éloigné d'une distance inférieure au rayon

            return line.Distance(circle.Center) <= circle.Radius;
        }

        public static List<RealPoint> GetCrossingPoints(Circle circle, Line line)
        {
            List<RealPoint> intersectsPoints = new List<RealPoint>();

            double x1, y1, x2, y2;

            if (line.IsVertical)
            {
                y1 = 0;
                x1 = -line.B;
                y2 = 100;
                x2 = -line.B;
            }
            else
            {
                x1 = 0;
                y1 = line.A * x1 + line.B;
                x2 = 100;
                y2 = line.A * x2 + line.B;
            }

            double dx = x2 - x1;
            double dy = y2 - y1;
            double Ox = x1 - circle.Center.X;
            double Oy = y1 - circle.Center.Y;
            double A = dx * dx + dy * dy;
            double B = 2 * (dx * Ox + dy * Oy);
            double C = Ox * Ox + Oy * Oy - circle.Radius * circle.Radius;
            double delta = B * B - 4 * A * C;

            if (delta < 0 + double.Epsilon && delta > 0 - double.Epsilon)
            {
                double t = -B / (2 * A);
                intersectsPoints.Add(new RealPoint(x1 + t * dx, y1 + t * dy));
            }
            if (delta > 0)
            {
                double t1 = (double)((-B - Math.Sqrt(delta)) / (2 * A));
                double t2 = (double)((-B + Math.Sqrt(delta)) / (2 * A));
                intersectsPoints.Add(new RealPoint(x1 + t1 * dx, y1 + t1 * dy));
                intersectsPoints.Add(new RealPoint(x1 + t2 * dx, y1 + t2 * dy));
            }

            return intersectsPoints;
        }
    }
}
<file_sep>/GoBot/GoBot/CameraIP.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Net;
using System.IO;

namespace GoBot
{
    class CameraIP
    {
        public String URLImage { get; set; }

        public CameraIP(String url)
        {
            URLImage = url;
        }

        public CameraIP()
        {
            URLImage = "http://10.1.0.10/snapshot.jpg";
        }

        public Bitmap GetImage()
        {
            try
            {
                // Récupération de l'image
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URLImage);
                Stream stream = req.GetResponse().GetResponseStream();
                Bitmap img = (Bitmap)Bitmap.FromStream(stream);
                return img;
            }
            catch (Exception)
            {
                return null;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/Deplacement/ActionStop.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionStop : IAction
    {
        private StopMode mode;
        private Robot robot;

        public ActionStop(Robot r, StopMode m)
        {
            robot = r;
            mode = m;
        }

        public override string ToString()
        {
            return robot.Name + " stop " + mode;
        }

        void IAction.Executer()
        {
            robot.Stop(mode);
        }

        public System.Drawing.Image Image
        {
            get 
            { 
                return GoBot.Properties.Resources.Stop16;  
            }
        }
    }
}
<file_sep>/GoBot/GeometryTester/TestAngles.cs
﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Geometry;

namespace GeometryTester
{
    [TestClass]
    public class TestAngles
    {
        [TestMethod]
        public void TestAngleDeltaDegRad()
        {
            AngleDelta deg = new AngleDelta(720, AngleType.Degre);
            AngleDelta rad = new AngleDelta(Math.PI * 4, AngleType.Radian);

            Assert.AreEqual(deg, rad);
            Assert.AreEqual(deg, 720, AngleDelta.PRECISION);
        }

        [TestMethod]
        public void TestAngleDeltaTrigo()
        {
            double a = Math.PI / 2.54; // Arbitraire

            AngleDelta deg = new AngleDelta(a, AngleType.Radian);

            Assert.AreEqual(Math.Cos(a), deg.Cos, AngleDelta.PRECISION);
            Assert.AreEqual(Math.Sin(a), deg.Sin, AngleDelta.PRECISION);
            Assert.AreEqual(Math.Tan(a), deg.Tan, AngleDelta.PRECISION);
        }

        [TestMethod]
        public void TestAngleDeltaModulo()
        {
            Assert.AreEqual(10, new AngleDelta(10).Modulo(), AngleDelta.PRECISION);
            Assert.AreEqual(10, new AngleDelta(370).Modulo(), AngleDelta.PRECISION);
            Assert.AreEqual(0, new AngleDelta(0).Modulo(), AngleDelta.PRECISION);
            Assert.AreEqual(180, new AngleDelta(180).Modulo(), AngleDelta.PRECISION);
            Assert.AreEqual(-180, new AngleDelta(-180).Modulo(), AngleDelta.PRECISION);
            Assert.AreEqual(0, new AngleDelta(360).Modulo(), AngleDelta.PRECISION);
            Assert.AreEqual(-10, new AngleDelta(-10).Modulo(), AngleDelta.PRECISION);
            Assert.AreEqual(170, new AngleDelta(-190).Modulo(), AngleDelta.PRECISION);
        }

        [TestMethod]
        public void TestAnglePositionDegRad()
        {
            AnglePosition deg = new AnglePosition(90, AngleType.Degre);
            AnglePosition rad = new AnglePosition(Math.PI / 2, AngleType.Radian);

            Assert.AreEqual(deg, rad);
            Assert.AreEqual(deg, 90, AnglePosition.PRECISION);
        }

        [TestMethod]
        public void TestAnglePositionModulo()
        {
            AnglePosition a800 = new AnglePosition(800, AngleType.Degre);
            AnglePosition a80 = new AnglePosition(80, AngleType.Degre);

            Assert.AreEqual(a800, a80);
            Assert.AreEqual(80, a800.InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(80, a800.InPositiveDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(80.0 / 180.0 * Math.PI, a800.InRadians, AnglePosition.PRECISION);
        }

        [TestMethod]
        public void TestAnglePositionNeg()
        {
            AnglePosition a = new AnglePosition(-50, AngleType.Degre);

            Assert.AreEqual(-50, a.InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(310, a.InPositiveDegrees, AnglePosition.PRECISION);
        }

        [TestMethod]
        public void TestAnglePositionAdd()
        {
            AnglePosition a10 = new AnglePosition(10);
            AnglePosition a190 = new AnglePosition(190);
            AnglePosition a350 = new AnglePosition(350);
            AngleDelta da10 = new AngleDelta(10);
            AngleDelta da80 = new AngleDelta(80);
            AngleDelta da20 = new AngleDelta(20);

            Assert.AreEqual(0, (a350 + da10).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(10, (a350 + da20).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(-90, (a190 + da80).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(20, (a10 + da10).InDegrees, AnglePosition.PRECISION);
        }

        [TestMethod]
        public void TestAnglePositionSub1()
        {
            AnglePosition a30 = new AnglePosition(30);
            AnglePosition a350 = new AnglePosition(350);
            AngleDelta da30 = new AngleDelta(30);
            AngleDelta da350 = new AngleDelta(350);

            Assert.AreEqual(40, (a30 - da350).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(-40, (a350 - da30).InDegrees, AnglePosition.PRECISION);
        }

        [TestMethod]
        public void TestAnglePositionSub2()
        {
            AnglePosition a0 = new AnglePosition(0);
            AnglePosition a30 = new AnglePosition(30);
            AnglePosition a60 = new AnglePosition(60);
            AnglePosition a89 = new AnglePosition(89);
            AnglePosition a90 = new AnglePosition(90);
            AnglePosition a91 = new AnglePosition(91);
            AnglePosition a270 = new AnglePosition(270);
            AnglePosition a350 = new AnglePosition(350);
            AnglePosition a360 = new AnglePosition(360);

            Assert.AreEqual(-40, (a350 - a30).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(40, (a30 - a350).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(30, (a90 - a60).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(-30, (a60 - a90).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(0, (a0 - a360).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(180, (a90 - a270).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(-179, (a91 - a270).InDegrees, AnglePosition.PRECISION);
            Assert.AreEqual(179, (a89 - a270).InDegrees, AnglePosition.PRECISION);
        }

        [TestMethod]
        public void TestAnglePositionEqual()
        {
            AnglePosition a1, a2;

            a1 = new AnglePosition(90);
            a2 = new AnglePosition(90);
            Assert.AreEqual(a1, a2);

            a1 = new AnglePosition(0);
            a2 = new AnglePosition(0);
            Assert.AreEqual(a1, a2);

            a1 = new AnglePosition(-10);
            a2 = new AnglePosition(350);
            Assert.AreEqual(a1, a2);

            a1 = new AnglePosition(0);
            a2 = new AnglePosition(360);
            Assert.AreEqual(a1, a2);

            a1 = new AnglePosition(90);
            a2 = new AnglePosition(-270);
            Assert.AreEqual(a1, a2);
        }

        [TestMethod]
        public void TestAnglePositionNotEqual()
        {
            AnglePosition a1, a2;

            a1 = new AnglePosition(85);
            a2 = new AnglePosition(86);
            Assert.AreNotEqual(a1, a2);

            a1 = new AnglePosition(0);
            a2 = new AnglePosition(359);
            Assert.AreNotEqual(a1, a2);
        }

        [TestMethod]
        public void TestAnglePositionCenter()
        {
            Assert.AreEqual(45, AnglePosition.Center(0, 90));
            Assert.AreEqual(45 + 180, AnglePosition.CenterLongArc(0, 90));
            Assert.AreEqual(45, AnglePosition.CenterSmallArc(0, 90));

            Assert.AreEqual(45 + 180, AnglePosition.Center(90, 0));
            Assert.AreEqual(45 + 180, AnglePosition.CenterLongArc(90, 0));
            Assert.AreEqual(45, AnglePosition.CenterSmallArc(90, 0));

            Assert.AreEqual(30, AnglePosition.Center(-20, 80));
            Assert.AreEqual(30 + 180, AnglePosition.CenterLongArc(-20, 80));
            Assert.AreEqual(30, AnglePosition.CenterSmallArc(-20, 80));

            Assert.AreEqual(30 + 180, AnglePosition.Center(80, -20));
            Assert.AreEqual(30 + 180, AnglePosition.CenterLongArc(80, -20));
            Assert.AreEqual(30, AnglePosition.CenterSmallArc(80, -20));

            Assert.AreEqual(175, AnglePosition.Center(80, 270));
            Assert.AreEqual(175, AnglePosition.CenterLongArc(80, 270));
            Assert.AreEqual(175 + 180, AnglePosition.CenterSmallArc(80, 270));

            Assert.AreEqual(175 + 180, AnglePosition.Center(270, 80));
            Assert.AreEqual(175, AnglePosition.CenterLongArc(270, 80));
            Assert.AreEqual(175 + 180, AnglePosition.CenterSmallArc(270, 80));
        }

        [TestMethod]
        public void TestAnglePositionIsOnArc()
        {
            Assert.IsTrue(((AnglePosition)45).IsOnArc(0, 90));
            Assert.IsTrue(((AnglePosition)0).IsOnArc(0, 90));
            Assert.IsTrue(((AnglePosition)90).IsOnArc(0, 90));
            Assert.IsFalse(((AnglePosition)91).IsOnArc(0, 90));
            Assert.IsFalse(((AnglePosition)(-50)).IsOnArc(0, 90));

            Assert.IsFalse(((AnglePosition)45).IsOnArc(90, 0));
            Assert.IsTrue(((AnglePosition)0).IsOnArc(90, 0));
            Assert.IsTrue(((AnglePosition)90).IsOnArc(90, 0));
            Assert.IsTrue(((AnglePosition)91).IsOnArc(90, 0));
            Assert.IsTrue(((AnglePosition)(-50)).IsOnArc(90, 0));

            Assert.IsTrue(((AnglePosition)0).IsOnArc(-20, 20));
            Assert.IsFalse(((AnglePosition)(-40)).IsOnArc(-20, 20));

            Assert.IsFalse(((AnglePosition)0).IsOnArc(20, -20));
            Assert.IsTrue(((AnglePosition)(-40)).IsOnArc(20, -20));
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Elements/PanelActuatorOnOff.cs
﻿using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class PanelActuatorOnOff : UserControl
    {
        private ActuatorOnOffID _actuator;

        public PanelActuatorOnOff()
        {
            InitializeComponent();
        }

        public void SetActuator(ActuatorOnOffID actuator)
        {
            _actuator = actuator;
            lblName.Text = NameFinder.GetName(actuator).Substring(0, 1).ToUpper() + NameFinder.GetName(actuator).Substring(1);
        }

        private void btnOnOff_ValueChanged(object sender, bool value)
        {
            Robots.MainRobot.SetActuatorOnOffValue(_actuator, value);
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/Connection.cs
﻿namespace GoBot.Communications
{
    public abstract class Connection
    {
        /// <summary>
        /// Sauvegarde des trames transitées par la connexion
        /// </summary>
        public FramesLog Archives { get; protected set; }

        /// <summary>
        /// Verificateur de connexion
        /// </summary>
        public ConnectionChecker ConnectionChecker { get; set; }

        /// <summary>
        /// Vrai si le client est connecté (dans le sens où la liaison est établie, mais pas forcément que le protocole répond)
        /// </summary>
        public bool Connected { get; set; }

        /// <summary>
        /// Envoi le message au client actuellement connecté
        /// </summary>
        /// <param name="message">Message à envoyer au client</param>
        /// <returns>Nombre de caractères envoyés</returns>
        abstract public bool SendMessage(Frame message);

        //Déclaration du délégué pour l’évènement réception ou émission de message
        public delegate void NewFrameDelegate(Frame frame);
        //Déclaration de l’évènement utilisant le délégué pour la réception d'une trame
        public event NewFrameDelegate FrameReceived;
        //Déclaration de l’évènement utilisant le délégué pour l'émission d'une trame
        public event NewFrameDelegate FrameSend;

        public Connection()
        {
            Archives = new FramesLog();
        }

        /// <summary>
        /// Lance la réception de trames sur la configuration actuelle
        /// </summary>
        abstract public void StartReception();

        abstract public string Name { get; set; }

        /// <summary>
        /// Libère la connexion vers le client
        /// </summary>
        abstract public void Close();

        /// <summary>
        /// Ajoute une trame reçue
        /// </summary>
        /// <param name="frame">Trame reçue</param>
        public void OnFrameReceived(Frame frame)
        {
            Archives.AddFrame(frame, true);

            FrameReceived?.Invoke(frame);
        }

        /// <summary>
        /// Ajoute une trame envoyée
        /// </summary>
        /// <param name="frame">Trame envoyée</param>
        public void OnFrameSend(Frame frame)
        {
            Archives.AddFrame(frame, false);

            FrameSend?.Invoke(frame);
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/CAN/CanFrameFactory.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Communications.CAN
{
    static class CanFrameFactory
    {
        public static CanFrameFunction ExtractFunction(Frame frame)
        {
            return (CanFrameFunction)frame[2];
        }

        public static CanBoard ExtractBoard(Frame frame)
        {
            return (CanBoard)(frame[0] * 256 + frame[1]);
        }

        public static CanBoard ExtractSender(Frame frame, bool isInput)
        {
            return isInput ? ExtractBoard(frame) : CanBoard.PC;
        }

        public static CanBoard ExtractReceiver(Frame frame, bool isInput)
        {
            return isInput ? CanBoard.PC : ExtractBoard(frame);
        }

        public static ServomoteurID ExtractServomoteurID(Frame frame)
        {
            if (Config.CurrentConfig.IsMiniRobot)
                return (ServomoteurID)((int)(ExtractBoard(frame) - 1) * 4 + frame[3]);
            else
                return (ServomoteurID)((int)(ExtractBoard(frame) - 1) * 4 + frame[3]);
        }

        public static int ExtractValue(Frame frame, int paramNo = 0)
        {
            return frame[4 + paramNo * 2] * 256 + frame[5 + paramNo * 2];
        }

        public static Frame BuildGetPosition(ServomoteurID id)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.PositionAsk;
            tab[3] = GlobalIdToServoNo(id);

            return new Frame(tab);
        }

        public static Frame BuildGetPositionMin(ServomoteurID id)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.PositionMinAsk;
            tab[3] = GlobalIdToServoNo(id);

            return new Frame(tab);
        }

        public static Frame BuildGetPositionMax(ServomoteurID id)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.PositionMaxAsk;
            tab[3] = GlobalIdToServoNo(id);

            return new Frame(tab);
        }

        public static Frame BuildGetSpeedMax(ServomoteurID id)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.SpeedMaxAsk;
            tab[3] = GlobalIdToServoNo(id);

            return new Frame(tab);
        }

        public static Frame BuildGetAcceleration(ServomoteurID id)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.AccelerationAsk;
            tab[3] = GlobalIdToServoNo(id);

            return new Frame(tab);
        }

        public static Frame BuildGetTorqueCurrent(ServomoteurID id)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.TorqueCurrentAsk;
            tab[3] = GlobalIdToServoNo(id);

            return new Frame(tab);
        }

        public static Frame BuildGetTorqueMax(ServomoteurID id)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.TorqueMaxAsk;
            tab[3] = GlobalIdToServoNo(id);

            return new Frame(tab);
        }

        public static Frame BuildSetAcceleration(ServomoteurID id, int acceleration)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.AccelerationSet;
            tab[3] = GlobalIdToServoNo(id);
            tab[4] = ByteDivide(acceleration, true);
            tab[5] = ByteDivide(acceleration, false);

            return new Frame(tab);
        }

        public static Frame BuildSetPosition(ServomoteurID id, int position)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.PositionSet;
            tab[3] = GlobalIdToServoNo(id);
            tab[4] = ByteDivide(position, true);
            tab[5] = ByteDivide(position, false);

            return new Frame(tab);
        }

        public static Frame BuildSetPositionMax(ServomoteurID id, int position)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.PositionMaxSet;
            tab[3] = GlobalIdToServoNo(id);
            tab[4] = ByteDivide(position, true);
            tab[5] = ByteDivide(position, false);

            return new Frame(tab);
        }

        public static Frame BuildSetPositionMin(ServomoteurID id, int position)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.PositionMinSet;
            tab[3] = GlobalIdToServoNo(id);
            tab[4] = ByteDivide(position, true);
            tab[5] = ByteDivide(position, false);

            return new Frame(tab);
        }

        public static Frame BuildSetSpeedMax(ServomoteurID id, int speed)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.SpeedMaxSet;
            tab[3] = GlobalIdToServoNo(id);
            tab[4] = ByteDivide(speed, true);
            tab[5] = ByteDivide(speed, false);

            return new Frame(tab);
        }

        public static Frame BuildSetTorqueMax(ServomoteurID id, int torque)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.TorqueMaxSet;
            tab[3] = GlobalIdToServoNo(id);
            tab[4] = ByteDivide(torque, true);
            tab[5] = ByteDivide(torque, false);

            return new Frame(tab);
        }

        public static Frame BuildSetTrajectory(ServomoteurID id, int position, int speed, int accel)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.TrajectorySet;
            tab[3] = GlobalIdToServoNo(id);
            tab[4] = ByteDivide(position, true);
            tab[5] = ByteDivide(position, false);
            tab[6] = ByteDivide(speed, true);
            tab[7] = ByteDivide(speed, false);
            tab[8] = ByteDivide(accel, true);
            tab[9] = ByteDivide(accel, false);

            return new Frame(tab);
        }

        public static Frame BuildDisableOutput(ServomoteurID id)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)GlobalIdToCanBoard(id);
            tab[2] = (byte)CanFrameFunction.DisableOutput;
            tab[3] = GlobalIdToServoNo(id);

            return new Frame(tab);
        }

        public static Frame BuildTestConnection(CanBoard board)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)board;
            tab[2] = (byte)CanFrameFunction.TestConnection;

            return new Frame(tab);
        }

        public static Frame BuildBeep(CanBoard board, int freqHz, int durationMs)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)board;
            tab[2] = (byte)CanFrameFunction.Buzzer;
            tab[3] = ByteDivide(freqHz, true);
            tab[4] = ByteDivide(freqHz, false);
            tab[5] = ByteDivide(durationMs, true);
            tab[6] = ByteDivide(durationMs, false);

            return new Frame(tab);
        }

        public static Frame BuildDebug(CanBoard board)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)board;
            tab[2] = (byte)CanFrameFunction.Debug;

            return new Frame(tab);
        }

        public static Frame BuildDebugAsk(CanBoard board)
        {
            byte[] tab = new byte[10];

            tab[0] = 0x00;
            tab[1] = (byte)board;
            tab[2] = (byte)CanFrameFunction.DebugAsk;

            return new Frame(tab);
        }

        private static byte ByteDivide(int valeur, bool mostSignifiantBit)
        {
            byte b;
            if (mostSignifiantBit)
                b = (byte)(valeur >> 8);
            else
                b = (byte)(valeur & 0x00FF);
            return b;
        }

        private static CanBoard GlobalIdToCanBoard(ServomoteurID servoGlobalId)
        {
            return (CanBoard)((int)servoGlobalId / 4 + 1);
        }

        private static byte GlobalIdToServoNo(ServomoteurID servoGlobalId)
        {
            return (byte)((int)servoGlobalId % 4);
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/PagesPanda/PagePandaScore.cs
﻿using GoBot.Threading;
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PagePandaScore : UserControl
    {
        public PagePandaScore()
        {
            InitializeComponent();
        }

        private void picLogo_MouseDown(object sender, MouseEventArgs e)
        {
            picLogo.Location = new Point(picLogo.Location.X + 2, picLogo.Location.Y + 2);
        }

        private void picLogo_MouseUp(object sender, MouseEventArgs e)
        {
            picLogo.Location = new Point(picLogo.Location.X - 2, picLogo.Location.Y - 2);
        }

        private void picLogo_MouseClick(object sender, MouseEventArgs e)
        {
            picLogo.Visible = false;
            pnlDetails.Location = new Point(pnlDetails.Left, this.Height);
            pnlDetails.Visible = true;
            //lblSlogan.Visible = false;
            //lblSubSlogan.Visible = false;

            ThreadManager.CreateThread(link => ScoreAnimation(link)).StartThread();
        }

        private void ScoreAnimation(ThreadLink link)
        {
            link.RegisterName();

            double progress = 0;
            int startYScore = 600;
            int endYScore = 241;

            int startYSlogan = 507;
            int endYSlogan = startYSlogan + (startYScore - endYScore);

            int startYSubSlogan = 555;
            int endYSubSlogan = startYSubSlogan + (startYScore - endYScore);

            int startYLogo = 301;
            int endYLogo = startYLogo + (startYScore - endYScore);

            int animDuration = 1000;
            int fps = 30;

            do
            {
                progress += 1f / fps;
                progress = Math.Min(1, progress);
                this.InvokeAuto(() =>
                {
                    Font f = new Font(lblScore.Font.Name, (float)(172 - (172 - 124) * Math.Max(0, (progress - 0.5) * 2)));
                    lblScore.Font = f;
                    pnlDetails.Location = new Point(pnlDetails.Left, (int)(startYScore - progress * (startYScore - endYScore)));
                    lblSlogan.Location = new Point(lblSlogan.Left, (int)(startYSlogan - progress * (startYSlogan - endYSlogan)));
                    lblSubSlogan.Location = new Point(lblSubSlogan.Left, (int)(startYSubSlogan - progress * (startYSubSlogan - endYSubSlogan)));
                });
                Thread.Sleep(animDuration / fps);
            } while (progress < 1);
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/Buzzer.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GoBot.Communications.CAN;

namespace GoBot.Devices
{
    public class Buzzer
    {
        private iCanSpeakable _communication;

        public Buzzer(iCanSpeakable comm)
        {
            _communication = comm;
        }

        public void Buzz(int freqHz, int durationMs)
        {
            _communication.SendFrame(CanFrameFactory.BuildBeep(CanBoard.CanAlim, freqHz, durationMs));
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/RobotPositionables.cs
﻿using System;
using GoBot.Actionneurs;

namespace GoBot
{
    public partial class Config
    {
        public ServoFlagLeft ServoFlagLeft { get; set; } = new ServoFlagLeft();
        public ServoFlagRight ServoFlagRight { get; set; } = new ServoFlagRight();

        public ServoPushArmRight ServoPushArmRight { get; set; } = new ServoPushArmRight();
        public ServoPushArmLeft ServoPushArmLeft { get; set; } = new ServoPushArmLeft();

        public ServoGrabberRight ServoGrabberRight { get; set; } = new ServoGrabberRight();
        public ServoGrabberLeft ServoGrabberLeft { get; set; } = new ServoGrabberLeft();

        public ServoFingerRight ServoFingerRight { get; set; } = new ServoFingerRight();
        public ServoFingerLeft ServoFingerLeft { get; set; } = new ServoFingerLeft();

        public ServoLockerRight ServoLockerRight { get; set; } = new ServoLockerRight();
        public ServoLockerLeft ServoLockerLeft { get; set; } = new ServoLockerLeft();

        public MotorElevatorRight MotorElevatorRight { get; set; } = new MotorElevatorRight();
        public MotorElevatorLeft MotorElevatorLeft { get; set; } = new MotorElevatorLeft();

        public ServoClamp1 ServoClamp1 { get; set; } = new ServoClamp1();
        public ServoClamp2 ServoClamp2 { get; set; } = new ServoClamp2();
        public ServoClamp3 ServoClamp3 { get; set; } = new ServoClamp3();
        public ServoClamp4 ServoClamp4 { get; set; } = new ServoClamp4();
        public ServoClamp5 ServoClamp5 { get; set; } = new ServoClamp5();

        public ServoLifter ServoLifter { get; set; } = new ServoLifter();
        public ServoTilter ServoTilter { get; set; } = new ServoTilter();

        // Peit robot

        public SmallElevatorLeft SmallElevatorLeft { get; set; } = new SmallElevatorLeft();
        public SmallElevatorRight SmallElevatorRight { get; set; } = new SmallElevatorRight();
        public SmallElevatorBack SmallElevatorBack { get; set; } = new SmallElevatorBack();
        public ArmLeft ArmLeft { get; set; } = new ArmLeft();
        public ArmRight ArmRight { get; set; } = new ArmRight();
        public Selector Selector { get; set; } = new Selector();
        public Retractor Retractor { get; set; } = new Retractor();
    }
}

namespace GoBot.Actionneurs
{
    #region PositionableServo

    public abstract class ServoFlag : PositionableServo
    {
        public int PositionOpen { get; set; }
        public int PositionClose { get; set; }
    }

    public class ServoFlagLeft : ServoFlag
    {
        public override ServomoteurID ID => ServomoteurID.FlagLeft;
    }

    public class ServoFlagRight : ServoFlag
    {
        public override ServomoteurID ID => ServomoteurID.FlagRight;
    }

    public abstract class ServoPushArm : PositionableServo
    {
        public int PositionOpen { get; set; }
        public int PositionClose { get; set; }
        public int PositionLight { get; set; }
    }

    public class ServoPushArmLeft : ServoPushArm
    {
        public override ServomoteurID ID => ServomoteurID.PushArmLeft;
    }

    public class ServoPushArmRight : ServoPushArm
    {
        public override ServomoteurID ID => ServomoteurID.PushArmRight;
    }

    public abstract class ServoFinger : PositionableServo
    {
        public int PositionHide { get; set; }
        public int PositionKeep { get; set; }
        public int PositionGrab { get; set; }
    }

    public class ServoFingerLeft : ServoFinger
    {
        public override ServomoteurID ID => ServomoteurID.FingerLeft;
    }

    public class ServoFingerRight : ServoFinger
    {
        public override ServomoteurID ID => ServomoteurID.FingerRight;
    }

    public abstract class ServoGrabber : PositionableServo
    {
        public int PositionOpen { get; set; }
        public int PositionClose { get; set; }
        public int PositionHide { get; set; }
        public int PositionRelease { get; set; }
    }

    public class ServoGrabberRight : ServoGrabber
    {
        public override ServomoteurID ID => ServomoteurID.GrabberRight;
    }

    public class ServoGrabberLeft : ServoGrabber
    {
        public override ServomoteurID ID => ServomoteurID.GrabberLeft;
    }

    public abstract class ServoLocker : PositionableServo
    {
        public int PositionEngage { get; set; }
        public int PositionDisengage { get; set; }
        public int PositionMaintain { get; set; }
    }

    public class ServoLockerRight : ServoLocker
    {
        public override ServomoteurID ID => ServomoteurID.LockerRight;
    }

    public class ServoLockerLeft : ServoLocker
    {
        public override ServomoteurID ID => ServomoteurID.LockerLeft;
    }

    public abstract class ServoClamp : PositionableServo
    {
        public int PositionOpen { get; set; }
        public int PositionMaintain { get; set; }
        public int PositionClose { get; set; }
        public int PositionStore { get; set; }
    }

    public class ServoClamp1 : ServoClamp
    {
        public override ServomoteurID ID => ServomoteurID.Clamp1;
    }

    public class ServoClamp2 : ServoClamp
    {
        public override ServomoteurID ID => ServomoteurID.Clamp2;
    }

    public class ServoClamp3 : ServoClamp
    {
        public override ServomoteurID ID => ServomoteurID.Clamp3;
    }

    public class ServoClamp4 : ServoClamp
    {
        public override ServomoteurID ID => ServomoteurID.Clamp4;
    }

    public class ServoClamp5 : ServoClamp
    {
        public override ServomoteurID ID => ServomoteurID.Clamp5;
    }

    public class ServoLifter : PositionableServo
    {
        public override ServomoteurID ID => ServomoteurID.Lifter;

        public int PositionStore { get; set; }
        public int PositionExtract { get; set; }
    }

    public class ServoTilter : PositionableServo
    {
        public override ServomoteurID ID => ServomoteurID.Tilter;

        public int PositionStore { get; set; }
        public int PositionPickup { get; set; }
        public int PositionExtract { get; set; }
        public int PositionDropoff { get; set; }
    }

    #endregion

    #region PositionableMotorSpeed

    #endregion

    #region PositionableMotorPosition

    public abstract class MotorElevator : PositionableMotorPosition
    {
        public int PositionFloor0 { get; set; }
        public int PositionFloor1 { get; set; }
        public int PositionFloor2 { get; set; }
        public int PositionFloor3 { get; set; }
    }

    public class MotorElevatorRight : MotorElevator
    {
        public override MotorID ID => MotorID.ElevatorRight;
    }

    public class MotorElevatorLeft : MotorElevator
    {
        public override MotorID ID => MotorID.ElevatorLeft;
    }

    #endregion

    // Petit robot

    public abstract class Arm : PositionableServo
    {
        public int PositionClose { get; set; }
        public int PositionOpen { get; set; }
    }

    public class ArmLeft : Arm
    {
        public override ServomoteurID ID => ServomoteurID.ArmLeft;
    }

    public class ArmRight : Arm
    {
        public override ServomoteurID ID => ServomoteurID.ArmRight;
    }

    public abstract class SmallElevator : PositionableServo
    {
        public int PositionBottom { get; set; }
        public int PositionMiddle { get; set; }
        public int PositionTop { get; set; }
        public int PositionGrab { get; set; }
    }
    public class SmallElevatorLeft : SmallElevator
    {
        public override ServomoteurID ID => ServomoteurID.ElevatorLeft;
    }
    public class SmallElevatorRight : SmallElevator
    {
        public override ServomoteurID ID => ServomoteurID.ElevatorRight;
    }
    public class SmallElevatorBack : SmallElevator
    {
        public override ServomoteurID ID => ServomoteurID.ElevatorBack;
    }
    public class Selector : PositionableServo
    {
        public override ServomoteurID ID => ServomoteurID.Selector;
        public int PositionLeft { get; set; }
        public int PositionMiddle { get; set; }
        public int PositionRight { get; set; }
    }
    public class Retractor : PositionableServo
    {
        public override ServomoteurID ID => ServomoteurID.Retractor;
        public int PositionEngage { get; set; }
        public int PositionDisengage { get; set; }
    }
}
<file_sep>/GoBot/Composants/GroupBoxPlus.cs
﻿using System;
using System.Windows.Forms;

namespace Composants
{
    public partial class GroupBoxPlus : GroupBox
    {
        private Button ButtonArrow { get; set; }
        private Timer TimerAnimation { get; set; }
        private int OriginalHeight { get; set; }
        private int ReducedHeight { get; set; }

        /// <summary>
        /// Vrai si le panel est actuellement ouvert
        /// </summary>
        public bool Deployed { get; private set; }
        
        public delegate void DeployedChangedDelegate(bool deployed);

        /// <summary>
        /// Se produit lorsque le déploiement du panel change
        /// </summary>
        public event DeployedChangedDelegate DeployedChanged;

        public GroupBoxPlus()
        {
            InitializeComponent();

            ButtonArrow = new Button();
            ButtonArrow.Visible = true;
            ButtonArrow.Image = Properties.Resources.ArrowUp;
            ButtonArrow.SetBounds(this.Width - 23 - 5, 10, 23, 23);
            ButtonArrow.Anchor = AnchorStyles.Right | AnchorStyles.Top;
            ButtonArrow.Click += new EventHandler(ButtonArrow_Click);
            Controls.Add(ButtonArrow);
            ReducedHeight = 37;
            OriginalHeight = 0;
            Deployed = true;

            this.SizeChanged += new EventHandler(GroupBoxPlus_SizeChanged);
            this.DoubleBuffered = true;
        }
        
        /// <summary>
        /// Ouvre ou ferme le panel
        /// </summary>
        /// <param name="open">Ouvre le panel si vrai, sinon le ferme</param>
        /// <param name="animation">Vrai si l'animation smooth doit être utilisée, faux si l'action doit être instantanée</param>
        public void Deploy(bool open = true, bool animation = false)
        {
            if (open)
                Open(animation);
            else
                Close(animation);
        }

        void GroupBoxPlus_SizeChanged(object sender, EventArgs e)
        {
            ButtonArrow.SetBounds(this.Width - ButtonArrow.Width - 5, 10, ButtonArrow.Width, ButtonArrow.Height);
        }

        void ButtonArrow_Click(object sender, EventArgs e)
        {
            Deploy(!Deployed, true);
        }

        private void Open(bool animation = false)
        {
            Deployed = true;

            if (OriginalHeight == 0)
                return;

            foreach (Control c in Controls)
                c.Visible = true;

            ButtonArrow.Image = Properties.Resources.ArrowUp;

            Deployed = true;

            if (animation)
            {
                TimerAnimation = new Timer();
                TimerAnimation.Interval = 20;
                TimerAnimation.Tick += new EventHandler(TimerAnimation_Tick);
                TimerAnimation.Start();
            }
            else
            {
                this.Height = OriginalHeight;
            }
            
            DeployedChanged?.Invoke(true);
        }

        private void Close(bool animation = false)
        {
            Deployed = false;

            if(OriginalHeight == 0)
                OriginalHeight = this.Height;

            ButtonArrow.Image = Properties.Resources.ArrowBottom;

            Deployed = false;

            if (animation)
            {
                TimerAnimation = new Timer();
                TimerAnimation.Interval = 20;
                TimerAnimation.Tick += new EventHandler(TimerAnimation_Tick);
                TimerAnimation.Start();
            }
            else
            {
                this.Height = ReducedHeight;
                foreach (Control c in Controls)
                    c.Visible = false;

                ButtonArrow.Visible = true;
            }
            
            DeployedChanged?.Invoke(false);
        }

        void TimerAnimation_Tick(object sender, EventArgs e)
        {
            if (Deployed)
            {
                if (OriginalHeight - this.Height == 1)
                {
                    this.Height = OriginalHeight;
                    TimerAnimation.Stop();
                }
                else
                {
                    this.Height += (int)Math.Ceiling((OriginalHeight - this.Height) / 5.0);
                }
            }
            else
            {
                if (this.Height - ReducedHeight == 1)
                {
                    this.Height = ReducedHeight;
                    TimerAnimation.Stop();
                    foreach (Control c in Controls)
                        c.Visible = false;

                    ButtonArrow.Visible = true;
                    ButtonArrow.Focus();
                }
                else
                {
                    this.Height -= (int)Math.Ceiling((this.Height - ReducedHeight) / 5.0);
                }
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/RealPointWithRealPoint.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class RealPointWithRealPoint
    {
        public static bool Contains(RealPoint containingPoint, RealPoint containedPoint)
        {
            return containingPoint == containedPoint;
        }

        public static bool Cross(RealPoint point1, RealPoint point2)
        {
            return point1 == point2;
        }

        public static double Distance(RealPoint point1, RealPoint point2)
        {
            return Maths.Hypothenuse((point2.X - point1.X), (point2.Y - point1.Y)); ;
        }

        public static List<RealPoint> GetCrossingPoints(RealPoint point1, RealPoint point2)
        {
            List<RealPoint> output = new List<RealPoint>();

            if (point1 == point2)
                output.Add(new RealPoint(point1));

            return output;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/IShape.cs
﻿using System.Collections.Generic;
using System.Drawing;

namespace Geometry.Shapes
{
    public interface IShape
    {
        /// <summary>
        /// Teste si la forme courante croise la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Vrai si la forme courante croise la forme donnée</returns>
        bool Cross(IShape shape);

        /// <summary>
        /// Teste si la forme courante contient la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Vrai si la forme courante contient la forme donnée</returns>
        bool Contains(IShape shape);

        /// <summary>
        /// Retourne la distance minimum entre la forme courante et la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Distance entre la forme actuelle et la forme testée. Si les deux formes se croisent la distance est de 0</returns>
        double Distance(IShape shape);

        /// <summary>
        /// Retourne les points de croisement entre la forme courante et la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Liste des points de croisements entre la forme courante et la forme donnée</returns>
        List<RealPoint> GetCrossingPoints(IShape shape);

        /// <summary>
        /// Obtient la surface de la forme
        /// </summary>
        double Surface { get; }

        /// <summary>
        /// Obtient le barycentre de la forme
        /// </summary>
        RealPoint Barycenter { get; }

        /// <summary>
        /// Peint la forme sur le Graphic donné
        /// </summary>
        /// <param name="g">Graphic sur lequel peindre</param>
        /// <param name="outline">Pen utilisé pour peindre le contour de la forme (null pour aucun contour)</param>
        /// <param name="fillColor">Brush pour de remplissag de la forme (null pour aucun remplissage)</param>
        /// <param name="scale">Echelle de conversion</param>
        void Paint(Graphics g, Pen outline, Brush fill, WorldScale scale);
    }

    public static class IShapeExtensions
    {
        /// <summary>
        /// Retourne une copie de la forme ayant subit une translation
        /// </summary>
        /// <param name="shape">Forme à translater</param>
        /// <param name="dx">Distance de translation en X</param>
        /// <param name="dy">Distance de translation en Y</param>
        /// <returns>Nouvelle forme ayant subit la translation</returns>
        public static IShape Translation(this IShape shape, double dx, double dy)
        {
            return ((IShapeModifiable<IShape>)shape).Translation(dx, dy);
        }

        /// <summary>
        /// Retourne une copie de la forme ayant subit une rotation
        /// </summary>
        /// <param name="shape">Forme à tourner</param>
        /// <param name="angle">Angle de rotation</param>
        /// <param name="rotationCenter">Centre de rotation. Si null alors le barycentre est utilisé.</param>
        /// <returns>Nouvelle forme ayant subit la rotation</returns>
        public static IShape Rotation(this IShape shape, AngleDelta angle, RealPoint rotationCenter = null)
        {
            return ((IShapeModifiable<IShape>)shape).Rotation(angle, rotationCenter);
        }

        public static void Paint(this IShape shape, Graphics g, Color outline, int outlineWidth, Color fill, WorldScale scale)
        {
            Pen p = new Pen(outline, outlineWidth);
            Brush b = new SolidBrush(fill);

            shape.Paint(g, p, b, scale);

            p.Dispose();
            b.Dispose();
        }
    }

    public interface IShapeModifiable<out T>
    {
        /// <summary>
        /// Retourne une copie de la forme ayant subit une translation
        /// </summary>
        /// <param name="dx">Distance de translation en X</param>
        /// <param name="dy">Distance de translation en Y</param>
        /// <returns>Nouvelle forme ayant subit la trabslation</returns>
        T Translation(double dx, double dy);

        /// <summary>
        /// Retourne une copie de la forme ayant subit une rotation
        /// </summary>
        /// <param name="angle">Angle de rotation</param>
        /// <param name="rotationCenter">Centre de rotation. Si null alors le barycentre est utilisé.</param>
        /// <returns>Nouvelle forme ayant subit la rotation</returns>
        T Rotation(AngleDelta angle, RealPoint rotationCenter = null);
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelSpeedConfig.cs
﻿using System;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class PanelSpeedConfig : UserControl
    {
        private bool _loaded;

        public PanelSpeedConfig()
        {
            InitializeComponent();
            _loaded = false;
        }

        private void PanelSpeedConfig_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                numSlowLineAcceleration.Value = Config.CurrentConfig.ConfigLent.LineAcceleration;
                numSlowLineDeceleration.Value = Config.CurrentConfig.ConfigLent.LineDeceleration;
                numSlowLineSpeed.Value = Config.CurrentConfig.ConfigLent.LineSpeed;
                numSlowPivotAcceleration.Value = Config.CurrentConfig.ConfigLent.PivotAcceleration;
                numSlowPivotDeceleration.Value = Config.CurrentConfig.ConfigLent.PivotDeceleration;
                numSlowPivotSpeed.Value = Config.CurrentConfig.ConfigLent.PivotSpeed;

                numFastLineAcceleration.Value = Config.CurrentConfig.ConfigRapide.LineAcceleration;
                numFastLineDeceleration.Value = Config.CurrentConfig.ConfigRapide.LineDeceleration;
                numFastLineSpeed.Value = Config.CurrentConfig.ConfigRapide.LineSpeed;
                numFastPivotAcceleration.Value = Config.CurrentConfig.ConfigRapide.PivotAcceleration;
                numFastPivotDeceleration.Value = Config.CurrentConfig.ConfigRapide.PivotDeceleration;
                numFastPivotSpeed.Value = Config.CurrentConfig.ConfigRapide.PivotSpeed;

                _loaded = true;
            }
        }

        private void num_ValueChanged(object sender, EventArgs e)
        {
            if (_loaded)
            {
                Config.CurrentConfig.ConfigRapide.SetParams(
                    (int)numFastLineSpeed.Value,
                    (int)numFastLineAcceleration.Value,
                    (int)numFastLineDeceleration.Value,
                    (int)numFastPivotSpeed.Value,
                    (int)numFastPivotAcceleration.Value,
                    (int)numFastPivotDeceleration.Value);

                Config.CurrentConfig.ConfigLent.SetParams(
                    (int)numSlowLineSpeed.Value,
                    (int)numSlowLineAcceleration.Value,
                    (int)numSlowLineDeceleration.Value,
                    (int)numSlowPivotSpeed.Value,
                    (int)numSlowPivotAcceleration.Value,
                    (int)numSlowPivotDeceleration.Value);

                Config.Save();
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/CircleWithCircle.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class CircleWithCircle
    {
        public static bool Contains(Circle circleContaining, Circle circleContained)
        {
            // Pour contenir un cercle il faut que son rayon + la distance entre les centres des deux cercles soit inférieure à notre rayon

            return circleContained.Radius + circleContained.Center.Distance(circleContaining.Center) < circleContaining.Radius;
        }

        public static double Distance(Circle circle1, Circle circle2)
        {
            // C'est la distance entre les deux centres - la somme des deux rayons

            return Math.Max(0, circle1.Center.Distance(circle2.Center) - circle2.Radius - circle1.Radius);
        }

        public static bool Cross(Circle circle1, Circle circle2)
        {
            // 2 cercles identiques se croisent
            // Pour croiser un cercle il suffit que son centre soit éloigné de notre centre de moins que la somme de nos 2 rayons
            // Et que les cercles ne se contiennent pas l'un l'autre

            bool output;

            if (circle1.Center == circle2.Center && Math.Abs(circle1.Radius - circle2.Radius) < RealPoint.PRECISION)
                output = true;
            else if (circle1.Center.Distance(circle2.Center) <= circle1.Radius + circle2.Radius)
                output = (!circle2.Contains(circle1)) && (!circle1.Contains(circle2));
            else
                output = false;

            return output;
        }
        
        public static List<RealPoint> GetCrossingPoints(Circle circle1, Circle circle2)
        {
            // Résolution du système d'équation à deux inconnues des deux équations de cercle

            List<RealPoint> output = new List<RealPoint>();

            if (circle1.Radius == 0 && circle2.Radius == 0 && circle1.Center == circle2.Center)
            {
                // Cas particulier où les deux cercles ont un rayon de 0 et sont au même endroit : le croisement c'est ce point.
                output.Add(new RealPoint(circle1.Center));
            }
            else if (circle1.Center == circle2.Center)
            {
                // Cas particulier où les deux cercles ont un rayon > 0 et sont au même endroit : on ne calcule pas les points de croisement, même s'ils se superposent (une autre idée est la bienvenue ?)
            }
            else
            {
                bool aligned = Math.Abs(circle1.Center.Y - circle2.Center.Y) < RealPoint.PRECISION;

                if (aligned)// Cercles non alignés horizontalement (on pivote pour les calculs, sinon division par 0)
                    circle1 = circle1.Rotation(90, circle2.Center);

                RealPoint oc1 = new RealPoint(circle1.Center), oc2 = new RealPoint(circle2.Center);
                double b = circle1.Radius, c = circle2.Radius;

                double a = (-(Math.Pow(oc1.X, 2)) - (Math.Pow(oc1.Y, 2)) + Math.Pow(oc2.X, 2) + Math.Pow(oc2.Y, 2) + Math.Pow(b, 2) - Math.Pow(c, 2)) / (2 * (oc2.Y - oc1.Y));
                double d = ((oc2.X - oc1.X) / (oc2.Y - oc1.Y));

                double A = Math.Pow(d, 2) + 1;
                double B = -2 * oc1.X + 2 * oc1.Y * d - 2 * a * d;
                double C = Math.Pow(oc1.X, 2) + Math.Pow(oc1.Y, 2) - 2 * oc1.Y * a + Math.Pow(a, 2) - Math.Pow(b, 2);

                double delta = Math.Pow(B, 2) - 4 * A * C;

                if (delta >= 0)
                {
                    double x1 = (-B + Math.Sqrt(delta)) / (2 * A);
                    double y1 = a - x1 * d;
                    output.Add(new RealPoint(x1, y1));

                    if (delta > 0)
                    {
                        double x2 = (-B - Math.Sqrt(delta)) / (2 * A);
                        double y2 = a - x2 * d;
                        output.Add(new RealPoint(x2, y2));
                    }
                }

                if (aligned)
                    output = output.ConvertAll(p => p.Rotation(-90, circle2.Center));
            }

            return output;
        }
    }
}
<file_sep>/GoBot/Composants/LabelPlus.cs
﻿using System;
using System.Windows.Forms;
using System.Drawing;

namespace Composants
{
    public partial class LabelPlus : Label
    {        
        
        // Fonctionnalités supplémentaires :
        //
        //      - Afficher un texte pendant une certaine durée
        //              Fonction TextDuring(texte, durée)

        private Timer TimerDisplay { get; set; }
        private Color PreviousColor { get; set; }
        private String PreviousText { get; set; }

        public LabelPlus()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Permet d'afficher un texte donné dans une couleur donnée pendant un temps donné. Après quoi le texte et la couelru d'origine seront rétablis.
        /// </summary>
        /// <param name="text">Texte à afficher momentanément</param>
        /// <param name="during">Durée d'affichage du texte (ms)</param>
        /// <param name="color">Couleur du texte affiché momentanément</param>
        public void ShowText(String text, int during = 2000, Color? color = null)
        {
            PreviousColor = ForeColor;
            PreviousText = Text;

            if (color.HasValue)
                ForeColor = color.Value;

            Text = text;

            if (TimerDisplay != null)
            {
                if (TimerDisplay.Enabled)
                {
                    TimerDisplay.Stop();
                }
                TimerDisplay.Dispose();
            }

            TimerDisplay = new Timer();
            TimerDisplay.Interval = during;
            TimerDisplay.Tick += new EventHandler(TimerDisplay_Tick);
            TimerDisplay.Enabled = true;
            TimerDisplay.Start();
        }

        void TimerDisplay_Tick(object sender, EventArgs e)
        {
            ForeColor = PreviousColor;
            Text = PreviousText;
            TimerDisplay.Stop();
            Text = "";
        }
    }
}
<file_sep>/GoBot/Geometry/WorldScale.cs
﻿using System.Drawing;

using Geometry.Shapes;

namespace Geometry
{
    /// <summary>
    /// Classe permettant de convertir des coordonnées réelles (sur l'aire de jeu) en coordonnées écran (sur l'image de l'aire de jeu)
    /// </summary>
    public class WorldScale
    {
        public double Factor { get; protected set; }
        public int OffsetX { get; protected set; }
        public int OffsetY { get; protected set; }

        /// <summary>
        /// Crée un PaintScale à partir des mesures fournies
        /// </summary>
        /// <param name="mmPerPixel">Nombre de mm par pixels</param>
        /// <param name="offsetX">Position en pixels de l'abscisse 0</param>
        /// <param name="offsetY">Position en pixels de l'ordonnéee 0</param>
        public WorldScale(double mmPerPixel, int offsetX, int offsetY)
        {
            Factor = mmPerPixel;
            OffsetX = offsetX;
            OffsetY = offsetY;
        }

        public WorldScale(WorldScale other)
        {
            Factor = other.Factor;
            OffsetX = other.OffsetX;
            OffsetY = other.OffsetY;
        }

        /// <summary>
        /// Retourne une échelle par défaut (par de transformation)
        /// </summary>
        /// <returns></returns>
        public static WorldScale Default()
        {
            return new WorldScale(1, 0, 0);
        }

        // Ecran vers réel

        public int ScreenToRealDistance(double value)
        {
            return (int)(value * Factor);
        }

        public RealPoint ScreenToRealPosition(Point value)
        {
            return new RealPoint(ScreenToRealDistance(value.X - OffsetX), ScreenToRealDistance(value.Y - OffsetY));
        }

        public SizeF ScreenToRealSize(Size sz)
        {
            return new SizeF(ScreenToRealDistance(sz.Width), ScreenToRealDistance(sz.Height));
        }

        public RectangleF ScreenToRealRect(Rectangle rect)
        {
            return new RectangleF(ScreenToRealPosition(rect.Location), ScreenToRealSize(rect.Size));
        }

        // Réel vers écran

        public int RealToScreenDistance(double value)
        {
            return (int)System.Math.Round(value / Factor, 0);
        }

        public Point RealToScreenPosition(RealPoint value)
        {
            return new Point(RealToScreenDistance(value.X) + OffsetX, RealToScreenDistance(value.Y) + OffsetY);
        }

        public Size RealToScreenSize(SizeF sz)
        {
            return new Size(RealToScreenDistance(sz.Width), RealToScreenDistance(sz.Height));
        }

        public Rectangle RealToScreenRect(RectangleF rect)
        {
            return new Rectangle(RealToScreenPosition(rect.Location), RealToScreenSize(rect.Size));
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/PolygonWithLine.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class PolygonWithLine
    {
        public static bool Contains(Polygon containingPolygon, Line containedLine)
        {
            // Un polygon fini ne peut pas contenir une droite infinie.
            return false;
        }

        public static bool Cross(Polygon polygon, Line line)
        {
            return LineWithPolygon.Cross(line, polygon);
        }

        public static double Distance(Polygon polygon, Line line)
        {
            return LineWithPolygon.Distance(line, polygon);
        }

        public static List<RealPoint> GetCrossingPoints(Polygon polygon, Line line)
        {
            return LineWithPolygon.GetCrossingPoints(line, polygon);
        }
    }
}
<file_sep>/GoBot/GoBot/Logs/ILog.cs
﻿using System;

namespace GoBot.Logs
{
    public interface ILog
    {
        /// <summary>
        /// Ecrit un message dans le log
        /// </summary>
        /// <param name="message"></param>
        void Write(String message);

        /// <summary>
        /// Ferme le log
        /// </summary>
        void Close();
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageLogsEvents.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PageLogsEvents : UserControl
    {
        private Dictionary<IDRobot, Color> couleurRobot;
        private Dictionary<TypeLog, Color> couleurTypeLog;

        private Dictionary<IDRobot, bool> dicRobotsAutorises;
        private Dictionary<TypeLog, bool> dicTypesAutorises;

        private DateTime dateDebut;
        private DateTime datePrec;
        private DateTime datePrecAff;
        private System.Windows.Forms.Timer timerAffichage;
        private bool affichageTempsReel = false;
        private int compteur = 0;
        private EventsReplay Replay { get; set; }

        public PageLogsEvents()
        {
            InitializeComponent();

            dataGridViewLog.Columns.Add("Id", "Id");
            dataGridViewLog.Columns[0].Width = 50;
            dataGridViewLog.Columns.Add("Heure", "Heure");
            dataGridViewLog.Columns[1].Width = 80;
            dataGridViewLog.Columns.Add("Robot", "Robot");
            dataGridViewLog.Columns[2].Width = 80;
            dataGridViewLog.Columns.Add("Type", "Type");
            dataGridViewLog.Columns[3].Width = 80;
            dataGridViewLog.Columns.Add("Message", "Message");
            dataGridViewLog.Columns[4].Width = 520;

            couleurRobot = new Dictionary<IDRobot, Color>();
            couleurRobot.Add(IDRobot.GrosRobot, Color.LightBlue);

            couleurTypeLog = new Dictionary<TypeLog, Color>();
            couleurTypeLog.Add(TypeLog.Action, Color.Lavender);
            couleurTypeLog.Add(TypeLog.PathFinding, Color.Khaki);
            couleurTypeLog.Add(TypeLog.Strat, Color.IndianRed);

            dicRobotsAutorises = new Dictionary<IDRobot, bool>();
            dicTypesAutorises = new Dictionary<TypeLog, bool>();

            // L'ajout de champs déclenche le SetCheck event qui ajoute les éléments automatiquement dans le dictionnaire
            foreach (TypeLog type in Enum.GetValues(typeof(TypeLog)))
            {
                checkedListBoxEvents.Items.Add(type.ToString(), true);
            }
            foreach (IDRobot robot in Enum.GetValues(typeof(IDRobot)))
            {
                checkedListBoxRobots.Items.Add(robot.ToString(), true);
            }
            Replay = new EventsReplay();
        }

        private void btnCharger_Click(object sender, EventArgs e)
        {
            OpenFileDialog open = new OpenFileDialog();
            open.Filter = "Fichiers replay events (*.elog)|*.elog";
            open.Multiselect = true;
            if (open.ShowDialog() == DialogResult.OK)
            {

                foreach(String fichier in open.FileNames)
                {
                    ChargerLog(fichier);
                }
                Replay.Trier();
                Afficher();
            }
        }

        public void Clear()
        {
            Replay = new EventsReplay();
        }

        public void ChargerLog(String fichier)
        {
            EventsReplay replayTemp = new EventsReplay();
            replayTemp.Charger(fichier);

            foreach (HistoLigne t in replayTemp.Events)
                Replay.Events.Add(t);
        }

        public void Afficher()
        {
            try
            {
                dataGridViewLog.Rows.Clear();

                if (Replay.Events.Count > 0)
                {

                    dateDebut = Replay.Events[0].Heure;
                    datePrec = Replay.Events[0].Heure;
                    datePrecAff = Replay.Events[0].Heure;
                    compteur = 0;

                    for (int iTrame = 0; iTrame < Replay.Events.Count; iTrame++)
                        AfficherEvent(Replay.Events[iTrame]);
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Impossible de décoder tous les events contenus dans ce fichier.", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            Afficher();
        }

        private void checkedListBoxRobots_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!Execution.DesignMode)
            {
                String robotString = (String)checkedListBoxRobots.Items[e.Index];
                IDRobot robot = (IDRobot)Enum.Parse(typeof(IDRobot), robotString);

                dicRobotsAutorises[robot] = (e.NewValue == CheckState.Checked);
            }
        }

        private void checkedListBoxEvents_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!Execution.DesignMode)
            {
                String typeString = (String)checkedListBoxEvents.Items[e.Index];
                TypeLog type = (TypeLog)Enum.Parse(typeof(TypeLog), typeString);

                dicTypesAutorises[type] = (e.NewValue == CheckState.Checked);
            }
        }

        private void dataGridViewLog_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right && e.RowIndex > 0 && e.ColumnIndex > 0)
                dataGridViewLog.CurrentCell = dataGridViewLog.Rows[e.RowIndex].Cells[e.ColumnIndex];
        }

        private void btnAfficher_Click(object sender, EventArgs e)
        {
            if (!affichageTempsReel)
            {
                Replay = new EventsReplay();
                timerAffichage = new System.Windows.Forms.Timer();
                timerAffichage.Interval = 1000;
                timerAffichage.Tick += timerAffichage_Tick;
                timerAffichage.Start();

                Robots.MainRobot.Historique.NouveauLog += Replay.AjouterEvent;
                
                btnCharger.Enabled = false;
                btnAfficher.Text = "Arrêter l'affichage";
                btnAfficher.Image = Properties.Resources.Pause16;
                affichageTempsReel = true;
            }
            else
            {
                timerAffichage.Stop();

                Robots.MainRobot.Historique.NouveauLog -= Replay.AjouterEvent;

                btnCharger.Enabled = true;
                btnAfficher.Text = "Afficher temps réel";
                btnAfficher.Image = Properties.Resources.Play16;
                affichageTempsReel = false;
            }
        }

        void timerAffichage_Tick(object sender, EventArgs e)
        {
            int nbEvents = Replay.Events.Count;
            for (int i = compteur; i < nbEvents; i++)
                AfficherEvent(Replay.Events[i]);

            if(boxScroll.Checked && dataGridViewLog.Rows.Count > 10)
                dataGridViewLog.FirstDisplayedScrollingRowIndex = dataGridViewLog.RowCount - 1;
        }

        private void AfficherEvent(HistoLigne eventReplay)
        {
            String heure = "";

            if (rdoHeure.Checked)
                heure = eventReplay.Heure.ToString("hh:mm:ss:fff");
            if (rdoTempsDebut.Checked)
                heure = (eventReplay.Heure - dateDebut).ToString(@"hh\:mm\:ss\:fff");
            if (rdoTempsPrec.Checked)
                heure = (eventReplay.Heure - datePrec).ToString(@"hh\:mm\:ss\:fff");
            if (rdoTempsPrecAff.Checked)
                heure = (eventReplay.Heure - datePrecAff).ToString(@"hh\:mm\:ss\:fff");

            if (dicRobotsAutorises[eventReplay.Robot] && dicTypesAutorises[eventReplay.Type])
            {
                dataGridViewLog.Rows.Add(compteur, heure, eventReplay.Robot.ToString(), eventReplay.Type.ToString(), eventReplay.Message);
                datePrecAff = eventReplay.Heure;

                if (rdoRobot.Checked)
                    dataGridViewLog.Rows[dataGridViewLog.Rows.Count - 1].DefaultCellStyle.BackColor = couleurRobot[eventReplay.Robot];
                else if (rdoType.Checked)
                    dataGridViewLog.Rows[dataGridViewLog.Rows.Count - 1].DefaultCellStyle.BackColor = couleurTypeLog[eventReplay.Type];
            }

            compteur++;

            datePrec = eventReplay.Heure;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/PolygonRectangle.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;

namespace Geometry.Shapes
{
    public class PolygonRectangle : Polygon
    {
        /// <summary>
        /// Construit un rectangle à partir du point en haut à gauche, de la largeur et de la hauteur
        /// </summary>
        /// <param name="topLeft">Point en haut à gauche du rectangle</param>
        /// <param name="width">Largeur du rectangle</param>
        /// <param name="heigth">Hauteur du rectangle</param>
        public PolygonRectangle(RealPoint topLeft, double width, double heigth) : base()
        {
            List<Segment> rectSides = new List<Segment>();

            if (topLeft == null)
                throw new ArgumentOutOfRangeException();

            topLeft = new RealPoint(topLeft);

            if (width < 0)
            {
                topLeft.X += width;
                width = -width;
            }
            if(heigth < 0)
            {
                topLeft.Y += heigth;
                heigth = -heigth;
            }

            List<RealPoint> points = new List<RealPoint>
            {
                new RealPoint(topLeft.X, topLeft.Y),
                new RealPoint(topLeft.X + width, topLeft.Y),
                new RealPoint(topLeft.X + width, topLeft.Y + heigth),
                new RealPoint(topLeft.X, topLeft.Y + heigth)
            };

            for (int i = 1; i < points.Count; i++)
                rectSides.Add(new Segment(points[i - 1], points[i]));

            rectSides.Add(new Segment(points[points.Count - 1], points[0]));

            BuildPolygon(rectSides, false);
        }

        public PolygonRectangle(PolygonRectangle other) : base(other)
        {

        }

        public PolygonRectangle(RectangleF other) : this(new RealPoint(other.Left, other.Top), other.Width, other.Height)
        {

        }

        public override string ToString()
        {
            return _sides[0].StartPoint.ToString() + "; " +
                "W = " + (_sides[1].StartPoint.X - _sides[0].StartPoint.X).ToString("0.00") + "; " +
                "H = " + (_sides[3].StartPoint.Y - _sides[0].StartPoint.Y).ToString("0.00");
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/CAN/CanSubConnection.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Communications.CAN
{
    class CanSubConnection : Connection
    {
        private CanConnection _baseConnection;
        private CanBoard _board;

        private String _name;

        public CanSubConnection(CanConnection baseConnection, CanBoard board)
        {
            _baseConnection = baseConnection;
            _board = board;
            ConnectionChecker = new ConnectionChecker(this, 500);
            ConnectionChecker.SendConnectionTest += ConnectionChecker_SendConnectionTest;
            _baseConnection.FrameReceived += _baseConnection_FrameReceived;

            _name = board.ToString();
        }

        public override string Name { get => _name; set => _name = value; }

        private void _baseConnection_FrameReceived(Frame frame)
        {
            if ((CanBoard) frame[1] == _board)
                ConnectionChecker.NotifyAlive();
        }

        private void ConnectionChecker_SendConnectionTest(Connection sender)
        {
            _baseConnection.SendMessage(CanFrameFactory.BuildTestConnection(_board));
        }

        public override void Close()
        {
        }

        public override bool SendMessage(Frame message)
        {
            return _baseConnection.SendMessage(new Frame(new byte[] { 0, (byte)_board }.Concat(message.ToBytes())));
        }

        public override void StartReception()
        {

        }
    }
}
<file_sep>/GoBot/GoBot/Robots/RobotReel.cs
﻿using GoBot.Actions;
using Geometry;
using Geometry.Shapes;
using GoBot.Communications;
using GoBot.Devices;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Threading;
using GoBot.Threading;
using GoBot.Communications.UDP;
using GoBot.BoardContext;
using GoBot.Communications.CAN;
using GoBot.Strategies;
using GoBot.Actionneurs;
using GoBot.GameElements;

namespace GoBot
{
    class RobotReel : Robot
    {
        private Dictionary<SensorOnOffID, Semaphore> _lockSensorOnOff;
        private Dictionary<SensorColorID, Semaphore> _lockSensorColor;
        private Dictionary<UdpFrameFunction, Semaphore> _lockFrame;
        private Dictionary<MotorID, Semaphore> _lockMotor;

        private Dictionary<SensorColorID, Board> _boardSensorColor;
        private Dictionary<SensorOnOffID, Board> _boardSensorOnOff;
        private Dictionary<ActuatorOnOffID, Board> _boardActuatorOnOff;
        private Dictionary<MotorID, Board> _boardMotor;

        private Connection _asserConnection;
        private bool _positionReceived;

        private List<double> _lastRecMoveLoad, _lastPwmLeft, _lastPwmRight;
        List<int>[] _lastPidTest;
        private string _lastLidarMeasure;
        private Position _lastLidarPosition;

        public override Position Position { get; protected set; }

        public RobotReel(IDRobot id, Board asserBoard) : base(id, "Robot")
        {
            AsserEnable = true;
            AsservBoard = asserBoard;

            _asserConnection = Connections.UDPBoardConnection[AsservBoard];
            _positionReceived = false;

            _lockFrame = new Dictionary<UdpFrameFunction, Semaphore>();
            foreach (UdpFrameFunction o in Enum.GetValues(typeof(UdpFrameFunction)))
                _lockFrame.Add(o, new Semaphore(0, int.MaxValue));

            _lockMotor = new Dictionary<MotorID, Semaphore>();
            foreach (MotorID o in Enum.GetValues(typeof(MotorID)))
                _lockMotor.Add(o, new Semaphore(0, int.MaxValue));

            _lockSensorOnOff = new Dictionary<SensorOnOffID, Semaphore>();
            foreach (SensorOnOffID o in Enum.GetValues(typeof(SensorOnOffID)))
                _lockSensorOnOff.Add(o, new Semaphore(0, int.MaxValue));

            _lockSensorColor = new Dictionary<SensorColorID, Semaphore>();
            foreach (SensorColorID o in Enum.GetValues(typeof(SensorColorID)))
                _lockSensorColor.Add(o, new Semaphore(0, int.MaxValue));

            _boardSensorColor = new Dictionary<SensorColorID, Board>();
            _boardSensorColor.Add(SensorColorID.BuoyRight, Board.RecMove);
            _boardSensorColor.Add(SensorColorID.BuoyLeft, Board.RecIO);

            _boardSensorOnOff = new Dictionary<SensorOnOffID, Board>();
            _boardSensorOnOff.Add(SensorOnOffID.StartTrigger, Board.RecMove);
            _boardSensorOnOff.Add(SensorOnOffID.PressureSensorLeftBack, Board.RecMove);
            _boardSensorOnOff.Add(SensorOnOffID.PressureSensorLeftFront, Board.RecMove);
            _boardSensorOnOff.Add(SensorOnOffID.PressureSensorRightBack, Board.RecIO);
            _boardSensorOnOff.Add(SensorOnOffID.PressureSensorRightFront, Board.RecIO);
            _boardSensorOnOff.Add(SensorOnOffID.PresenceBuoyRight, Board.RecIO);

            _boardActuatorOnOff = new Dictionary<ActuatorOnOffID, Board>();
            _boardActuatorOnOff.Add(ActuatorOnOffID.PowerSensorColorBuoyRight, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.MakeVacuumLeftBack, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.MakeVacuumLeftFront, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.OpenVacuumLeftBack, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.OpenVacuumLeftFront, Board.RecIO);
            _boardActuatorOnOff.Add(ActuatorOnOffID.PowerSensorColorBuoyLeft, Board.RecIO);
            _boardActuatorOnOff.Add(ActuatorOnOffID.MakeVacuumRightBack, Board.RecIO);
            _boardActuatorOnOff.Add(ActuatorOnOffID.MakeVacuumRightFront, Board.RecIO);
            _boardActuatorOnOff.Add(ActuatorOnOffID.OpenVacuumRightBack, Board.RecIO);
            _boardActuatorOnOff.Add(ActuatorOnOffID.OpenVacuumRightFront, Board.RecMove);

            // Petit robot
            _boardActuatorOnOff.Add(ActuatorOnOffID.OpenVacuumLeft, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.OpenVacuumRight, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.OpenVacuumBack, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.MakeVacuumLeft, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.MakeVacuumRight, Board.RecMove);
            _boardActuatorOnOff.Add(ActuatorOnOffID.MakeVacuumBack, Board.RecMove);

            _boardSensorOnOff.Add(SensorOnOffID.PressureSensorLeft, Board.RecMove);
            _boardSensorOnOff.Add(SensorOnOffID.PressureSensorRight, Board.RecMove);
            _boardSensorOnOff.Add(SensorOnOffID.PressureSensorBack, Board.RecMove);

            _boardMotor = new Dictionary<MotorID, Board>();
            _boardMotor.Add(MotorID.ElevatorLeft, Board.RecIO);
            _boardMotor.Add(MotorID.ElevatorRight, Board.RecIO);

            SpeedConfig.ParamChange += SpeedConfig_ParamChange;
        }

        public override void DeInit()
        {

        }

        public override void ShowMessage(string message1, String message2)
        {
            Pepperl display = ((Pepperl)AllDevices.LidarAvoid);
            display.ShowMessage(message1, message2);
            display.SetFrequency(PepperlFreq.Hz50);
        }

        private void SpeedConfig_ParamChange(bool lineAccelChange, bool lineDecelChange, bool lineSpeedChange, bool pivotAccelChange, bool pivotDecelChange, bool pivotSpeedChange)
        {
            if (lineSpeedChange)
            {
                Frame frame = UdpFrameFactory.VitesseLigne(SpeedConfig.LineSpeed, this);
                _asserConnection.SendMessage(frame);
                Historique.AjouterAction(new ActionVitesseLigne(this, SpeedConfig.LineSpeed));
            }
            if (lineAccelChange || lineDecelChange)
            {
                Frame frame = UdpFrameFactory.AccelLigne(SpeedConfig.LineAcceleration, SpeedConfig.LineDeceleration, this);
                _asserConnection.SendMessage(frame);
                Historique.AjouterAction(new ActionAccelerationLigne(this, SpeedConfig.LineAcceleration, SpeedConfig.LineDeceleration));
            }
            if (pivotSpeedChange)
            {
                Frame frame = UdpFrameFactory.VitessePivot(SpeedConfig.PivotSpeed, this);
                _asserConnection.SendMessage(frame);
                Historique.AjouterAction(new ActionVitessePivot(this, SpeedConfig.PivotSpeed));
            }
            if (pivotAccelChange || pivotDecelChange)
            {
                Frame frame = UdpFrameFactory.AccelPivot(SpeedConfig.PivotAcceleration, this);
                _asserConnection.SendMessage(frame);
                Historique.AjouterAction(new ActionAccelerationPivot(this, SpeedConfig.PivotAcceleration, SpeedConfig.PivotDeceleration));
            }
        }

        private void SetStartTrigger(bool state)
        {
            if (!state && StartTriggerEnable)
            {
                StartTriggerEnable = false;

                if (GameBoard.Strategy == null)
                {
                    if (Config.CurrentConfig.IsMiniRobot)
                        GameBoard.Strategy = new StrategyMini();
                    else
                        GameBoard.Strategy = new StrategyMatch();
                }

                GameBoard.Strategy.ExecuteMatch();
            }
        }

        public override void Init()
        {
            Historique = new Historique(IDRobot);

            Connections.ConnectionCan.FrameReceived += ReceptionCanMessage;

            _asserConnection.FrameReceived += ReceptionUdpMessage;

            if (this == Robots.MainRobot)
                Connections.ConnectionIO.FrameReceived += ReceptionUdpMessage;

            //Connections.ConnectionsCan[CanBoard.CanAlim].FrameReceived += ReceptionCanMessage;

            if (this == Robots.MainRobot)
            {
                if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
                    Position = new Position(0, new RealPoint(240, 1000));
                else
                    Position = new Position(180, new RealPoint(3000 - 240, 1000));
            }
            else
            {
                if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
                    Position = new Position(0, new RealPoint(480, 1000));
                else
                    Position = new Position(180, new RealPoint(3000 - 480, 1000));
            }

            PositionTarget = null; //TODO2018 Init commun à la simu

            PositionsHistorical = new List<Position>();
            _asserConnection.SendMessage(UdpFrameFactory.DemandePositionContinue(50, this));
        }

        public override Color ReadSensorColor(SensorColorID sensor, bool wait = true)
        {
            if (wait) _lockSensorColor[sensor] = new Semaphore(0, int.MaxValue);

            Frame t = UdpFrameFactory.DemandeCapteurCouleur(_boardSensorColor[sensor], sensor);
            Connections.UDPBoardConnection[_boardSensorColor[sensor]].SendMessage(t);

            if (wait) _lockSensorColor[sensor].WaitOne(100);

            return SensorsColorValue[sensor];
        }

        public override bool ReadSensorOnOff(SensorOnOffID sensor, bool wait = true)
        {
            if (wait) _lockSensorOnOff[sensor] = new Semaphore(0, int.MaxValue);

            Frame t = UdpFrameFactory.DemandeCapteurOnOff(_boardSensorOnOff[sensor], sensor);
            Connections.UDPBoardConnection[_boardSensorOnOff[sensor]].SendMessage(t);

            if (wait) _lockSensorOnOff[sensor].WaitOne(100);

            return SensorsOnOffValue[sensor];
        }

        public void AsyncAsserEnable(ThreadLink link)
        {
            link.RegisterName();

            //TODO2020AllDevices.RecGoBot.Buzz(7000, 200);

            Thread.Sleep(500);
            TrajectoryFailed = true;
            Stop(StopMode.Abrupt);
            _lockFrame[UdpFrameFunction.FinDeplacement]?.Release();

            //TODO2020AllDevices.RecGoBot.Buzz(0, 200);
        }

        public void ReceptionCanMessage(Frame frame)
        {
            switch (CanFrameFactory.ExtractFunction(frame))
            {
                case CanFrameFunction.BatterieVoltage:
                    BatterieVoltage = (frame[3] * 256 + frame[4]) / 1000f;
                    BatterieIntensity = (frame[5] * 256 + frame[6]) / 1000f;
                    break;
            }
        }

        public void ReceptionUdpMessage(Frame frame)
        {
            // Analyser la trame reçue

            //Console.WriteLine(trameRecue.ToString());

            switch ((UdpFrameFunction)frame[1])
            {
                case UdpFrameFunction.RetourTension:
                    //BatterieVoltage = (frame[2] * 256 + frame[3]) / 100f;
                    break;
                case UdpFrameFunction.MoteurFin:
                    _lockMotor[(MotorID)frame[2]]?.Release();
                    break;
                case UdpFrameFunction.MoteurBlocage:    // Idem avec bip
                    _lockMotor[(MotorID)frame[2]]?.Release();
                    //TODO2020AllDevices.RecGoBot.Buzz("..");
                    break;
                case UdpFrameFunction.Blocage:
                    ThreadManager.CreateThread(AsyncAsserEnable).StartThread();
                    break;
                case UdpFrameFunction.FinDeplacement:
                case UdpFrameFunction.FinRecallage:        // Idem
                    Thread.Sleep(40); // TODO2018 ceci est une tempo ajoutée au pif de pwet parce qu'on avant envie alors voilà
                    IsInLineMove = false;
                    _lockFrame[UdpFrameFunction.FinDeplacement]?.Release();
                    break;
                case UdpFrameFunction.AsserRetourPositionXYTeta:
                    // Réception de la position mesurée par l'asservissement
                    try
                    {
                        double y = (double)((short)(frame[2] << 8 | frame[3]) / 10.0);
                        double x = (double)((short)(frame[4] << 8 | frame[5]) / 10.0);
                        double teta = (frame[6] << 8 | frame[7]) / 100.0 - 180;
                        teta = (-teta);
                        y = -y;
                        x = -x;

                        Position nouvellePosition = new Position(teta, new RealPoint(x, y));

                        if (Position.Coordinates.Distance(nouvellePosition.Coordinates) < 300 || !_positionReceived)
                        {
                            // On reçoit la position du robot
                            // On la prend si elle est pas très éloignée de la position précédente ou si je n'ai jamais reçu de position
                            Position = nouvellePosition;
                        }
                        else
                        {
                            // On pense avoir une meilleure position à lui redonner parce que la position reçue est loin de celle qu'on connait alors qu'on l'avait reçue du robot
                            SetAsservOffset(Position);
                        }

                        _positionReceived = true;

                        _lockFrame[UdpFrameFunction.AsserDemandePositionXYTeta]?.Release();

                        lock (PositionsHistorical)
                        {
                            PositionsHistorical.Add(new Position(teta, new RealPoint(x, y)));

                            while (PositionsHistorical.Count > 1200)
                                PositionsHistorical.RemoveAt(0);
                        }

                        OnPositionChanged(Position);
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("Erreur dans le retour de position asservissement.");
                    }
                    break;
                case UdpFrameFunction.AsserRetourPositionCodeurs:
                    int nbPositions = frame[2];

                    for (int i = 0; i < nbPositions; i++)
                    {
                        // TODO2018 peut mieux faire, décaller les bits
                        int gauche1 = frame[3 + i * 8];
                        int gauche2 = frame[4 + i * 8];
                        int gauche3 = frame[5 + i * 8];
                        int gauche4 = frame[6 + i * 8];

                        int codeurGauche = gauche1 * 256 * 256 * 256 + gauche2 * 256 * 256 + gauche3 * 256 + gauche4;

                        int droite1 = frame[7 + i * 8];
                        int droite2 = frame[8 + i * 8];
                        int droite3 = frame[9 + i * 8];
                        int droite4 = frame[10 + i * 8];

                        int codeurDroit = droite1 * 256 * 256 * 256 + droite2 * 256 * 256 + droite3 * 256 + droite4;

                        _lastPidTest[0].Add(codeurGauche);
                        _lastPidTest[1].Add(codeurDroit);
                    }
                    break;
                case UdpFrameFunction.RetourChargeCPU_PWM:
                    int valsCount = frame[2];

                    for (int i = 0; i < valsCount; i++)
                    {
                        double cpuLoad = (frame[3 + i * 6] * 256 + frame[4 + i * 6]) / 5000.0;
                        double pwmLeft = frame[5 + i * 6] * 256 + frame[6 + i * 6] - 4000;
                        double pwmRight = frame[7 + i * 6] * 256 + frame[8 + i * 6] - 4000;


                        _lastRecMoveLoad.Add(cpuLoad);
                        _lastPwmLeft.Add(pwmLeft);
                        _lastPwmRight.Add(pwmRight);
                    }
                    break;
                case UdpFrameFunction.RetourCapteurCouleur:
                    //TODO2018 : multiplier par 2 pour obtenir de belles couleurs ?

                    SensorColorID sensorColor = (SensorColorID)frame[2];

                    Color newColor = Color.FromArgb(Math.Min(255, frame[3] * 1), Math.Min(255, frame[4] * 1), Math.Min(255, frame[5] * 1));

                    if (newColor != SensorsColorValue[sensorColor])
                        OnSensorColorChanged(sensorColor, newColor);

                    _lockSensorColor[(SensorColorID)frame[2]]?.Release();
                    break;
                case UdpFrameFunction.RetourCapteurOnOff:
                    SensorOnOffID sensorOnOff = (SensorOnOffID)frame[2];

                    bool newState = frame[3] > 0 ? true : false;

                    if (sensorOnOff == SensorOnOffID.StartTrigger)
                        SetStartTrigger(newState);

                    if (sensorOnOff == SensorOnOffID.PresenceBuoyRight && newState && Actionneur.ElevatorRight.Armed)
                    {
                        Actionneur.ElevatorRight.Armed = false;
                        Actionneur.ElevatorRight.DoSequencePickupColorThread(Buoy.Red);
                    }

                    if (newState != SensorsOnOffValue[sensorOnOff])
                        OnSensorOnOffChanged(sensorOnOff, newState);

                    _lockSensorOnOff[sensorOnOff]?.Release();

                    break;
                case UdpFrameFunction.RetourValeursNumeriques:
                    Board numericBoard = (Board)frame[0];

                    lock (NumericPinsValue)
                    {
                        NumericPinsValue[numericBoard][0] = (Byte)frame[2];
                        NumericPinsValue[numericBoard][1] = (Byte)frame[3];
                        NumericPinsValue[numericBoard][2] = (Byte)frame[4];
                        NumericPinsValue[numericBoard][3] = (Byte)frame[5];
                        NumericPinsValue[numericBoard][4] = (Byte)frame[6];
                        NumericPinsValue[numericBoard][5] = (Byte)frame[7];
                    }

                    _lockFrame[UdpFrameFunction.RetourValeursNumeriques]?.Release();
                    break;

                case UdpFrameFunction.RetourValeursAnalogiques:
                    Board analogBoard = (Board)frame[0];

                    const double toVolts = 0.0008056640625;

                    List<double> values = new List<double>();
                    AnalogicPinsValue[analogBoard][0] = ((frame[2] * 256 + frame[3]) * toVolts);
                    AnalogicPinsValue[analogBoard][1] = ((frame[4] * 256 + frame[5]) * toVolts);
                    AnalogicPinsValue[analogBoard][2] = ((frame[6] * 256 + frame[7]) * toVolts);
                    AnalogicPinsValue[analogBoard][3] = ((frame[8] * 256 + frame[9]) * toVolts);
                    AnalogicPinsValue[analogBoard][4] = ((frame[10] * 256 + frame[11]) * toVolts);
                    AnalogicPinsValue[analogBoard][5] = ((frame[12] * 256 + frame[13]) * toVolts);
                    AnalogicPinsValue[analogBoard][6] = ((frame[14] * 256 + frame[15]) * toVolts);
                    AnalogicPinsValue[analogBoard][7] = ((frame[16] * 256 + frame[17]) * toVolts);
                    AnalogicPinsValue[analogBoard][8] = ((frame[18] * 256 + frame[19]) * toVolts);

                    _lockFrame[UdpFrameFunction.RetourValeursAnalogiques]?.Release();
                    break;
                case UdpFrameFunction.ReponseLidar:
                    int lidarID = frame[2];

                    if (_lastLidarMeasure == null)
                        _lastLidarMeasure = "";

                    string mess = "";
                    int decallageEntete = 3;

                    if (_lastLidarMeasure.Length == 0)
                    {
                        // C'est le début de la trame à recomposer, et au début y'a la position de la prise de mesure à lire !

                        double y = (double)((short)(frame[3] << 8 | frame[4]) / 10.0);
                        double x = (double)((short)(frame[5] << 8 | frame[6]) / 10.0);
                        double teta = (frame[7] << 8 | frame[8]) / 100.0 - 180;

                        _lastLidarPosition = new Position(-teta, new RealPoint(-x, -y));
                        decallageEntete += 6;
                    }

                    for (int i = decallageEntete; i < frame.Length; i++)
                    {
                        _lastLidarMeasure += (char)frame[i];
                        mess += (char)frame[i];
                    }

                    if (Regex.Matches(_lastLidarMeasure, "\n\n").Count == 2)
                    {
                        _lockFrame[UdpFrameFunction.ReponseLidar]?.Release();
                    }
                    break;
                case UdpFrameFunction.RetourCouleurEquipe:
                    GameBoard.MyColor = frame[2] == 0 ? GameBoard.ColorLeftBlue : GameBoard.ColorRightYellow;
                    if (Config.CurrentConfig.IsMiniRobot) StartTriggerEnable = true;
                    break;
            }
        }

        #region Déplacements

        public override void SetAsservOffset(Position newPosition)
        {
            Position.Copy(newPosition);
            newPosition.Angle = -newPosition.Angle; // Repère angulaire du robot inversé

            Frame frame = UdpFrameFactory.OffsetPos(newPosition, this);
            _asserConnection.SendMessage(frame);

            OnPositionChanged(Position);
        }

        public override void MoveForward(int distance, bool wait = true)
        {
            //TODO2018 : FOnction de déplacement communes avec Simu sur l'historique etc, à voir, le recallage de Simu se fait en avancant...
            base.MoveForward(distance, wait);

            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement] = new Semaphore(0, int.MaxValue);

            IsInLineMove = true;

            Frame frame;
            if (distance < 0)
                frame = UdpFrameFactory.Deplacer(SensAR.Arriere, -distance, this);
            else
                frame = UdpFrameFactory.Deplacer(SensAR.Avant, distance, this);

            _asserConnection.SendMessage(frame);

            Historique.AjouterAction(new ActionAvance(this, distance));

            if (wait)
            {
                //if (!SemaphoresTrame[FrameFunction.FinDeplacement].WaitOne((int)SpeedConfig.LineDuration(distance).TotalMilliseconds))
                //    Thread.Sleep(1000); // Tempo de secours, on a jamais reçu la fin de trajectoire après la fin du délai théorique
                _lockFrame[UdpFrameFunction.FinDeplacement].WaitOne();
            }
        }

        public override void MoveBackward(int distance, bool wait = true)
        {
            base.MoveBackward(distance, wait);

            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement] = new Semaphore(0, int.MaxValue);

            IsInLineMove = true;

            Frame frame;
            if (distance < 0)
                frame = UdpFrameFactory.Deplacer(SensAR.Avant, -distance, this);
            else
                frame = UdpFrameFactory.Deplacer(SensAR.Arriere, distance, this);

            _asserConnection.SendMessage(frame);

            Historique.AjouterAction(new ActionRecule(this, distance));

            if (wait)
            {
                //if (!SemaphoresTrame[FrameFunction.FinDeplacement].WaitOne((int)SpeedConfig.LineDuration(distance).TotalMilliseconds))
                //    Thread.Sleep(1000); // Tempo de secours, on a jamais reçu la fin de trajectoire après la fin du délai théorique
                _lockFrame[UdpFrameFunction.FinDeplacement].WaitOne();
            }
        }

        public override void PivotLeft(AngleDelta angle, bool wait = true)
        {
            base.PivotLeft(angle, wait);

            angle = Math.Round(angle, 2);

            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.Pivot(SensGD.Gauche, angle, this);
            _asserConnection.SendMessage(frame);

            Historique.AjouterAction(new ActionPivot(this, angle, SensGD.Gauche));

            if (wait)
            {
                //if (!SemaphoresTrame[FrameFunction.FinDeplacement].WaitOne((int)SpeedConfig.PivotDuration(angle, Entraxe).TotalMilliseconds))
                //    Thread.Sleep(1000); // Tempo de secours, on a jamais reçu la fin de trajectoire après la fin du délai théorique
                _lockFrame[UdpFrameFunction.FinDeplacement].WaitOne();
            }
        }

        public override void PivotRight(AngleDelta angle, bool wait = true)
        {
            base.PivotRight(angle, wait);

            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.Pivot(SensGD.Droite, angle, this);
            _asserConnection.SendMessage(frame);

            Historique.AjouterAction(new ActionPivot(this, angle, SensGD.Droite));

            if (wait)
            {
                //if (!SemaphoresTrame[FrameFunction.FinDeplacement].WaitOne((int)SpeedConfig.PivotDuration(angle, Entraxe).TotalMilliseconds))
                //    Thread.Sleep(1000); // Tempo de secours, on a jamais reçu la fin de trajectoire après la fin du délai théorique
                _lockFrame[UdpFrameFunction.FinDeplacement].WaitOne();
            }
        }

        public override void Stop(StopMode mode = StopMode.Smooth)
        {
            AsserEnable = (mode != StopMode.Freely);

            IsInLineMove = false;

            Frame frame = UdpFrameFactory.Stop(mode, this);
            _asserConnection.SendMessage(frame);

            Historique.AjouterAction(new ActionStop(this, mode));
        }

        public override void Turn(SensAR sensAr, SensGD sensGd, int radius, AngleDelta angle, bool wait = true)
        {
            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.Virage(sensAr, sensGd, radius, angle, this);
            _asserConnection.SendMessage(frame);

            Historique.AjouterAction(new ActionVirage(this, radius, angle, sensAr, sensGd));

            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement].WaitOne();
        }

        public override void PolarTrajectory(SensAR sens, List<RealPoint> points, bool wait = true)
        {
            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.TrajectoirePolaire(sens, points, this);
            _asserConnection.SendMessage(frame);

            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement].WaitOne();
        }

        public override void Recalibration(SensAR sens, bool wait = true, bool sendOffset = false)
        {
            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.Recallage(sens, this);
            _asserConnection.SendMessage(frame);

            Historique.AjouterAction(new ActionRecallage(this, sens));

            if (wait) _lockFrame[UdpFrameFunction.FinDeplacement].WaitOne();

            base.Recalibration(sens, wait, sendOffset);
        }

        #endregion

        public override void SendPID(int p, int i, int d)
        {
            Frame frame = UdpFrameFactory.CoeffAsserv(p, i, d, this);
            _asserConnection.SendMessage(frame);
        }

        public override void SendPIDCap(int p, int i, int d)
        {
            Frame frame = UdpFrameFactory.CoeffAsservCap(p, i, d, this);
            _asserConnection.SendMessage(frame);
        }

        public override void SendPIDSpeed(int p, int i, int d)
        {
            Frame frame = UdpFrameFactory.CoeffAsservVitesse(p, i, d, this);
            _asserConnection.SendMessage(frame);
        }

        public Position ReadPosition()
        {
            _lockFrame[UdpFrameFunction.AsserRetourPositionCodeurs] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.DemandePosition(this);
            _asserConnection.SendMessage(frame);

            _lockFrame[UdpFrameFunction.AsserRetourPositionXYTeta].WaitOne(100);

            return Position;
        }

        public override void ReadAnalogicPins(Board board, bool wait = true)
        {
            if (wait) _lockFrame[UdpFrameFunction.RetourValeursAnalogiques] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.DemandeValeursAnalogiques(board);
            Connections.UDPBoardConnection[board].SendMessage(frame);

            if (wait) _lockFrame[UdpFrameFunction.RetourValeursAnalogiques].WaitOne(100);
        }

        public override void ReadNumericPins(Board board, bool wait = true)
        {
            if (wait) _lockFrame[UdpFrameFunction.RetourValeursNumeriques] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.DemandeValeursNumeriques(board);
            Connections.UDPBoardConnection[board].SendMessage(frame);

            if (wait) _lockFrame[UdpFrameFunction.RetourValeursNumeriques].WaitOne(100);
        }

        public override void SetActuatorOnOffValue(ActuatorOnOffID actuator, bool on)
        {
            ActuatorOnOffState[actuator] = on;

            Frame frame = UdpFrameFactory.ActionneurOnOff(_boardActuatorOnOff[actuator], actuator, on);
            Connections.UDPBoardConnection[_boardActuatorOnOff[actuator]].SendMessage(frame);

            Historique.AjouterAction(new ActionOnOff(this, actuator, on));
        }

        public override void SetMotorAtPosition(MotorID motor, int position, bool wait)
        {
            base.SetMotorAtPosition(motor, position);

            if (wait) _lockMotor[motor] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.MoteurPosition(_boardMotor[motor], motor, position);
            Connections.UDPBoardConnection[_boardMotor[motor]].SendMessage(frame);

            if (wait) _lockMotor[motor].WaitOne(5000);
        }

        public override void MotorWaitEnd(MotorID motor)
        {
            base.MotorWaitEnd(motor);

            _lockMotor[motor].WaitOne(5000);
        }

        public override void SetMotorAtOrigin(MotorID motor, bool wait)
        {
            base.SetMotorAtOrigin(motor);

            if (wait) _lockMotor[motor] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.MoteurOrigin(_boardMotor[motor], motor);
            Connections.UDPBoardConnection[_boardMotor[motor]].SendMessage(frame);

            if (wait) _lockMotor[motor].WaitOne(30000);
        }

        public override void SetMotorSpeed(MotorID motor, SensGD sens, int speed)
        {
            base.SetMotorSpeed(motor, sens, speed);

            Frame trame = UdpFrameFactory.MoteurVitesse(_boardMotor[motor], motor, sens, speed);
            Connections.UDPBoardConnection[_boardMotor[motor]].SendMessage(trame);
        }

        public override void SetMotorReset(MotorID motor)
        {
            base.SetMotorReset(motor);

            Frame trame = UdpFrameFactory.MoteurResetPosition(_boardMotor[motor], motor);
            Connections.UDPBoardConnection[_boardMotor[motor]].SendMessage(trame);
        }

        public override void SetMotorStop(MotorID motor, StopMode mode)
        {
            base.SetMotorStop(motor, mode);

            Frame trame = UdpFrameFactory.MoteurStop(_boardMotor[motor], motor, mode);
            Connections.UDPBoardConnection[_boardMotor[motor]].SendMessage(trame);
        }

        public override void SetMotorAcceleration(MotorID motor, int acceleration)
        {
            base.SetMotorAcceleration(motor, acceleration);

            Frame trame = UdpFrameFactory.MoteurAcceleration(_boardMotor[motor], motor, acceleration);
            Connections.UDPBoardConnection[_boardMotor[motor]].SendMessage(trame);
        }

        public override void EnablePower(bool on)
        {
            // TODOEACHYEAR : couper tout manuellement

            Stop(StopMode.Freely);
        }

        public override bool ReadStartTrigger()
        {
            return ReadSensorOnOff(SensorOnOffID.StartTrigger, true);
        }

        public override List<int>[] DiagnosticPID(int steps, SensAR sens, int pointsCount)
        {
            _lastPidTest = new List<int>[2];
            _lastPidTest[0] = new List<int>();
            _lastPidTest[1] = new List<int>();

            Frame frame = UdpFrameFactory.EnvoiConsigneBrute(steps, sens, this);
            _asserConnection.SendMessage(frame);

            frame = UdpFrameFactory.DemandePositionsCodeurs(this);
            while (_lastPidTest[0].Count < pointsCount)
            {
                _asserConnection.SendMessage(frame);
                Thread.Sleep(30);
            }

            List<int>[] output = new List<int>[2];
            output[0] = _lastPidTest[0].GetRange(0, pointsCount);
            output[1] = _lastPidTest[1].GetRange(0, pointsCount);

            return output;
        }

        public override List<int>[] DiagnosticLine(int distance, SensAR sens)
        {
            _lastPidTest = new List<int>[2];
            _lastPidTest[0] = new List<int>();
            _lastPidTest[1] = new List<int>();

            _lockFrame[UdpFrameFunction.FinDeplacement] = new Semaphore(0, int.MaxValue);

            Frame frame = UdpFrameFactory.Deplacer(sens, distance, this);
            _asserConnection.SendMessage(frame);

            frame = UdpFrameFactory.DemandePositionsCodeurs(this);

            while (!_lockFrame[UdpFrameFunction.FinDeplacement].WaitOne(0))
            {
                _asserConnection.SendMessage(frame);
                Thread.Sleep(30);
            }

            List<int>[] output = new List<int>[2];
            output[0] = new List<int>(_lastPidTest[0]);
            output[1] = new List<int>(_lastPidTest[1]);

            return output;
        }

        public override List<double>[] DiagnosticCpuPwm(int pointsCount)
        {
            _lastRecMoveLoad = new List<double>();
            _lastPwmLeft = new List<double>();
            _lastPwmRight = new List<double>();

            Frame frame = UdpFrameFactory.DemandeCpuPwm(this);
            while (_lastRecMoveLoad.Count <= pointsCount)
            {
                _asserConnection.SendMessage(frame);
                Thread.Sleep(30);
            }

            // Supprime d'éventuelles valeurs supplémentaires
            while (_lastRecMoveLoad.Count > pointsCount)
                _lastRecMoveLoad.RemoveAt(_lastRecMoveLoad.Count - 1);

            while (_lastPwmLeft.Count > pointsCount)
                _lastPwmLeft.RemoveAt(_lastPwmLeft.Count - 1);

            while (_lastPwmRight.Count > pointsCount)
                _lastPwmRight.RemoveAt(_lastPwmRight.Count - 1);

            return new List<double>[3] { _lastRecMoveLoad, _lastPwmLeft, _lastPwmRight };
        }

        public void EnvoyerUart(Board byBoard, Frame frame)
        {
            Frame frameUdp = UdpFrameFactory.EnvoyerUart1(byBoard, frame);
            Connections.UDPBoardConnection[byBoard].SendMessage(frameUdp);
        }

        public override String ReadLidarMeasure(LidarID lidar, int timeout, out Position refPosition)
        {
            _lastLidarMeasure = "";

            _lockFrame[UdpFrameFunction.ReponseLidar] = new Semaphore(0, int.MaxValue);
            Connections.ConnectionMove.SendMessage(UdpFrameFactory.DemandeMesureLidar(lidar));
            _lockFrame[UdpFrameFunction.ReponseLidar].WaitOne(timeout);

            refPosition = _lastLidarPosition;

            return _lastLidarMeasure;
        }
    }
}
<file_sep>/GoBot/GoBot/GameElements/AllGameElements.cs
﻿using System.Collections.Generic;
using System.Linq;
using System.Collections;
using GoBot.BoardContext;

using Geometry.Shapes;
using System.Drawing;
using Geometry;
using System;

namespace GoBot.GameElements
{
    public class AllGameElements : IEnumerable<GameElement>
    {
        public delegate void ObstaclesChangedDelegate();
        public event ObstaclesChangedDelegate ObstaclesChanged;

        private List<Buoy> _buoys;
        private List<GroundedZone> _groundedZones;
        private List<RandomDropOff> _randomDropoff;
        private List<ColorDropOff> _colorDropoff;
        private List<LightHouse> _lightHouses;
        private GameElementZone _randomPickup;

        public AllGameElements()
        {
            // TODOEACHYEAR Ajouter ici tous les éléments dans les listes

            _buoys = new List<Buoy>();
            _buoys.Add(new Buoy(new RealPoint(-67, 1450), GameBoard.ColorLeftBlue, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(-67, 1525), GameBoard.ColorLeftBlue, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(-67, 1600), GameBoard.ColorLeftBlue, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(-67, 1675), GameBoard.ColorLeftBlue, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(-67, 1750), GameBoard.ColorLeftBlue, Buoy.Red));

            _buoys.Add(new Buoy(new RealPoint(3000 - 700, -67), GameBoard.ColorLeftBlue, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3000 - 775, -67), GameBoard.ColorLeftBlue, Color.LightGray));
            _buoys.Add(new Buoy(new RealPoint(3000 - 850, -67), GameBoard.ColorLeftBlue, Color.LightGray));
            _buoys.Add(new Buoy(new RealPoint(3000 - 925, -67), GameBoard.ColorLeftBlue, Color.LightGray));
            _buoys.Add(new Buoy(new RealPoint(3000 - 1000, -67), GameBoard.ColorLeftBlue, Buoy.Red));

            _buoys.Add(new Buoy(new RealPoint(700, -67), GameBoard.ColorLeftBlue, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(775, -67), GameBoard.ColorLeftBlue, Color.LightGray));
            _buoys.Add(new Buoy(new RealPoint(850, -67), GameBoard.ColorLeftBlue, Color.LightGray));
            _buoys.Add(new Buoy(new RealPoint(925, -67), GameBoard.ColorLeftBlue, Color.LightGray));
            _buoys.Add(new Buoy(new RealPoint(1000, -67), GameBoard.ColorLeftBlue, Buoy.Red));

            _buoys.Add(new Buoy(new RealPoint(3067, 1450), GameBoard.ColorLeftBlue, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3067, 1525), GameBoard.ColorLeftBlue, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(3067, 1600), GameBoard.ColorLeftBlue, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3067, 1675), GameBoard.ColorLeftBlue, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(3067, 1750), GameBoard.ColorLeftBlue, Buoy.Green));

            _buoys.Add(new Buoy(new RealPoint(300, 400), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(300, 1200), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(450, 510), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(450, 1080), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(670, 100), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(950, 400), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(1100, 800), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(1270, 1200), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(1005, 1955), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(1065, 1650), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(1335, 1650), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(1395, 1955), GameBoard.ColorNeutral, Buoy.Green));

            _buoys.Add(new Buoy(new RealPoint(3000 - 300, 400), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3000 - 300, 1200), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(3000 - 450, 510), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(3000 - 450, 1080), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3000 - 670, 100), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3000 - 950, 400), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(3000 - 1100, 800), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3000 - 1270, 1200), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(3000 - 1005, 1955), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3000 - 1065, 1650), GameBoard.ColorNeutral, Buoy.Red));
            _buoys.Add(new Buoy(new RealPoint(3000 - 1335, 1650), GameBoard.ColorNeutral, Buoy.Green));
            _buoys.Add(new Buoy(new RealPoint(3000 - 1395, 1955), GameBoard.ColorNeutral, Buoy.Red));

            _groundedZones = new List<GroundedZone>();
            _groundedZones.Add(new GroundedZone(new RealPoint(-70, 1600), Color.White, _buoys.GetRange(0, 5)));
            _groundedZones.Add(new GroundedZone(new RealPoint(3000 - 850, -70), Color.White, _buoys.GetRange(5, 5)));
            _groundedZones.Add(new GroundedZone(new RealPoint(850, -70), Color.White, _buoys.GetRange(10, 5)));
            _groundedZones.Add(new GroundedZone(new RealPoint(3000 + 70, 1600), Color.White, _buoys.GetRange(15, 5)));

            _randomDropoff = new List<RandomDropOff>();
            _randomDropoff.Add(new RandomDropOff(new RealPoint(200, 800), GameBoard.ColorLeftBlue));
            _randomDropoff.Add(new RandomDropOff(new RealPoint(3000 - 200, 800), GameBoard.ColorRightYellow));

            _colorDropoff = new List<ColorDropOff>();
            _colorDropoff.Add(new ColorDropOff(new RealPoint(1200, 2000 - 150), GameBoard.ColorRightYellow, 
                _buoys.Find(b => b.Position.Distance(new RealPoint(1065, 1650)) < 1),
                _buoys.Find(b => b.Position.Distance(new RealPoint(1335, 1650)) < 1),
                _buoys.Find(b => b.Position.Distance(new RealPoint(1005, 1955)) < 1),
                _buoys.Find(b => b.Position.Distance(new RealPoint(1395, 1955)) < 1)));
            _colorDropoff.Add(new ColorDropOff(new RealPoint(1800, 2000 - 150), GameBoard.ColorLeftBlue, 
                _buoys.Find(b => b.Position.Distance(new RealPoint(3000 - 1065, 1650)) < 1),
                _buoys.Find(b => b.Position.Distance(new RealPoint(3000 - 1335, 1650)) < 1),
                _buoys.Find(b => b.Position.Distance(new RealPoint(3000 - 1005, 1955)) < 1),
                _buoys.Find(b => b.Position.Distance(new RealPoint(3000 - 1395, 1955)) < 1)));

            _lightHouses = new List<LightHouse>();
            _lightHouses.Add(new LightHouse(new RealPoint(325, -122), GameBoard.ColorLeftBlue));
            _lightHouses.Add(new LightHouse(new RealPoint(3000 - 325, -122), GameBoard.ColorRightYellow));

            _randomPickup = new GameElementZone(new RealPoint(1500, 0), Color.White, 500);

            GenerateRandomBuoys();
        }

        private void GenerateRandomBuoys()
        {
            Circle zone = new Circle(new RealPoint(1500, 0),  500);
            List<Circle> randoms = new List<Circle>();

            Random r = new Random();
            for (int i = 0; i < 6; i++)
            {
                RealPoint random = new RealPoint(r.Next(1000, 1500) + (i < 3 ? 0 : 500), r.Next(36, 500-36));
                Circle c = new Circle(random, 36);
                if (zone.Contains(c) && !randoms.Exists(o => random.Distance(o) < 36+2))
                {
                    _buoys.Add(new Buoy(random, Color.White, i % 2 == 0 ? Buoy.Green : Buoy.Red, true));
                    randoms.Add(c);
                }
                else
                    i -= 1;
            }
        }

        public IEnumerable<GameElement> AllElements
        {
            get
            {
                IEnumerable<GameElement> elements = Enumerable.Empty<GameElement>();

                // TODOEACHYEAR Concaténer ici les listes d'éléments
                elements = elements.Concat(_buoys);
                elements = elements.Concat(_groundedZones);
                elements = elements.Concat(_randomDropoff);
                elements = elements.Concat(_colorDropoff);
                elements = elements.Concat(_lightHouses);
                elements = elements.Concat(new List<GameElement>() { _randomPickup });

                return elements;
            }
        }

        public Buoy FindBuoy(RealPoint pos)
        {
            return _buoys.OrderBy(b => b.Position.Distance(pos)).First();
        }

        public void RemoveBuoy(Buoy b)
        {
            _buoys.Remove(b);
        }

        public List<Buoy> Buoys => _buoys;
        public List<Buoy> BuoysForLeft => _buoys.Where(b => b.Position.X < 1800 && b.Position.Y < 1500 && !RandomPickup.Contains(b.Position)).ToList();
        public List<Buoy> BuoysForRight => _buoys.Where(b => b.Position.X > 1200 && b.Position.Y < 1500 && !RandomPickup.Contains(b.Position)).ToList();
        public List<GroundedZone> GroundedZones => _groundedZones;
        public List<RandomDropOff> RandomDropoffs => _randomDropoff;
        public List<ColorDropOff> ColorDropoffs => _colorDropoff;
        public List<LightHouse> LightHouses => _lightHouses;
        public GameElementZone RandomPickup => _randomPickup;

        public IEnumerator<GameElement> GetEnumerator()
        {
            return AllElements.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return AllElements.GetEnumerator();
        }

        public IEnumerable<IShape> AsObstacles
        {
            get
            {
                List<IShape> obstacles = new List<IShape>();

                if (GameBoard.Strategy != null && GameBoard.Strategy.AvoidElements)
                {
                    // TODOEACHYEAR Ici ajouter à obstacles les elements à contourner
                }

                return obstacles;
            }
        }

        public IEnumerable<IShape> AsVisibleObstacles
        {
            get
            {
                List<IShape> obstacles = new List<IShape>();

                if (GameBoard.Strategy != null && GameBoard.Strategy.AvoidElements)
                {

                }

                obstacles.AddRange(_buoys.Select(b => new Circle(b.Position, b.HoverRadius)));

                return obstacles;
            }
        }

        public void SetOpponents(List<RealPoint> positions)
        {
            // TODOEACHYEAR Mettre à jour ICI les éléments en fonction de la position des adversaires

            int opponentRadius = 150;

            //foreach (Buoy b in _buoys)
            //{
            //    if (positions.Exists(p => p.Distance(b.Position) < opponentRadius))
            //        b.IsAvailable = false;
            //}
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/CAN/CanFrameFunction.cs
﻿namespace GoBot.Communications.CAN
{
    public enum CanFrameFunction
    {
        PositionAsk = 0x01,
        PositionResponse = 0x02,
        PositionSet = 0x03,
        PositionMinAsk = 0x04,
        PositionMinResponse = 0x05,
        PositionMinSet = 0x06,
        PositionMaxAsk = 0x07,
        PositionMaxResponse = 0x08,
        PositionMaxSet = 0x09,
        SpeedMaxAsk = 0x0A,
        SpeedMaxResponse = 0x0B,
        SpeedMaxSet = 0x0C,
        TorqueMaxAsk = 0x0D,
        TorqueMaxResponse = 0x0E,
        TorqueMaxSet = 0x0F,
        TorqueCurrentAsk = 0x10,
        TorqueCurrentResponse = 0x11,
        AccelerationAsk = 0x12,
        AccelerationResponse = 0x13,
        AccelerationSet = 0x14,
        TargetSet = 0x15,
        TrajectorySet = 0x16,
        DisableOutput = 0x17,
        TorqueAlert = 0x18,
        TestConnection = 0x19,

        Debug = 0xF0,
        DebugAsk = 0xF1,
        DebugResponse = 0xF2,
        Buffer = 0xF3, // TODO2020
        BatterieVoltage = 0xF5,
        Buzzer = 0xF6
    }

    public enum CanBoard
    {
        PC = 0x00,
        CanServo1 = 0x01,
        CanServo2 = 0x02,
        CanServo3 = 0x03,
        CanServo4 = 0x04,
        CanServo5 = 0x05,
        CanServo6 = 0x06,
        CanAlim = 0x0A
    }
}
<file_sep>/GoBot/GoBot/Logs/logFile.cs
﻿using System;
using System.IO;

namespace GoBot.Logs
{
    public class LogFile : ILog
    {
        private String FileName { get; set; }
        private StreamWriter Writer { get; set; }

        public LogFile(String fileName)
        {
            FileName = fileName;
            if (!Directory.Exists(Path.GetDirectoryName(FileName))) Directory.CreateDirectory(Path.GetDirectoryName(FileName));
            Writer = new StreamWriter(FileName);
            Writer.AutoFlush = true;
        }

        public void Write(String message)
        {
            Writer.WriteLine(DateTime.Now.ToString("dd:MM:yyyy hh:mm:ss:ffff\t") + message);
        }

        public void Close()
        {
            Writer.Close();
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/LidarSimu.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.BoardContext;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;

namespace GoBot.Devices
{
    class LidarSimu : Lidar
    {
        private List<double> _distances;
        private Threading.ThreadLink _link;
        private AngleDelta _scanRange;
        private int _pointsCount;
        private double _noise;
        private Random _rand;

        public override bool Activated => _link != null && _link.Running;

        public LidarSimu() : base()
        {
            _distances = Enumerable.Repeat(1500d, 360 * 3).ToList();
            _scanRange = 360;
            _pointsCount = 360 * 3;
            _noise = 5;
            _rand = new Random();
        }

        public override AngleDelta DeadAngle => 0;

        protected override bool StartLoop()
        {
            _link = Threading.ThreadManager.CreateThread(link => UpdateDetection());
            _link.StartInfiniteLoop(50);

            return true;
        }

        protected override void StopLoop()
        {
            _link.Cancel();
            _link.WaitEnd();
            _link = null;
        }

        protected void UpdateDetection()
        {
            //List<RealPoint> measures = ValuesToPositions(_distances, false, 50, 5000, _position);
            List<RealPoint> measures = Detection(_position);
            this.OnNewMeasure(measures);

            Random r = new Random();
            for (int i = 0; i < _distances.Count; i++)
                _distances[i] = (_distances[(i - 1 + _distances.Count) % _distances.Count] + _distances[i] + _distances[(i + 1) % _distances.Count]) / 3 + r.NextDouble() * 3 - 1.5;
        }

        private List<RealPoint> ValuesToPositions(List<double> measures, bool limitOnTable, int minDistance, int maxDistance, Position refPosition)
        {
            List<RealPoint> positions = new List<RealPoint>();
            AngleDelta resolution = _scanRange / _pointsCount;

            for (int i = 0; i < measures.Count; i++)
            {
                AnglePosition angle = resolution * i;

                if (measures[i] > minDistance && (measures[i] < maxDistance || maxDistance == -1))
                {
                    AnglePosition anglePoint = new AnglePosition(angle.InPositiveRadians - refPosition.Angle.InPositiveRadians - _scanRange.InRadians / 2 - Math.PI / 2, AngleType.Radian);

                    RealPoint pos = new RealPoint(refPosition.Coordinates.X - anglePoint.Sin * measures[i], refPosition.Coordinates.Y - anglePoint.Cos * measures[i]);

                    int marge = 20; // Marge en mm de distance de detection à l'exterieur de la table (pour ne pas jeter les mesures de la bordure qui ne collent pas parfaitement)
                    if (!limitOnTable || (pos.X > -marge && pos.X < GameBoard.Width + marge && pos.Y > -marge && pos.Y < GameBoard.Height + marge))
                        positions.Add(pos);
                }
            }

            return positions;
        }

        public override List<RealPoint> GetPoints()
        {
            return Detection(_position);
        }

        private List<RealPoint> Detection(Position refPosition)
        {
            List<RealPoint> positions = new List<RealPoint>();
            AngleDelta resolution = _scanRange / _pointsCount;
            int dist = 10000;

            List<IShape> obstacles = new List<IShape>();
            obstacles.AddRange(GameBoard.ObstaclesLidarGround);

            for (int i = 0; i < _pointsCount; i++)
            {
                AnglePosition angle = resolution * i;

                AnglePosition anglePoint = new AnglePosition(angle.InPositiveRadians - refPosition.Angle.InPositiveRadians - _scanRange.InRadians / 2 - Math.PI / 2, AngleType.Radian);

                RealPoint pos = new RealPoint(refPosition.Coordinates.X - anglePoint.Sin * dist, refPosition.Coordinates.Y - anglePoint.Cos * dist);

                positions.Add(GetDetectionLinePoint(new Segment(refPosition.Coordinates, pos), obstacles));

            }

            return positions;
        }

        private RealPoint GetDetectionLinePoint(Segment seg, IEnumerable<IShape> obstacles)
        {
            RealPoint closest = null;
            double minDistance = double.MaxValue;

            foreach (IShape s in obstacles)
            {
                List<RealPoint> pts = s.GetCrossingPoints(seg);
                foreach (RealPoint p in pts)
                {
                    double newDistance = seg.StartPoint.Distance(p);
                    if (newDistance < minDistance)
                    {
                        minDistance = newDistance;
                        closest = p;
                    }
                }
            }

            Direction d = Maths.GetDirection(seg.StartPoint, closest);
            d.distance += (_rand.NextDouble() * _noise * 2 - _noise);
            closest = Maths.GetDestination(seg.StartPoint, d);

            return closest;
        }
    }
}
<file_sep>/GoBot/Geometry/BezierCurve.cs
﻿using System;
using System.Collections.Generic;

using Geometry.Shapes;

namespace Geometry
{
    public class BezierCurve
    {
        private static double[] _factorial;
        
        /// <summary>
        /// Retourne une liste de points correspondant à une courbe de Bézier suivant des points donnés en entrée
        /// </summary>
        /// <param name="points">Points à suivre pour créer la courbe de Bézier</param>
        /// <param name="pointsCnt">Nombre de points à générer</param>
        /// <returns>Liste de points suivant la courbe de Bezier générée</returns>
        public static List<RealPoint> GetPoints(List<RealPoint> points, int pointsCnt)
        {
            List<RealPoint> pts = new List<RealPoint>();

            double[] ptsIn = new double[points.Count * 2];
            double[] ptsOut = new double[pointsCnt * 2];

            for (int i = 0; i < points.Count; i++)
            {
                ptsIn[i * 2] = points[i].X;
                ptsIn[i * 2 + 1] = points[i].Y;
            }

            Bezier2D(ptsIn, pointsCnt, ptsOut);

            for (int i = 0; i < pointsCnt * 2; i += 2)
            {
                pts.Add(new RealPoint(ptsOut[i], ptsOut[i + 1]));
            }

            return pts;
        }

        private static double factorial(int n)
        {
            if (_factorial is null)
                CreateFactorialTable();

            // Just check if n is appropriate, then return the result
            if (n < 0) { throw new Exception("n is less than 0"); }
            if (n > 32) { throw new Exception("n is greater than 32"); }

            return _factorial[n]; /* returns the value n! as a SUMORealing point number */
        }

        private static void CreateFactorialTable()
        {
            // Create lookup table for fast factorial calculation
            // Fill untill n=32. The rest is too high to represent
            _factorial = new double[33];
            _factorial[0] = 1.0;
            _factorial[1] = 1.0;
            _factorial[2] = 2.0;
            _factorial[3] = 6.0;
            _factorial[4] = 24.0;
            _factorial[5] = 120.0;
            _factorial[6] = 720.0;
            _factorial[7] = 5040.0;
            _factorial[8] = 40320.0;
            _factorial[9] = 362880.0;
            _factorial[10] = 3628800.0;
            _factorial[11] = 39916800.0;
            _factorial[12] = 479001600.0;
            _factorial[13] = 6227020800.0;
            _factorial[14] = 87178291200.0;
            _factorial[15] = 1307674368000.0;
            _factorial[16] = 20922789888000.0;
            _factorial[17] = 355687428096000.0;
            _factorial[18] = 6402373705728000.0;
            _factorial[19] = 121645100408832000.0;
            _factorial[20] = 2432902008176640000.0;
            _factorial[21] = 51090942171709440000.0;
            _factorial[22] = 1124000727777607680000.0;
            _factorial[23] = 25852016738884976640000.0;
            _factorial[24] = 620448401733239439360000.0;
            _factorial[25] = 15511210043330985984000000.0;
            _factorial[26] = 403291461126605635584000000.0;
            _factorial[27] = 10888869450418352160768000000.0;
            _factorial[28] = 304888344611713860501504000000.0;
            _factorial[29] = 8841761993739701954543616000000.0;
            _factorial[30] = 265252859812191058636308480000000.0;
            _factorial[31] = 8222838654177922817725562880000000.0;
            _factorial[32] = 263130836933693530167218012160000000.0;
        }

        private static double Ni(int n, int i)
        {
            double ni;
            double a1 = factorial(n);
            double a2 = factorial(i);
            double a3 = factorial(n - i);
            ni = a1 / (a2 * a3);
            return ni;
        }

        private static double Bernstein(int n, int i, double t)
        {
            // Calculate Bernstein basis

            double basis;
            double ti; /* t^i */
            double tni; /* (1 - t)^i */

            /* Prevent problems with pow */

            if (t == 0.0 && i == 0)
                ti = 1.0;
            else
                ti = Math.Pow(t, i);

            if (n == i && t == 1.0)
                tni = 1.0;
            else
                tni = Math.Pow((1 - t), (n - i));

            //Bernstein basis
            basis = Ni(n, i) * ti * tni;
            return basis;
        }

        private static void Bezier2D(double[] b, int cpts, double[] p)
        {
            int npts = (b.Length) / 2;
            int icount, jcount;
            double step, t;

            // Calculate points on curve

            icount = 0;
            t = 0;
            step = (double)1.0 / (cpts - 1);

            for (int i1 = 0; i1 != cpts; i1++)
            {
                if ((1.0 - t) < 5e-6)
                    t = 1.0;

                jcount = 0;
                p[icount] = 0.0;
                p[icount + 1] = 0.0;
                for (int i = 0; i != npts; i++)
                {
                    double basis = Bernstein(npts - 1, i, t);
                    p[icount] += basis * b[jcount];
                    p[icount + 1] += basis * b[jcount + 1];
                    jcount = jcount + 2;
                }

                icount += 2;
                t += step;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/GameElements/Buoy.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using Geometry;
using Geometry.Shapes;

namespace GoBot.GameElements
{
    public class Buoy : GameElement
    {
        public static Color Red = Color.FromArgb(187, 30, 16);
        public static Color Green = Color.FromArgb(0, 111, 61);

        private Color _color;

        private bool _virtual;

        public Buoy(RealPoint position, Color owner, Color color, bool isVirtual = false) : base(position, owner, 36)
        {
            _color = color;
            _virtual = isVirtual;
        }

        public Color Color => _color;

        public bool IsVirtual => _virtual;

        public override void Paint(Graphics g, WorldScale scale)
        {
            if (_isAvailable)
            {
                Circle c = new Circle(_position, _hoverRadius);
                c.Paint(g, _isHover ? Color.White : Color.Black, 1, _color, scale);
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PagePepperl.cs
﻿using System;
using System.Windows.Forms;
using System.Threading;

using GoBot.Devices;

namespace GoBot.IHM.Pages
{
    public partial class PagePepperl : UserControl
    {
        Pepperl _lidar;
        
        private void btnText_Click(object sender, EventArgs e)
        {
            _lidar.ShowMessage(txtText1.Text, txtText2.Text);
        }

        public PagePepperl()
        {
            InitializeComponent();
        }

        private void PagePepperl_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                _lidar = (Pepperl)AllDevices.LidarAvoid;

                foreach (PepperlFreq f in Enum.GetValues(typeof(PepperlFreq)))
                    cboFreq.Items.Add(f);

                foreach (PepperlFilter f in Enum.GetValues(typeof(PepperlFilter)))
                    cboFilter.Items.Add(f);

                UpdateInfos();
            }
        }

        private void btnReboot_Click(object sender, EventArgs e)
        {
            _lidar.Reboot();
        }

        private void cboFreq_SelectedValueChanged(object sender, EventArgs e)
        {
            PepperlFreq value = (PepperlFreq)cboFreq.SelectedItem;

            if (_lidar.Frequency != value)
            {
                _lidar.SetFrequency(value);
                UpdateInfos();
            }
        }

        private void numFilter_ValueChanged(object sender, EventArgs e)
        {
            int value = (int)numFilter.Value;

            if (_lidar.FilterWidth != value)
            {
                _lidar.SetFilter(_lidar.Filter, value);
                UpdateInfos();
            }
        }

        private void cboFilter_SelectedValueChanged(object sender, EventArgs e)
        {
            PepperlFilter value = (PepperlFilter)cboFilter.SelectedItem;

            if (_lidar.Filter != value)
            {
                _lidar.SetFilter(value, _lidar.FilterWidth);
                UpdateInfos();
            }
        }

        private void UpdateInfos()
        {
            cboFilter.SelectedItem = _lidar.Filter;
            cboFreq.SelectedItem = _lidar.Frequency;

            lblPoints.Text = _lidar.PointsPerScan.ToString();
            lblResolution.Text = _lidar.Resolution.ToString();
            lblDistPoints.Text = _lidar.PointsDistanceAt(3000).ToString("0.0") + " mm";

            numFilter.Visible = (_lidar.Filter != PepperlFilter.None);
            lblFilterPoints.Visible = numFilter.Visible;
        }
    }
}
<file_sep>/GoBot/GoBot/Movements/MovementRandomDropoff.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Actionneurs;
using GoBot.BoardContext;
using GoBot.GameElements;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;

namespace GoBot.Movements
{
    class MovementRandomDropoff : Movement
    {
        private RandomDropOff _dropoff;

        public override bool CanExecute => Actionneur.Lifter.Loaded;

        public override int Score => 5;

        public override double Value => 3;

        public override GameElement Element => _dropoff;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => _dropoff.Owner;

        public MovementRandomDropoff(RandomDropOff dropoff)
        {
            _dropoff = dropoff;

            Positions.Add(new Position(0, new RealPoint(_dropoff.Position.X + 300 + 40, _dropoff.Position.Y)));
            Positions.Add(new Position(180, new RealPoint(_dropoff.Position.X - 300 - 40, _dropoff.Position.Y)));
        }

        protected override void MovementBegin()
        {
            // rien
        }

        protected override bool MovementCore()
        {
            Robots.MainRobot.SetSpeedSlow();
            Stopwatch sw = Stopwatch.StartNew();
            Actionneur.Lifter.DoTilterPositionDropoff();

            Robots.MainRobot.Move(-(270 - 85 * _dropoff.LoadsCount));

            int waitMs = 500 - (int)sw.ElapsedMilliseconds;
            if (waitMs > 0) Thread.Sleep(waitMs);
            Actionneur.Lifter.DoOpenAll();
            Thread.Sleep(100);
            Actionneur.Lifter.DoTilterPositionStore();
            _dropoff.AddLoad(Actionneur.Lifter.Load);
            Actionneur.Lifter.Load = null;
            Thread.Sleep(150);

            Actionneur.Lifter.DoStoreAll();
            Robots.MainRobot.Move(200);
            Robots.MainRobot.SetSpeedFast();

            Actionneur.Lifter.DoDisableAll();

            return true;
        }

        protected override void MovementEnd()
        {
            // rien
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/Lidar.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Communications;
using System.Collections.Generic;

namespace GoBot.Devices
{
    public abstract class Lidar
    {
        private TicksPerSecond _measuresTicker;
        
        protected Position _position;
        protected bool _started;
        protected ConnectionChecker _checker;
        
        public delegate void NewMeasureHandler(List<RealPoint> measure);
        public delegate void FrequencyChangeHandler(double value);

        public event NewMeasureHandler NewMeasure;
        public event FrequencyChangeHandler FrequencyChange;

        public Lidar()
        {
            _measuresTicker = new TicksPerSecond();
            _measuresTicker.ValueChange += OnFrequencyChange;
            _position = new Position();
            _started = false;
            _checker = new ConnectionChecker(null);
            _checker.Start();
        }

        public Position Position { get { return _position; } set { _position = new Position(value); } }
        
        public abstract AngleDelta DeadAngle { get; }

        public ConnectionChecker ConnectionChecker { get { return _checker; } }

        public abstract bool Activated { get; }

        public void StartLoopMeasure()
        {
            if (!_started)
            {
                _measuresTicker.Start();
                _started = true;
                StartLoop();
            }
        }

        public void StopLoopMeasure()
        {
            if (_started)
            {
                _measuresTicker.Stop();
                _started = false;
                StopLoop();
            }
        }

        protected abstract bool StartLoop();
        protected abstract void StopLoop();
        public abstract List<RealPoint> GetPoints();
                
        protected void OnNewMeasure(List<RealPoint> measure)
        {
            _measuresTicker.AddTick();
            _checker.NotifyAlive();
            NewMeasure?.Invoke(measure);
        }

        protected void OnFrequencyChange(double freq)
        {
            FrequencyChange?.Invoke(freq);
        }
    }
}
<file_sep>/GoBot/GoBot/Strategies/StrategyMini.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using GoBot.Movements;
using GoBot.BoardContext;
using Geometry;
using Geometry.Shapes;

namespace GoBot.Strategies
{
    class StrategyMini : Strategy
    {
        public override bool AvoidElements => true;

        protected override void SequenceBegin()
        {
            Robots.MainRobot.UpdateGraph(GameBoard.ObstaclesAll);

            // Sortir de la zonde de départ dès le début sans détection
            Robots.MainRobot.MoveForward(500);

            Position positionCale = new Position(50, new RealPoint(1800, 1800));
            Position positionInit = new Position(0, new RealPoint(500, 500));

            if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
            {
                int tries = 0;
                bool succeed = false;

                do
                {
                    tries++;
                    succeed = Robots.MainRobot.GoToPosition(positionCale);
                } while (!succeed && tries < 3 && Robots.MainRobot.TrajectoryCutOff);// On reessaie, maximum 3 fois si la trajectoire a échoué parce qu'on m'a coupé la route

                if (succeed)
                {
                    // Je suis dans la cale !
                    Robots.MainRobot.PivotLeft(360);
                }
                else
                {
                    // On m'a empeché d'y aller, je vais ailleurs
                    succeed = Robots.MainRobot.GoToPosition(positionInit);
                }
            }
            else
            {
            }
        }

        protected override void SequenceCore()
        {
            // Le petit robot ne fait plus rien après la séquence initiale
            while (IsRunning)
            {
                Robots.MainRobot.Historique.Log("Aucune action à effectuer");
                Thread.Sleep(500);
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/SegmentWithRealPoint.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class SegmentWithRealPoint
    {
        public static bool Contains(Segment containingSegment, RealPoint containedPoint)
        {
            // Vérifie que le point est situé sur la droite
            if (!LineWithRealPoint.Contains((Line)containingSegment, containedPoint))
                return false;
            // Puis qu'il est entre les deux extrémités

            if (containedPoint.X - RealPoint.PRECISION > Math.Max(containingSegment.StartPoint.X, containingSegment.EndPoint.X) ||
                containedPoint.X + RealPoint.PRECISION < Math.Min(containingSegment.StartPoint.X, containingSegment.EndPoint.X) ||
                containedPoint.Y - RealPoint.PRECISION > Math.Max(containingSegment.StartPoint.Y, containingSegment.EndPoint.Y) ||
                containedPoint.Y + RealPoint.PRECISION < Math.Min(containingSegment.StartPoint.Y, containingSegment.EndPoint.Y))
                return false;

            return true;
        }

        public static bool Cross(Segment segment, RealPoint point)
        {
            return Contains(segment, point);
        }

        public static double Distance(Segment segment, RealPoint point)
        {            
            // Le raisonnement est le même que pour la droite cf Droite.Distance

            Line perpendicular = segment.GetPerpendicular(point);
            List<RealPoint> cross = segment.GetCrossingPoints(perpendicular);

            double distance;

            // Seule différence : on teste si l'intersection appartient bien au segment, sinon on retourne la distance avec l'extrémité la plus proche
            if (cross.Count > 0 && segment.Contains(cross[0]))
            {
                distance = point.Distance(cross[0]);
            }
            else
            {
                double distanceDebut = point.Distance(segment.StartPoint);
                double distanceFin = point.Distance(segment.EndPoint);

                distance = Math.Min(distanceDebut, distanceFin);
            }

            return distance;
        }

        public static List<RealPoint> GetCrossingPoints(Segment segment, RealPoint point)
        {
            List<RealPoint> output = new List<RealPoint>();

            if (segment.Contains(point)) output.Add(new RealPoint(point));

            return output;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/CircleWithSegment.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class CircleWithSegment
    {
        public static bool Contains(Circle containingCircle, Segment containedSegment)
        {
            // Pour contenir un Segment il suffit de contenir ses 2 extremités

            return containingCircle.Contains(containedSegment.StartPoint) && containingCircle.Contains(containedSegment.EndPoint);
        }

        public static double Distance(Circle circle, Segment segment)
        {
            // Distance jusqu'au centre du cercle - son rayon (si négatif c'est que ça croise, donc distance 0)

            return Math.Max(0, segment.Distance(circle.Center) - circle.Radius);
        }

        public static bool Cross(Circle circle, Segment segment)
        {
            // Si un segment croise le cercle, c'est que le point de segment le plus proche du centre du cercle est éloigné d'une distance inférieure au rayon

            return segment.Distance(circle.Center) <= circle.Radius && !circle.Contains(segment);
        }

        public static List<RealPoint> GetCrossingPoints(Circle circle, Segment segment)
        {
            // Equation du second degré

            List<RealPoint> intersectsPoints = new List<RealPoint>();
            double dx = segment.EndPoint.X - segment.StartPoint.X;
            double dy = segment.EndPoint.Y - segment.StartPoint.Y;
            double Ox = segment.StartPoint.X - circle.Center.X;
            double Oy = segment.StartPoint.Y - circle.Center.Y;
            double A = dx * dx + dy * dy;
            double B = 2 * (dx * Ox + dy * Oy);
            double C = Ox * Ox + Oy * Oy - circle.Radius * circle.Radius;
            double delta = B * B - 4 * A * C;

            if (delta < 0 + double.Epsilon && delta > 0 - double.Epsilon)
            {
                double t = -B / (2 * A);
                if (t >= 0 && t <= 1)
                    intersectsPoints.Add(new RealPoint(segment.StartPoint.X + t * dx, segment.StartPoint.Y + t * dy));
            }
            else if (delta > 0)
            {
                double t1 = ((-B - Math.Sqrt(delta)) / (2 * A));
                double t2 = ((-B + Math.Sqrt(delta)) / (2 * A));
                if (t1 >= 0 && t1 <= 1)
                    intersectsPoints.Add(new RealPoint(segment.StartPoint.X + t1 * dx, segment.StartPoint.Y + t1 * dy));
                if (t2 >= 0 && t2 <= 1)
                    intersectsPoints.Add(new RealPoint(segment.StartPoint.X + t2 * dx, segment.StartPoint.Y + t2 * dy));
            }

            return intersectsPoints;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/RealPointWithPolygon.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class RealPointWithPolygon
    {
        public static bool Contains(RealPoint containingPoint, Polygon containedPolygon)
        {
            return containedPolygon.Points.TrueForAll(p => p == containingPoint);
        }

        public static bool Cross(RealPoint point, Polygon polygon)
        {
            return polygon.Sides.Exists(s => s.Cross(point));
        }

        public static double Distance(RealPoint point, Polygon polygon)
        {
            if (polygon is PolygonRectangle)
            {
                // Plus rapide que la méthode des polygones
                RealPoint topLeft = polygon.Sides[0].StartPoint;
                RealPoint topRight = polygon.Sides[1].StartPoint;
                RealPoint bottomRight = polygon.Sides[2].StartPoint;
                RealPoint bottomLeft = polygon.Sides[3].StartPoint;

                double distance;

                if (point.X < topLeft.X)
                {
                    if (point.Y < topLeft.Y)
                        distance = point.Distance(topLeft);
                    else if (point.Y > bottomRight.Y)
                        distance = point.Distance(bottomLeft);
                    else
                        distance = topLeft.X - point.X;
                }
                else if (point.X > bottomRight.X)
                {
                    if (point.Y < topLeft.Y)
                        distance = point.Distance(topRight);
                    else if (point.Y > bottomRight.Y)
                        distance = point.Distance(bottomRight);
                    else
                        distance = point.X - topRight.X;
                }
                else
                {
                    if (point.Y < topLeft.Y)
                        distance = topLeft.Y - point.Y;
                    else if (point.Y > bottomRight.Y)
                        distance = point.Y - bottomLeft.Y;
                    else
                        distance = 0;
                }

                return distance;
            }

            // Distance jusqu'au segment le plus proche
            double minDistance = double.MaxValue;

            foreach (Segment s in polygon.Sides)
                minDistance = Math.Min(s.Distance(point), minDistance);

            return minDistance;
        }

        public static List<RealPoint> GetCrossingPoints(RealPoint point, Polygon polygon)
        {
            List<RealPoint> output = new List<RealPoint>();

            if (Cross(point, polygon)) 
                output.Add(new RealPoint(point));

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/CAN/CanFrameDecoder.cs
﻿using System;
using System.Collections.Generic;

namespace GoBot.Communications.CAN
{
    static class CanFrameDecoder
    {
        public static String Decode(Frame trame)
        {
            return GetMessage(trame);
        }

        /// <summary>
        /// Récupère le message explicite associé à une fonction du protocole.
        /// Les paramètres sont signalés par des accolades indiquant la position dans la trame des octets correspondant à la valeur du paramètre.
        /// Si les paramètres sont fournis, leur valeur remplacent les paramètres entre accolades.
        /// </summary>
        /// <param name="function"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static String GetMessage(CanFrameFunction function, List<uint> parameters = null)
        {
            String output = "";

            switch (function)
            {
                case CanFrameFunction.PositionAsk:
                    output = "Demande de position servo {ServoID}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                    }
                    break;
                case CanFrameFunction.PositionResponse:
                    output = "Retour de position servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.PositionSet:
                    output = "Envoi de position servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.PositionMinAsk:
                    output = "Demande de position min servo {ServoID}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                    }
                    break;
                case CanFrameFunction.PositionMinResponse:
                    output = "Retour de position min servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.PositionMinSet:
                    output = "Envoi de position min servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.PositionMaxAsk:
                    output = "Demande de position max servo {ServoID}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                    }
                    break;
                case CanFrameFunction.PositionMaxResponse:
                    output = "Retour de position max servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.PositionMaxSet:
                    output = "Envoi de position max servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.SpeedMaxAsk:
                    output = "Demande de vitesse servo {ServoID}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                    }
                    break;
                case CanFrameFunction.SpeedMaxResponse:
                    output = "Retour de vitesse servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.SpeedMaxSet:
                    output = "Envoi de vitesse servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.TorqueMaxAsk:
                    output = "Demande de couple max servo {ServoID}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                    }
                    break;
                case CanFrameFunction.TorqueMaxResponse:
                    output = "Retour de couple max servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.TorqueMaxSet:
                    output = "Envoi de couple max servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.TorqueCurrentAsk:
                    output = "Demande de couple actuel servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.TorqueCurrentResponse:
                    output = "Retour de couple actuel servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.AccelerationAsk:
                    output = "Demande de l'acceleration servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.AccelerationResponse:
                    output = "Retour de l'acceleration servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.AccelerationSet:
                    output = "Envoi de l'acceleration servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.TargetSet:
                    output = "Déplacement servo {ServoID} : {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.TrajectorySet:
                    output = "Déplacement servo {ServoID} : Pos = {4-5}; Vit = {6-7}; Accel = {8-9}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.DisableOutput:
                    output = "Disable servo {ServoID}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                    }
                    break;
                case CanFrameFunction.TorqueAlert:
                    output = "Alerte couple dépassé (1sec) {ServoID} : Couple = {4-5}";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, ((ServomoteurID)parameters[0]).ToString());
                        output = ReplaceParam(output, parameters[1].ToString());
                    }
                    break;
                case CanFrameFunction.Debug:
                    output = "Debug";
                    break;
                case CanFrameFunction.DebugAsk:
                    output = "Demande valeur debug";
                    break;
                case CanFrameFunction.DebugResponse:
                    output = "Retour valeur debug";
                    break;
                case CanFrameFunction.TestConnection:
                    output = "Test connexion";
                    break;
                case CanFrameFunction.BatterieVoltage:
                    output = "Retour alimentation : {3-4}V {5-6}A";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, (parameters[0] / 1000.0).ToString("0.00"));
                        output = ReplaceParam(output, (parameters[1] / 1000.0).ToString("0.00"));
                    }
                    break;
                case CanFrameFunction.Buzzer:
                    output = "Buzzer : {3-4}Hz {5-6}mS";
                    if (parameters != null)
                    {
                        output = ReplaceParam(output, parameters[0].ToString("0"));
                        output = ReplaceParam(output, parameters[1].ToString("0"));
                    }
                    break;
                default:
                    output = "Inconnu";
                    break;
            }

            return output;
        }

        /// <summary>
        /// Retourne le message contenu dans une trame dans un texte explicite avec les paramètres décodés
        /// </summary>
        /// <param name="frame">Trame à décoder</param>
        /// <returns>Message sur le contnu de la trame</returns>
        public static String GetMessage(Frame frame)
        {
            String output = "";

            output = GetMessage(CanFrameFactory.ExtractFunction(frame));
            output = GetMessage(CanFrameFactory.ExtractFunction(frame), GetParameters(output, frame));

            return output;
        }

        /// <summary>
        /// Retourne la liste des valeurs des paramètres d'une trame selon le format donné
        /// </summary>
        /// <param name="format">Format du décodage sous la forme "Message valeur = {0-1}" équivaut à un paramètre situé sur les 1er et 2eme octets.</param>
        /// <param name="frame">Trame dans laquelle lire les valeurs</param>
        /// <returns>Liste des valeurs brutes des paramètres</returns>
        private static List<uint> GetParameters(String format, Frame frame)
        {
            List<uint> parameters = new List<uint>();
            String subParameter;

            if (format.Contains("{ServoID}"))
            {
                parameters.Add((uint)CanFrameFactory.ExtractServomoteurID(frame));
                format = format.Replace("{ServoID}", "");
            }

            for (int iChar = 0; iChar < format.Length; iChar++)
            {
                if (format[iChar] == '{')
                {
                    iChar++;
                    parameters.Add(0);

                    while (format[iChar] != '}')
                    {
                        subParameter = "";
                        while (format[iChar] != '-' && format[iChar] != '}')
                        {
                            subParameter += format[iChar];
                            iChar++;
                        }
                        parameters[parameters.Count - 1] = parameters[parameters.Count - 1] * 256 + frame[int.Parse(subParameter)];
                        if (format[iChar] == '-')
                            iChar++;
                    }
                }
            }

            return parameters;
        }

        /// <summary>
        /// Remplace le premier paramètre entre accolades par le message donné
        /// </summary>
        /// <param name="input">Chaine contenant le message entier paramétré</param>
        /// <param name="msg">Message à écrire à la place du premier paramètre</param>
        /// <returns>Chaine contenant le message avec le paramètre remplacé</returns>
        private static String ReplaceParam(String input, String msg)
        {
            int iStart = input.IndexOf("{");
            int iEnd = input.IndexOf("}");

            String param = input.Substring(iStart, iEnd - iStart + 1);
            String output = input.Replace(param, msg);

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/Utils/DetectionAnalyzer.cs
﻿using Geometry.Shapes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Utils
{
    class DetectionAnalyzer
    {
        private List<IShape> _obstacles;
        private IShape _bounds;
        private double _precision;

        public DetectionAnalyzer()
        {
            _obstacles = new List<IShape>();
            _bounds = new PolygonRectangle(new RealPoint(0, 0), 3000, 2000);
            _precision = 10;
        }

        public List<RealPoint> KeepInsideBounds(List<RealPoint> detection)
        {
            return detection.GetPointsInside(_bounds).ToList();
        }

        public List<RealPoint> RemoveObstacles(List<RealPoint> detection)
        {
            return detection.Where(o => _obstacles.Min(s => s.Distance(o)) < _precision).ToList();
        }
    }
}
<file_sep>/GoBot/GeometryTester/TestCircleWithCircle.cs
﻿using Geometry.Shapes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GeometryTester
{
    [TestClass]
    public class TestCircleWithCircle
    {
        #region Contains

        [TestMethod]
        public void TestContainsStandard()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = new Circle(new RealPoint(20, 5), 80);
            Circle c3 = new Circle(new RealPoint(100, 220), 20);

            Assert.IsFalse(c1.Contains(c2));
            Assert.IsFalse(c1.Contains(c3));

            Assert.IsTrue(c2.Contains(c1));
            Assert.IsFalse(c2.Contains(c3));

            Assert.IsFalse(c3.Contains(c1));
            Assert.IsFalse(c3.Contains(c2));
        }

        [TestMethod]
        public void TestContainsPositionZero()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 30);
            Circle c2 = new Circle(new RealPoint(0, 0), 80);
            Circle c3 = new Circle(new RealPoint(100, 220), 20);

            Assert.IsFalse(c1.Contains(c2));
            Assert.IsFalse(c1.Contains(c3));

            Assert.IsTrue(c2.Contains(c1));
            Assert.IsFalse(c2.Contains(c3));

            Assert.IsFalse(c3.Contains(c1));
            Assert.IsFalse(c3.Contains(c2));
        }

        [TestMethod]
        public void TestContainsRadiusZero()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 0);
            Circle c2 = new Circle(new RealPoint(20, 5), 80);
            Circle c3 = new Circle(new RealPoint(100, 220), 20);

            Assert.IsFalse(c1.Contains(c2));
            Assert.IsFalse(c1.Contains(c3));

            Assert.IsTrue(c2.Contains(c1));
            Assert.IsFalse(c3.Contains(c1));
        }

        [TestMethod]
        public void TestContainsPositionNeg()
        {
            Circle c1 = new Circle(new RealPoint(-10, -20), 30);
            Circle c2 = new Circle(new RealPoint(-20, -5), 80);
            Circle c3 = new Circle(new RealPoint(-100, -220), 20);

            Assert.IsFalse(c1.Contains(c2));
            Assert.IsFalse(c1.Contains(c3));

            Assert.IsTrue(c2.Contains(c1));
            Assert.IsFalse(c2.Contains(c3));

            Assert.IsFalse(c3.Contains(c1));
            Assert.IsFalse(c3.Contains(c2));
        }

        [TestMethod]
        public void TestContainsSameX()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = new Circle(new RealPoint(10, 5), 80);

            Assert.IsFalse(c1.Contains(c2));
            Assert.IsTrue(c2.Contains(c1));
        }

        [TestMethod]
        public void TestContainsSameY()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = new Circle(new RealPoint(20, 20), 80);

            Assert.IsFalse(c1.Contains(c2));
            Assert.IsTrue(c2.Contains(c1));
        }

        [TestMethod]
        public void TestContainsSameCenter()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            Circle c2 = new Circle(new RealPoint(10, 20), 80);

            Assert.IsFalse(c1.Contains(c2));
            Assert.IsTrue(c2.Contains(c1));
        }

        [TestMethod]
        public void TestContainsSameCenterZero()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 30);
            Circle c2 = new Circle(new RealPoint(0, 0), 80);

            Assert.IsFalse(c1.Contains(c2));
            Assert.IsTrue(c2.Contains(c1));
        }

        #endregion

        #region Cross

        [TestMethod]
        public void TestCrossStandard()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 50);
            Circle c2 = new Circle(new RealPoint(55, 35), 40);
            Circle c3 = new Circle(new RealPoint(150, 20), 25);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsFalse(c1.Cross(c3));

            Assert.IsTrue(c2.Cross(c1));
            Assert.IsFalse(c2.Cross(c3));

            Assert.IsFalse(c3.Cross(c1));
            Assert.IsFalse(c3.Cross(c2));
        }

        [TestMethod]
        public void TestCrossSameX()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 20);
            Circle c2 = new Circle(new RealPoint(20, 35), 15);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossSameY()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 20);
            Circle c2 = new Circle(new RealPoint(35, 30), 15);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossAdjacentX()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 20);
            Circle c2 = new Circle(new RealPoint(60, 30), 20);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossAdjacentY()
        {
            Circle c1 = new Circle(new RealPoint(30, 20), 20);
            Circle c2 = new Circle(new RealPoint(30, 60), 20);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossAdjacent()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(10, 10), 2.5);
            Circle c2 = new Circle(new RealPoint(13, 14), 2.5);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossRadiusZeroBoth()
        {
            Circle c1 = new Circle(new RealPoint(10, 10), 0);
            Circle c2 = new Circle(new RealPoint(10, 10), 0);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossRadiusZeroOnCircle()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(10, 10), 5);
            Circle c2 = new Circle(new RealPoint(13, 14), 0);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossRadiusZeroOnCenter()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(3, 4), 5);
            Circle c2 = new Circle(new RealPoint(0, 0), 0);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossSuperposed()
        {
            Circle c1 = new Circle(new RealPoint(5, 5), 5);
            Circle c2 = new Circle(new RealPoint(5, 5), 5);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));
        }

        [TestMethod]
        public void TestCrossPrecision()
        {
            Circle c1 = new Circle(new RealPoint(5, 5), 5);
            Circle c2 = new Circle(new RealPoint(5, 15), 5 + RealPoint.PRECISION);
            Circle c3 = new Circle(new RealPoint(5, 15), 5 - RealPoint.PRECISION);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsTrue(c2.Cross(c1));

            Assert.IsFalse(c1.Cross(c3));
            Assert.IsFalse(c3.Cross(c1));
        }

        [TestMethod]
        public void TestCrossNeg()
        {
            Circle c1 = new Circle(new RealPoint(-20, -30), 50);
            Circle c2 = new Circle(new RealPoint(-55, -35), 40);
            Circle c3 = new Circle(new RealPoint(-150, -20), 25);
            Circle c4 = new Circle(new RealPoint(150, 20), 25);

            Assert.IsTrue(c1.Cross(c2));
            Assert.IsFalse(c1.Cross(c3));
            Assert.IsFalse(c4.Cross(c3));

            Assert.IsTrue(c2.Cross(c1));
            Assert.IsFalse(c2.Cross(c3));
            Assert.IsFalse(c4.Cross(c3));

            Assert.IsFalse(c3.Cross(c1));
            Assert.IsFalse(c3.Cross(c2));
            Assert.IsFalse(c3.Cross(c4));
        }


        #endregion

        #region Distance

        [TestMethod]
        public void TestDistanceStandard()
        {
            Circle c1 = new Circle(new RealPoint(3, 4), 1);
            Circle c2 = new Circle(new RealPoint(6, 10), 1);

            Assert.AreEqual(4.71, c1.Distance(c2), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceSame()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 20);
            Circle c2 = new Circle(new RealPoint(20, 30), 20);

            Assert.AreEqual(0, c1.Distance(c2), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceAdjacent()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(10, 10), 2.5);
            Circle c2 = new Circle(new RealPoint(13, 14), 2.5);

            Assert.AreEqual(0, c1.Distance(c2), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceRadiusZeroBoth()
        {
            Circle c1 = new Circle(new RealPoint(10, 10), 0);
            Circle c2 = new Circle(new RealPoint(10, 10), 0);

            Assert.AreEqual(0, c1.Distance(c2), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceRadiusZeroOnCircle()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(10, 10), 5);
            Circle c2 = new Circle(new RealPoint(13, 14), 0);

            Assert.AreEqual(0, c1.Distance(c2), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceRadiusZeroOnCenter()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(3, 4), 5);
            Circle c2 = new Circle(new RealPoint(0, 0), 0);

            Assert.AreEqual(0, c1.Distance(c2), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceCrossing()
        {
            Circle c1 = new Circle(new RealPoint(5, 6), 5);
            Circle c2 = new Circle(new RealPoint(6, 8), 5);

            Assert.AreEqual(0, c1.Distance(c2), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceCenterNeg()
        {
            Circle c1 = new Circle(new RealPoint(-20, -30), 5);
            Circle c2 = new Circle(new RealPoint(-55, -35), 4);

            Assert.AreEqual(26.35, c1.Distance(c2), RealPoint.PRECISION);
        }

        #endregion

        #region CrossingPoints

        [TestMethod]
        public void TestCrossingPointsStandard()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 50);
            Circle c2 = new Circle(new RealPoint(55, 35), 40);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(2, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(55.71, -4.99)));
            Assert.IsTrue(pts.Contains(new RealPoint(44.49, 73.59)));
        }

        [TestMethod]
        public void TestCrossingPointsSameX()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 20);
            Circle c2 = new Circle(new RealPoint(20, 35), 20);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(2, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(39.84, 32.5)));
            Assert.IsTrue(pts.Contains(new RealPoint(0.16, 32.5)));
        }

        [TestMethod]
        public void TestCrossingPointsSameY()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 20);
            Circle c2 = new Circle(new RealPoint(35, 30), 15);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(2, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(33.33, 15.09)));
            Assert.IsTrue(pts.Contains(new RealPoint(33.33, 44.91)));
        }

        [TestMethod]
        public void TestCrossingPointsAdjacentX()
        {
            Circle c1 = new Circle(new RealPoint(20, 30), 20);
            Circle c2 = new Circle(new RealPoint(60, 30), 20);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(1, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(40,30)));
        }

        [TestMethod]
        public void TestCrossingPointsAdjacentY()
        {
            Circle c1 = new Circle(new RealPoint(30, 20), 20);
            Circle c2 = new Circle(new RealPoint(30, 60), 20);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(1, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(30, 40)));
        }

        [TestMethod]
        public void TestCrossingPointsAdjacent()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(10, 10), 2.5);
            Circle c2 = new Circle(new RealPoint(13, 14), 2.5);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(1, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(11.5,12)));
        }

        [TestMethod]
        public void TestCrossingPointsRadiusZeroBoth()
        {
            Circle c1 = new Circle(new RealPoint(10, 10), 0);
            Circle c2 = new Circle(new RealPoint(10, 10), 0);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(1, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(10,10)));
        }

        [TestMethod]
        public void TestCrossingPointsRadiusZeroOnCircle()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(10, 10), 5);
            Circle c2 = new Circle(new RealPoint(13, 14), 0);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(1, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(13, 14)));
        }

        [TestMethod]
        public void TestCrossingPointsRadiusZeroOnCenter()
        {
            // 3² + 4² = 5², très pratique

            Circle c1 = new Circle(new RealPoint(3, 4), 5);
            Circle c2 = new Circle(new RealPoint(0, 0), 0);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(1, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(0, 0)));
        }

        [TestMethod]
        public void TestCrossingPointsSuperposed()
        {
            // Cas un peu particulier, les cercles se superposent donc se croisent, mais on ne calcule pas de points de croisement car il y en une infinité

            Circle c1 = new Circle(new RealPoint(5, 5), 5);
            Circle c2 = new Circle(new RealPoint(5, 5), 5);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(0, pts.Count);
        }

        [TestMethod]
        public void TestCrossingPointsNeg()
        {
            Circle c1 = new Circle(new RealPoint(-20, -30), 50);
            Circle c2 = new Circle(new RealPoint(-55, -35), 40);

            List<RealPoint> pts = c1.GetCrossingPoints(c2);

            Assert.AreEqual(2, pts.Count);
            Assert.IsTrue(pts.Contains(new RealPoint(-44.49, -73.59)));
            Assert.IsTrue(pts.Contains(new RealPoint(-55.71, 4.99)));
        }

        #endregion
    }
}<file_sep>/GoBot/Extensions/ControlExtensions.cs
﻿using System;
using System.Windows.Forms;

internal static class ControlExtensions
{
    public static void InvokeAuto(this Control control, Action action)
    {
        if (control.InvokeRequired)
            control.Invoke((MethodInvoker)(() => action()));
        else
            action.Invoke();
    }

    public static void InvokeAuto(this UserControl control, Action action)
    {
        if (control.InvokeRequired)
            control.Invoke((MethodInvoker)(() => action()));
        else
            action.Invoke();
    }
}<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/PolygonWithPolygon.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class PolygonWithPolygon
    {
        public static bool Contains(Polygon containingPolygon, Polygon containedPolygon)
        {            
            // Il suffit de contenir tous les segments du polygone testé
            return containedPolygon.Sides.TrueForAll(s => PolygonWithSegment.Contains(containingPolygon, s));
        }

        public static bool Cross(Polygon polygon1, Polygon polygon2)
        {
            // Si un des segments du premier polygone croise le second

            return polygon1.Sides.Exists(s => SegmentWithPolygon.Cross(s, polygon2));
        }

        public static double Distance(Polygon polygon1, Polygon polygon2)
        {
            double minDistance = 0;

            // Si les polygones se croisent ou se contiennent, la distance est nulle
            if (!PolygonWithPolygon.Cross(polygon1, polygon2) && !PolygonWithPolygon.Contains(polygon1, polygon2) && !PolygonWithPolygon.Contains(polygon2, polygon1))
            {
                minDistance = double.MaxValue;

                foreach (Segment s1 in polygon1.Sides)
                    foreach (Segment s2 in polygon2.Sides)
                        minDistance = Math.Min(minDistance, s1.Distance(s2));
            }

            return minDistance;
        }

        public static List<RealPoint> GetCrossingPoints(Polygon polygon1, Polygon polygon2)
        {
            // Croisement des segments du premier polygone avec le second

            return polygon1.Sides.SelectMany(s => SegmentWithPolygon.GetCrossingPoints(s, polygon2)).ToList();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Forms/FenGoBot.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using GoBot.IHM;
using GoBot.Communications;
using System.IO;
using GoBot.Threading;
using GoBot.Devices;
using GoBot.BoardContext;
using System.Diagnostics;
using Geometry.Shapes;
using GoBot.Communications.UDP;
using GoBot.IHM.Forms;

namespace GoBot
{
    public partial class FenGoBot : Form
    {
        public static FenGoBot Instance { get; private set; }
        private System.Windows.Forms.Timer timerSauvegarde;
        private List<TabPage> _pagesInWindow;

        /// <summary>
        /// Anti scintillement
        /// </summary>
        //protected override CreateParams CreateParams
        //{
        //    get
        //    {
        //        CreateParams cp = base.CreateParams;
        //        cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
        //        return cp;
        //    }
        //}

        public FenGoBot(string[] args)
        {
            InitializeComponent();
            timerSauvegarde = new Timer();
            timerSauvegarde.Interval = 10000;
            timerSauvegarde.Tick += timerSauvegarde_Tick;
            timerSauvegarde.Start();

            if (!Execution.DesignMode)
            {
                panelGrosRobot.Init();

                if (Screen.PrimaryScreen.Bounds.Width <= 1024)
                {
                    WindowState = FormWindowState.Maximized;
                    FormBorderStyle = FormBorderStyle.None;
                    tabControl.SelectedTab = tabPandaNew;
                    tabControl.Location = new Point(-14, -50);
                    tabControl.Width += 100;
                    tabControl.Height += 100;
                    panelConnexions.Visible = false;
                    lblSimulation.Visible = false;
                    switchBoutonSimu.Visible = false;
                    btnFenetre.Visible = false;
                }
                else
                {
                    WindowState = FormWindowState.Normal;
                    FormBorderStyle = FormBorderStyle.FixedSingle;
                }

                switchBoutonSimu.Value = Robots.Simulation;

                tabPrecedent = new Dictionary<TabPage, TabPage>();
                tabPrecedent.Add(tabControl.TabPages[0], null);
                for (int i = 1; i < tabControl.Controls.Count; i++)
                    tabPrecedent.Add(tabControl.TabPages[i], (tabControl.TabPages[i - 1]));

                List<String> fichiersElog = new List<string>();
                List<String> fichiersTlog = new List<string>();

                foreach (String chaine in args)
                {
                    if (Path.GetExtension(chaine) == ".elog")
                        fichiersElog.Add(chaine);
                    if (Path.GetExtension(chaine) == FramesLog.FileExtension)
                        fichiersTlog.Add(chaine);
                }

                if (fichiersElog.Count > 0)
                {
                    foreach (String fichier in fichiersElog)
                        panelLogsEvents.ChargerLog(fichier);
                    panelLogsEvents.Afficher();

                    if (fichiersTlog.Count == 0)
                    {
                        tabControl.SelectedTab = tabLogs;
                        tabControlLogs.SelectedTab = tabLogEvent;
                    }
                }

                if (fichiersTlog.Count > 0)
                {
                    foreach (String fichier in fichiersTlog)
                        pnlLogFrames.LoadLog(fichier);
                    pnlLogFrames.DisplayLog();

                    tabControl.SelectedTab = tabLogs;
                    tabControlLogs.SelectedTab = tabLogUDP;
                }

                Instance = this;

                GameBoard.MyColor = GameBoard.ColorLeftBlue;

                Connections.ConnectionMove.SendMessage(UdpFrameFactory.DemandeCouleurEquipe());
            }

            Connections.StartConnections();

            SplashScreen.CloseSplash(-1);

            pnlPower.StartGraph();

            pnlNumericIO.SetBoard(Board.RecIO);
            pnlNumericMove.SetBoard(Board.RecMove);

            panelTable.StartDisplay();

            _pagesInWindow = new List<TabPage>();

            this.Text = "GoBot 2020 - Beta";
        }

        public void ChargerReplay(String fichier)
        {
            if (Path.GetExtension(fichier) == FramesLog.FileExtension)
            {
                pnlLogFrames.Clear();
                pnlLogFrames.LoadLog(fichier);
                pnlLogFrames.DisplayLog();
                tabControl.SelectedTab = tabLogs;
                tabControlLogs.SelectedTab = tabLogUDP;
            }
            else if (Path.GetExtension(fichier) == ".elog")
            {
                panelLogsEvents.Clear();
                panelLogsEvents.ChargerLog(fichier);
                panelLogsEvents.Afficher();
                tabControl.SelectedTab = tabLogs;
                tabControlLogs.SelectedTab = tabLogEvent;
            }
        }

        void timerSauvegarde_Tick(object sender, EventArgs e)
        {
            SauverLogs();
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            this.Hide();

            if (!ThreadManager.ExitAll())
            {
                Console.WriteLine("Tous les threads ne se sont pas terminés : suicide de l'application.");
                Environment.Exit(0);
            }

            Execution.Shutdown = true;

            Config.Save();
            SauverLogs();

            if (GameBoard.Strategy != null && GameBoard.Strategy.IsRunning)
                GameBoard.Strategy.Stop();

            AllDevices.Close();

            base.OnClosing(e);
        }

        private void SauverLogs()
        {
            DateTime debut = DateTime.Now;

            foreach (Connection conn in Connections.AllConnections)
                conn.Archives.Export(Config.PathData + "/Logs/" + Execution.LaunchStartString + "/" + Connections.GetUDPBoardByConnection(conn).ToString() + FramesLog.FileExtension);

            Robots.MainRobot.Historique.Sauvegarder(Config.PathData + "/Logs/" + Execution.LaunchStartString + "/ActionsGros.elog");
        }

        private void FenGoBot_Load(object sender, EventArgs e)
        {
            this.Activate();
        }

        private void switchBoutonSimu_ValueChanged(object sender, bool value)
        {
            Robots.EnableSimulation(value);
            panelGrosRobot.Init();

            if (value)
                AllDevices.InitSimu();
            else
                AllDevices.Init();

            AllDevices.SetRobotPosition(Robots.MainRobot.Position);
        }

        private void buttonFenetre_Click(object sender, EventArgs e)
        {
            TabControl tab = new TabControl();
            tab.Height = tabControl.Height;
            tab.Width = tabControl.Width;
            _pagesInWindow.Add(tabControl.SelectedTab);
            tab.TabPages.Add(tabControl.SelectedTab);
            Fenetre fen = new Fenetre(tab);
            fen.Show();
            fen.FormClosing += fen_FormClosing;
        }

        private Dictionary<TabPage, TabPage> tabPrecedent;

        void fen_FormClosing(object sender, FormClosingEventArgs e)
        {
            Fenetre fen = (Fenetre)sender;
            TabControl tab = (TabControl)fen.Control;
            TabPage page = tab.TabPages[0];

            _pagesInWindow.Remove(page);

            TabPage tabPrec = tabPrecedent[page];
            bool trouve = false;
            if (tabPrec == null)
                tabControl.TabPages.Insert(0, page);
            else
            {
                while (!trouve && tabPrec != null)
                {
                    for (int i = 0; i < tabControl.TabPages.Count; i++)
                        if (tabControl.TabPages[i] == tabPrec)
                        {
                            trouve = true;
                            tabControl.TabPages.Insert(i + 1, page);
                            break;
                        }

                    if (!trouve)
                        tabPrec = tabPrecedent[tabPrec];

                    if (tabPrec == null)
                        tabControl.TabPages.Insert(0, page);
                }
            }
        }

        private void btnDebug_Click(object sender, EventArgs e)
        {
            DebugLidar fen = new DebugLidar();
            fen.ShowDialog();
        }

        private void pageCheckSpeed1_Load(object sender, EventArgs e)
        {

        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/CircleWithRealPoint.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    public static class CircleWithRealPoint
    {
        public static bool Contains(Circle containingCircle, RealPoint containedPoint)
        {
            // Pour contenir un point, celui si se trouve à une distance inférieure au rayon du centre

            return containedPoint.Distance(containingCircle.Center) <= containingCircle.Radius;
        }

        public static double Distance(Circle circle, RealPoint point)
        {
            // C'est la distance entre le centre du cercle et le point moins le rayon du cercle

            return Math.Max(0, point.Distance(circle.Center) - circle.Radius);
        }

        public static bool Cross(Circle circle, RealPoint point)
        {
            // Pour qu'un cercle croise un point en c'est que le point est sur son contour donc à une distance égale au rayon de son centre.

            return Math.Abs(circle.Center.Distance(point) - circle.Radius) < RealPoint.PRECISION;
        }
        
        public static List<RealPoint> GetCrossingPoints(Circle circle, RealPoint point)
        {
            // Le seul point de croisement d'un point et d'un cercle, c'est le point lui même si il croise le cercle.
            
            return circle.Cross(point) ? new List<RealPoint>(){ new RealPoint(point) } : new List<RealPoint>();
        }
    }
}
<file_sep>/GoBot/GeometryTester/TestCircleWithRealPoint.cs
﻿using Geometry.Shapes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GeometryTester
{
    [TestClass]
    public class TestCircleWithRealPoint
    {
        #region Contains

        [TestMethod]
        public void TestContainsStandard()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(11, 21);
            RealPoint p2 = new RealPoint(11, 60);
            RealPoint p3 = new RealPoint(11, -60);

            Assert.IsTrue(c1.Contains(p1));
            Assert.IsFalse(c1.Contains(p2));
            Assert.IsFalse(c1.Contains(p3));
        }

        [TestMethod]
        public void TestContainsCenter()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10, 20);

            Assert.IsTrue(c1.Contains(p1));
        }

        [TestMethod]
        public void TestContainsBorderOrtho()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10, 50);
            RealPoint p2 = new RealPoint(10, -10);
            RealPoint p3 = new RealPoint(-20, 20);
            RealPoint p4 = new RealPoint(40, 20);

            Assert.IsTrue(c1.Contains(p1));
            Assert.IsTrue(c1.Contains(p2));
            Assert.IsTrue(c1.Contains(p3));
            Assert.IsTrue(c1.Contains(p4));
        }

        [TestMethod]
        public void TestContainsBorderDiagonal()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10 + Math.Sin(Math.PI / 3) * 30, 20 + Math.Cos(Math.PI / 3) * 30);

            Assert.IsTrue(c1.Contains(p1));
        }

        [TestMethod]
        public void TestContainsOrigin()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 1);
            RealPoint p1 = c1.Center;

            Assert.IsTrue(c1.Contains(p1));
        }

        [TestMethod]
        public void TestContainsOriginDot()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 0);
            RealPoint p1 = c1.Center;

            Assert.IsTrue(p1.Contains(c1));
        }

        [TestMethod]
        public void TestContainsDot()
        {
            Circle c1 = new Circle(new RealPoint(42, 42), 0);
            RealPoint p1 = c1.Center;

            Assert.IsTrue(p1.Contains(c1));
        }

        [TestMethod]
        public void TestContainsCircle()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 1);
            RealPoint p1 = c1.Center;

            Assert.IsFalse(p1.Contains(c1));
        }

        #endregion

        #region Cross
        [TestMethod]

        public void TestCrossCenter()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10, 20);

            Assert.IsFalse(c1.Cross(p1));
        }

        [TestMethod]
        public void TestCrossBorderOrtho()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10, 50);
            RealPoint p2 = new RealPoint(10, -10);
            RealPoint p3 = new RealPoint(-20, 20);
            RealPoint p4 = new RealPoint(40, 20);

            Assert.IsTrue(c1.Cross(p1));
            Assert.IsTrue(c1.Cross(p2));
            Assert.IsTrue(c1.Cross(p3));
            Assert.IsTrue(c1.Cross(p4));
        }

        [TestMethod]
        public void TestCrossBorderDiagonal()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10 + Math.Sin(Math.PI / 3) * 30, 20 + Math.Cos(Math.PI / 3) * 30);

            Assert.IsTrue(c1.Cross(p1));
        }

        [TestMethod]
        public void TestCrossOrigin()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 1);
            RealPoint p1 = c1.Center;

            Assert.IsFalse(c1.Cross(p1));
        }

        [TestMethod]
        public void TestCrossOriginDot()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 0);
            RealPoint p1 = c1.Center;

            Assert.IsTrue(p1.Cross(c1));
        }

        [TestMethod]
        public void TestCrossDot()
        {
            Circle c1 = new Circle(new RealPoint(42, 42), 0);
            RealPoint p1 = c1.Center;

            Assert.IsTrue(p1.Cross(c1));
        }

        [TestMethod]
        public void TestCrossCircle()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 1);
            RealPoint p1 = c1.Center;

            Assert.IsFalse(p1.Cross(c1));
        }

        #endregion

        #region CrossingPoints
        [TestMethod]

        public void TestCrossingPointsCenter()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10, 20);

            List<RealPoint> points = c1.GetCrossingPoints(p1);
            Assert.AreEqual(0, points.Count);
        }

        [TestMethod]
        public void TestCrossingPointsBorderOrtho()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10, 50);
            RealPoint p2 = new RealPoint(10, -10);
            RealPoint p3 = new RealPoint(-20, 20);
            RealPoint p4 = new RealPoint(40, 20);

            List<RealPoint> points1 = c1.GetCrossingPoints(p1);
            List<RealPoint> points2 = c1.GetCrossingPoints(p2);
            List<RealPoint> points3 = c1.GetCrossingPoints(p3);
            List<RealPoint> points4 = c1.GetCrossingPoints(p4);

            Assert.AreEqual(1, points1.Count);
            Assert.AreEqual(1, points2.Count);
            Assert.AreEqual(1, points3.Count);
            Assert.AreEqual(1, points4.Count);

            Assert.AreEqual(points1[0], p1);
            Assert.AreEqual(points2[0], p2);
            Assert.AreEqual(points3[0], p3);
            Assert.AreEqual(points4[0], p4);
        }

        [TestMethod]
        public void TestCrossingPointsBorderDiagonal()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10 + Math.Sin(Math.PI / 3) * 30, 20 + Math.Cos(Math.PI / 3) * 30);

            List<RealPoint> points1 = c1.GetCrossingPoints(p1);
            Assert.AreEqual(1, points1.Count);
            Assert.AreEqual(points1[0], p1);
        }

        [TestMethod]
        public void TestCrossingPointsOrigin()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 1);
            RealPoint p1 = c1.Center;

            List<RealPoint> points1 = c1.GetCrossingPoints(p1);
            Assert.AreEqual(0, points1.Count);
        }

        [TestMethod]
        public void TestCrossingPointsOriginDot()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 0);
            RealPoint p1 = c1.Center;

            List<RealPoint> points1 = c1.GetCrossingPoints(p1);
            Assert.AreEqual(1, points1.Count);
            Assert.AreEqual(points1[0], p1);
        }

        [TestMethod]
        public void TestCrossingPointsDot()
        {
            Circle c1 = new Circle(new RealPoint(42, 42), 0);
            RealPoint p1 = c1.Center;

            List<RealPoint> points1 = c1.GetCrossingPoints(p1);
            Assert.AreEqual(1, points1.Count);
            Assert.AreEqual(points1[0], p1);
        }

        [TestMethod]
        public void TestCrossingPointsCircle()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 1);
            RealPoint p1 = c1.Center;

            List<RealPoint> points1 = c1.GetCrossingPoints(p1);
            Assert.AreEqual(0, points1.Count);
        }
        #endregion

        #region Distance

        [TestMethod]
        public void TestDistanceStandard()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(11, 21);
            RealPoint p2 = new RealPoint(11, 60);
            RealPoint p3 = new RealPoint(11, -60);

            Assert.AreEqual(0, c1.Distance(p1), RealPoint.PRECISION);
            Assert.AreEqual(10.0125, c1.Distance(p2), RealPoint.PRECISION);
            Assert.AreEqual(50.0062, c1.Distance(p3), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceCenter()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10, 20);

            Assert.AreEqual(0, c1.Distance(p1), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceBorderOrtho()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10, 50);
            RealPoint p2 = new RealPoint(10, -10);
            RealPoint p3 = new RealPoint(-20, 20);
            RealPoint p4 = new RealPoint(40, 20);

            Assert.AreEqual(0, c1.Distance(p1), RealPoint.PRECISION);
            Assert.AreEqual(0, c1.Distance(p2), RealPoint.PRECISION);
            Assert.AreEqual(0, c1.Distance(p3), RealPoint.PRECISION);
            Assert.AreEqual(0, c1.Distance(p4), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceBorderDiagonal()
        {
            Circle c1 = new Circle(new RealPoint(10, 20), 30);
            RealPoint p1 = new RealPoint(10 + Math.Sin(Math.PI / 3) * 30, 20 + Math.Cos(Math.PI / 3) * 30);

            Assert.AreEqual(0, c1.Distance(p1), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceOrigin()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 1);
            RealPoint p1 = c1.Center;

            Assert.AreEqual(0, c1.Distance(p1), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceOriginDot()
        {
            Circle c1 = new Circle(new RealPoint(0, 0), 0);
            RealPoint p1 = c1.Center;

            Assert.AreEqual(0, c1.Distance(p1), RealPoint.PRECISION);
        }

        [TestMethod]
        public void TestDistanceDot()
        {
            Circle c1 = new Circle(new RealPoint(42, 42), 0);
            RealPoint p1 = c1.Center;

            Assert.AreEqual(0, c1.Distance(p1), RealPoint.PRECISION);
        }

        #endregion

    }
}
<file_sep>/GoBot/GoBot/IHM/Elements/PanelGenerics.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace GoBot.IHM
{
    public partial class PanelGenerics : UserControl
    {
        public PanelGenerics()
        {
            InitializeComponent();
            AutoSize = true;
        }

        private void PanelGenerics_Load(object sender, EventArgs e)
        {
            int x = 0;

            if (!Execution.DesignMode)
            {
                Type t = typeof(Actionneurs.Actionneur);

                foreach (PropertyInfo prop in t.GetProperties())
                {
                    PanelActionneurGeneric panel = new PanelActionneurGeneric();
                    Object item = prop.GetValue(null, null);
                    if (item != null)
                    {
                        panel.SetObject(item);
                        panel.SetBounds(x, 0, panel.Width, this.Height);
                        this.Controls.Add(panel);
                        x += 135;
                    }
                }
            }
        }
    }
}
<file_sep>/GoBot/Composants/Led.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Composants
{
    public partial class Led : PictureBox
    {
        private Color _color;

        private Timer _blinkTimer;
        private int _blinkCounter;

        private static Dictionary<Color, Bitmap> _bitmaps { get; set; } // Images déjà créées, inutile de les recaculer à chaque fois
        
        public Led()
        {
            if(_bitmaps is null)
            {
                _bitmaps = new Dictionary<Color, Bitmap>();
                _bitmaps.Add(Color.Red, Properties.Resources.RedLed);
            }

            InitializeComponent();
            BackColor = Color.Transparent;

            _blinkTimer = new Timer();
            _blinkTimer.Interval = 100;
            _blinkTimer.Tick += new EventHandler(timer_Tick);

            _blinkCounter = 0;

            Color = Color.Red;
        }

        /// <summary>
        /// Permet d'obtenir ou de définir la couleur de la LED
        /// </summary>
        public Color Color
        {
            get { return _color; }
            set
            {
                _color = value;

                lock (_bitmaps)
                {
                    if (!_bitmaps.ContainsKey(_color))
                        GenerateLed(_color);

                    this.Image = (Image)_bitmaps[_color].Clone();
                }

            }
        }

        /// <summary>
        /// Fait clignoter la LED 7 fois
        /// </summary>
        /// <param name="shutdown">Vrai si la LED doit rester éteinte à la fin du clignotement</param>
        public void Blink(bool shutdown = false)
        {
            _blinkTimer.Stop();
            Visible = true;
            _blinkCounter = 0;
            if (shutdown)
                _blinkCounter = 1;

            _blinkTimer.Start();
        }

        /// <summary>
        /// Change la couleur de la led et la fait clignoter 7 fois si la couleur est différente à la précédente.
        /// </summary>
        /// <param name="color"></param>
        public void BlinkColor(Color color)
        {
            if(_color != color)
            {
                Color = color;
                Blink();
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            if (Visible)
                Visible = false;
            else
                Visible = true;

            _blinkCounter++;

            if (_blinkCounter > 7)
                _blinkTimer.Stop();
        }
        
        private void GenerateLed(Color col)
        {
            Bitmap bmp;
            Color ex;

            // Création des LEDs de différentes couleurs à partir du modèle rouge
            bmp = new Bitmap(Properties.Resources.RedLed);
            
            for (int i = 0; i <= bmp.Width - 1; i++)
            {
                for (int j = 0; j <= bmp.Height - 1; j++)
                {
                    ex = bmp.GetPixel(i, j);
                    bmp.SetPixel(i, j, Color.FromArgb(ex.A, (int)(ex.B + (ex.R - ex.B) / 255.0 * col.R), (int)(ex.B + (ex.R - ex.B) / 255.0 * col.G), (int)(ex.B + (ex.R - ex.B) / 255.0 * col.B)));
                }
            }

            _bitmaps.Add(col, bmp);
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/IAction.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace GoBot.Actions
{
    public interface IAction
    {
        Image Image { get; }
        void Executer();
    }
}
<file_sep>/GoBot/GoBot/Movements/MovementColorDropoff.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Actionneurs;
using GoBot.BoardContext;
using GoBot.GameElements;
using GoBot.Threading;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading;

namespace GoBot.Movements
{
    public class MovementColorDropoff : Movement
    {
        ColorDropOff _zone;

        public MovementColorDropoff(ColorDropOff zone)
        {
            _zone = zone;

            Positions.Add(new Position(90, new RealPoint(_zone.Position.X, _zone.Position.Y - 450)));
        }

        public override bool CanExecute =>
            _zone.Owner == GameBoard.MyColor &&
            Math.Max(_zone.LoadsOnRed, _zone.LoadsOnRed) < 4 &&
            !Actionneur.Lifter.Loaded &&
            _zone.HasInsideBuoys &&
            Actionneur.ElevatorLeft.CountTotal + Actionneur.ElevatorRight.CountTotal > 0;

        public override int Score => 0; // On va compter les points de ménière plus précise

        public override double Value => GameBoard.Strategy.TimeBeforeEnd.TotalSeconds > 25 ? 0.3 : 5;

        public override GameElement Element => _zone;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => _zone.Owner;

        protected override void MovementBegin()
        {
            // Rien, mais on peut envisager de pré-décharger SI y'a pas de gobelet à attraper à l'arrivée ?
            Actionneur.ElevatorLeft.DoGrabOpen();
            Actionneur.ElevatorRight.DoGrabOpen();
        }

        protected override bool MovementCore()
        {
            // TODO : Attrapage (avec test) des bouées exterieures, d'ailleurs prévoir de la place pour les attraper

            ThreadLink link1 = ThreadManager.CreateThread(link =>  Actionneur.ElevatorRight.DoSequencePickupColor(GameBoard.MyColor == GameBoard.ColorLeftBlue ? Buoy.Green : Buoy.Red));
            ThreadLink link2 = ThreadManager.CreateThread(link => Actionneur.ElevatorLeft.DoSequencePickupColor(GameBoard.MyColor == GameBoard.ColorLeftBlue ? Buoy.Red : Buoy.Green));

            link1.StartThread();
            link2.StartThread();

            link1.WaitEnd();
            link2.WaitEnd();

            if (_zone.HasOutsideBuoys)
            {
                Robot.SetSpeedSlow();
                Actionneur.ElevatorLeft.DoGrabOpen();
                Actionneur.ElevatorRight.DoGrabOpen();
                Robot.Move(250);
                Robot.SetSpeedFast();
                //ThreadManager.CreateThread(link => { Actionneur.ElevatorLeft.DoSequencePickupColor(Buoy.Red); }).StartThread();
                //ThreadManager.CreateThread(link => { Actionneur.ElevatorRight.DoSequencePickupColor(Buoy.Green); }).StartThread();
                Actionneur.ElevatorLeft.DoTakeLevel0(Buoy.Red);
                Actionneur.ElevatorRight.DoTakeLevel0(Buoy.Green);
                _zone.TakeOutsideBuoys();
                Thread.Sleep(500);
                Robot.PivotRight(180);
                Robot.Move(-150);
            }
            else
            {
                Robot.Move(-400);
            }

            Robot.SetSpeedSlow();

            if (_zone.HasInsideBuoys)
            {
                Robot.Recalibration(SensAR.Arriere, true, true);
                // TODO recallage X et Y tant qu'à faire ?
                DoFingersGrab(); // Attrapage des bouées initialement contre la bordure avec les fingers
                _zone.TakeInsideBuoys();
                Robot.SetSpeedFast();
                Robot.Move(150);
                Robot.PivotLeft(180);
                Robot.Move(125);
                Robot.SetSpeedSlow();
                DoElevatorsDropoff(0);
            }
            else
            {
                // TODO, y'a surement des bouées posées, donc pas de recallage et avancer moins
            }

            bool hasLeft = Actionneur.FingerLeft.Loaded;
            bool hasRight = Actionneur.FingerRight.Loaded;

            if (hasLeft || hasRight)
            {
                Color cLeft = Actionneur.FingerLeft.Load;
                Color cRight = Actionneur.FingerRight.Load;

                Robot.MoveBackward(90);
                Robot.PivotLeft(82);
                _zone.SetPendingRight(cRight, new RealPoint(Robot.Position.Coordinates.X + 193.5, Robot.Position.Coordinates.Y + 86.5).Rotation(Robot.Position.Angle.InPositiveDegrees + 90, Robot.Position.Coordinates));
                Actionneur.FingerRight.DoRelease();
                Robot.PivotRight(164);
                _zone.SetPendingLeft(cLeft, new RealPoint(Robot.Position.Coordinates.X - 193.5, Robot.Position.Coordinates.Y + 86.5).Rotation(Robot.Position.Angle.InPositiveDegrees + 90, Robot.Position.Coordinates));
                Actionneur.FingerLeft.DoRelease();
                Robot.PivotLeft(82);

                int score = 0;
                int level = _zone.GetAvailableLevel();
                if (hasLeft)
                {
                    _zone.SetBuoyOnGreen(cRight, level);
                    score += 2;
                }
                if (hasRight)
                {
                    _zone.SetBuoyOnRed(cLeft, level);
                    score += 2;
                }

                _zone.RemovePending();

                if (hasLeft && hasRight)
                    score += 2;

                GameBoard.Score += score;
            }

            if (_zone.LoadsOnGreen > 4 || _zone.LoadsOnRed > 4)
            {
                Actionneur.ElevatorLeft.DoGrabOpen();
                Actionneur.ElevatorRight.DoGrabOpen();
                Thread.Sleep(150);
                Robot.SetSpeedVerySlow();
                Robot.MoveForward(170);
                Robot.SetSpeedFast();
                Robot.MoveBackward(170);
            }
            else
            {
                Robot.MoveBackward(100);
            }

            Actionneur.ElevatorLeft.DoGrabClose();
            Actionneur.ElevatorRight.DoGrabClose();

            Robot.SetSpeedFast();

            return true;
        }

        private void DoFingersGrab()
        {
            ThreadLink left = ThreadManager.CreateThread(link => Actionneur.FingerLeft.DoGrabColor(Buoy.Red));
            ThreadLink right = ThreadManager.CreateThread(link => Actionneur.FingerRight.DoGrabColor(Buoy.Green));

            left.StartThread();
            right.StartThread();

            left.WaitEnd();
            right.WaitEnd();
        }

        private void DoElevatorsDropoff(int level)
        {
            Color colorLeft = Color.Transparent, colorRight = Color.Transparent;
            bool okColorLeft, okColorRight;

            ThreadLink left = ThreadManager.CreateThread(link => colorLeft = Actionneur.ElevatorLeft.DoSequenceDropOff());
            ThreadLink right = ThreadManager.CreateThread(link => colorRight = Actionneur.ElevatorRight.DoSequenceDropOff());

            while (_zone.LoadsOnGreen < 4 && _zone.LoadsOnRed < 4 && Actionneur.ElevatorLeft.CountTotal > 0 && Actionneur.ElevatorRight.CountTotal > 0)
            {
                left.StartThread();
                right.StartThread();

                left.WaitEnd();
                right.WaitEnd();

                if (colorLeft != Color.Transparent)
                {
                    _zone.SetBuoyOnRed(colorLeft, level);
                    okColorLeft = (colorLeft == Buoy.Red);
                    GameBoard.Score += (1 + (okColorLeft ? 1 : 0));
                }
                else
                {
                    okColorLeft = false;
                }

                if (colorRight != Color.Transparent)
                {
                    _zone.SetBuoyOnGreen(colorRight, level);
                    okColorRight = (colorRight == Buoy.Green);
                    GameBoard.Score += (1 + (okColorRight ? 1 : 0));
                }
                else
                {
                    okColorRight = false;
                }

                if (okColorLeft && okColorRight)
                    GameBoard.Score += 2;

                level++;

                Robot.Move(-85);
            }

            // Pour ranger à la fin :
            Actionneur.ElevatorLeft.DoPushInsideFast();
            Actionneur.ElevatorRight.DoPushInsideFast();
        }

        protected override void MovementEnd()
        {
            // rien ?
            Actionneur.ElevatorLeft.DoGrabClose();
            Actionneur.ElevatorRight.DoGrabClose();
        }
    }
}
<file_sep>/GoBot/Geometry/Position.cs
﻿using System;

using Geometry.Shapes;

namespace Geometry
{
    public class Position
    {
        public RealPoint Coordinates { get; set; }
        public AnglePosition Angle { get; set; }

        /// <summary>
        /// Constructeur par défaut
        /// Angle de 0° et Coordonnées (0, 0)
        /// </summary>
        public Position()
        {
            Angle = new AnglePosition();
            Coordinates = new RealPoint();
        }
        
        /// <summary>
        /// Constructeur par copie
        /// </summary>
        /// <param name="other">Position à copier</param>
        public Position(Position other)
        {
            Angle = other.Angle;
            Coordinates = new RealPoint(other.Coordinates);
        }

        /// <summary>
        /// Construit une position selon les paramètres
        /// </summary>
        /// <param name="angle">Angle de départ</param>
        /// <param name="coordinates">Coordonnées de départ</param>
        public Position(AnglePosition angle, RealPoint coordinates)
        {
            Angle = angle;
            Coordinates = new RealPoint(coordinates);
        }

        /// <summary>
        /// Déplace les coordonnées par rapport aux anciennes coordonnées
        /// </summary>
        /// <param name="x">Déplacement sur l'axe des abscisses</param>
        /// <param name="y">Déplacement sur l'axe des ordonnées</param>
        public void Shift(double x, double y)
        {
            Coordinates = Coordinates.Translation(x, y);
        }

        /// <summary>
        /// Fait tourner l'angle de l'angle choisi
        /// </summary>
        /// <param name="angle">Angle à tourner</param>
        public void Turn(AngleDelta angle)
        {
            Angle += angle;
        }

        /// <summary>
        /// Avance de la distance spécifiée suivant l'angle actuel
        /// </summary>
        /// <param name="distance">Distance à avancer</param>
        public void Move(double distance)
        {
            double depX = distance * Math.Cos(Angle.InRadians);
            double depY = distance * Math.Sin(Angle.InRadians);

            Coordinates = Coordinates.Translation(depX, depY);
        }

        /// <summary>
        /// Copie une autre position
        /// </summary>
        /// <param name="position">Position à copier</param>
        public void Copy(Position position)
        {
            Angle = position.Angle;
            Coordinates.Set(position.Coordinates.X, position.Coordinates.Y);
        }

        public override string ToString()
        {
            return Coordinates.ToString() + " " + Angle.ToString();
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/UDP/UdpFrameFunction.cs
﻿namespace GoBot.Communications.UDP
{
    public enum UdpFrameFunction
    {
        Debug = 0xEE,
        TestConnexion = 0xF0,
        DemandeTension = 0xF1,
        RetourTension = 0xF2,

        DemandeCapteurOnOff = 0x74,
        RetourCapteurOnOff = 0x75,
        DemandeValeursAnalogiques = 0x76,
        RetourValeursAnalogiques = 0x77,
        DemandeValeursNumeriques = 0x78,
        RetourValeursNumeriques = 0x79,
        DemandeCapteurCouleur = 0x52,
        RetourCapteurCouleur = 0x53,
        DemandePositionCodeur = 0x21,
        RetourPositionCodeur = 0x22,

        MoteurOrigin = 0x63,
        MoteurResetPosition = 0x64,
        PilotageOnOff = 0x65,
        MoteurPosition = 0x66,
        MoteurVitesse = 0x67,
        MoteurAccel = 0x68,
        MoteurStop = 0x69,
        MoteurFin = 0x70,
        MoteurBlocage = 0x71,

        Deplace = 0x01,
        Pivot = 0x03,
        Virage = 0x04,
        Stop = 0x05,
        Recallage = 0x10,
        TrajectoirePolaire = 0x20,
        FinRecallage = 0x11,
        FinDeplacement = 0x12,
        Blocage = 0x13,

        AsserDemandePositionCodeurs = 0x43,
        AsserRetourPositionCodeurs = 0x44,
        AsserEnvoiConsigneBrutePosition = 0x45,
        DemandeChargeCPU_PWM = 0x46,
        RetourChargeCPU_PWM = 0x47,
        AsserIntervalleRetourPosition = 0x48,

        AsserDemandePositionXYTeta = 0x30,
        AsserRetourPositionXYTeta = 0x31,
        AsserVitesseDeplacement = 0x32,
        AsserAccelerationDeplacement = 0x33,
        AsserVitessePivot = 0x34,
        AsserAccelerationPivot = 0x35,
        AsserPID = 0x36,
        AsserEnvoiPositionAbsolue = 0x37,
        AsserPIDCap = 0x38,
        AsserPIDVitesse = 0x39,

        EnvoiUart1 = 0xA0,
        RetourUart1 = 0xA1,
        DemandeLidar = 0xA2,
        ReponseLidar = 0xA3,
        EnvoiUart2 = 0xA4,
        RetourUart2 = 0xA5,

        EnvoiCAN = 0xC0,
        ReponseCAN = 0xC1,

        DemandeCouleurEquipe = 0x72,
        RetourCouleurEquipe = 0x73
    }

    public enum ServoFunction
    {
        DemandePositionCible = 0x01,
        RetourPositionCible = 0x02,
        EnvoiPositionCible = 0x03,
        EnvoiBaudrate = 0x06,
        DemandeVitesseMax = 0x07,
        RetourVitesseMax = 0x08,
        EnvoiVitesseMax = 0x09,
        DemandeAllIn = 0x10,
        RetourAllIn = 0x11,
        EnvoiId = 0x12,
        Reset = 0x13,
        DemandeCoupleMaximum = 0x14,
        RetourCoupleMaximum = 0x15,
        EnvoiCoupleMaximum = 0x16,
        DemandeCoupleActive = 0x17,
        RetourCoupleActive = 0x18,
        EnvoiCoupleActive = 0x19,
        DemandeTension = 0x20,
        RetourTension = 0x21,
        DemandeTemperature = 0x22,
        RetourTemperature = 0x23,
        DemandeMouvement = 0x24,
        RetourMouvement = 0x25,
        DemandePositionMinimum = 0x26,
        RetourPositionMinimum = 0x27,
        EnvoiPositionMinimum = 0x28,
        DemandePositionMaximum = 0x29,
        RetourPositionMaximum = 0x30,
        EnvoiPositionMaximum = 0x31,
        DemandeNumeroModele = 0x32,
        RetourNumeroModele = 0x33,
        DemandeVersionFirmware = 0x34,
        RetourVersionFirmware = 0x35,
        DemandeLed = 0x36,
        RetourLed = 0x37,
        EnvoiLed = 0x38,
        DemandeConfigAlarmeLED = 0x42,
        RetourConfigAlarmeLED = 0x43,
        EnvoiConfigAlarmeLED = 0x44,
        DemandeConfigAlarmeShutdown = 0x45,
        RetourConfigAlarmeShutdown = 0x46,
        EnvoiConfigAlarmeShutdown = 0x47,
        DemandeConfigEcho = 0x48,
        RetourConfigEcho = 0x49,
        EnvoiConfigEcho = 0x50,
        DemandeComplianceParams = 0x51,
        RetourComplianceParams = 0x52,
        EnvoiComplianceParams = 0x53,
        DemandePositionActuelle = 0x54,
        RetourPositionActuelle = 0x55,
        DemandeVitesseActuelle = 0x56,
        RetourVitesseActuelle = 0x57,
        DemandeErreurs = 0x58,
        RetourErreurs = 0x59,
        DemandeCoupleCourant = 0x60,
        RetourCoupleCourant = 0x61,
        DemandeStatusLevel = 0x62,
        RetourStatusLevel = 0x63,
        EnvoiTensionMax = 0x64,
        DemandeTensionMax = 0x65,
        RetourTensionMax = 0x66,
        EnvoiTensionMin = 0x67,
        DemandeTensionMin = 0x68,
        RetourTensionMin = 0x69,
        EnvoiTemperatureMax = 0x70,
        DemandeTemperatureMax = 0x71,
        RetourTemperatureMax = 0x72,
        EnvoiCoupleLimite = 0x73,
        DemandeCoupleLimite = 0x74,
        RetourCoupleLimite = 0x75
    }
}
<file_sep>/GoBot/GoBot/Communications/CAN/CanConnection.cs
﻿using GoBot.Communications;
using GoBot.Communications.UDP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace GoBot.Communications.CAN
{
    /// <summary>
    /// Définit un objet capable d'envoyer des messages CAN
    /// </summary>
    public interface iCanSpeakable
    {
        bool SendFrame(Frame frame);
        int GetNextFrameID();
    }

    public class CanConnection : Connection, iCanSpeakable
    {
        //TODO : CanConnection, héritier de Connection et transfert de la classe dans ../../Communications

        private int _framesCount;

        private Board _board;
        private List<byte> _receivedBuffer;

        private String _name;
        
        public CanConnection(Board board)
        {
            _board = board;
            _framesCount = 0;

            _receivedBuffer = new List<byte>();
            _name = board.ToString();
        }

        public override string Name { get => _name; set => _name = value; }

        private void board_FrameReceived(Frame frame)
        {
            if (frame[1] == (byte)UdpFrameFunction.ReponseCAN)
            {
                for (int i = 3; i < frame.Length; i++)
                    _receivedBuffer.Add(frame[i]);
            }

            if (_receivedBuffer.Count >= 10)
            {
                Frame canFrame = new Frame(_receivedBuffer.GetRange(0, 10));
                _receivedBuffer.RemoveRange(0, 10);
                OnFrameReceived(canFrame);
            }
        }

        bool iCanSpeakable.SendFrame(Frame frame)
        {
            return this.SendMessage(frame);
        }

        int iCanSpeakable.GetNextFrameID()
        {
            _framesCount = (_framesCount + 1) % 255;
            return _framesCount;
        }

        public override bool SendMessage(Frame f)
        {
            bool ok = true;
            
            Connections.UDPBoardConnection[_board].SendMessage(UdpFrameFactory.EnvoyerCAN(_board, f));
            OnFrameSend(f);

            _framesCount = (_framesCount + 1) % 255;
            
            return ok;
        }

        public override void StartReception()
        {
            Connections.UDPBoardConnection[_board].StartReception();
            Connections.UDPBoardConnection[_board].FrameReceived += board_FrameReceived;
        }

        public override void Close()
        {
            Connections.UDPBoardConnection[_board].FrameReceived -= board_FrameReceived;
        }
    }
}
<file_sep>/GoBot/GoBot/Threading/ThreadManager.cs
﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace GoBot.Threading
{
    /// <summary>
    /// Gestion de threads supervisés
    /// </summary>
    static class ThreadManager
    {
        #region Fields

        private static List<ThreadLink> _threadsLink;
        private static ThreadLink _linkCleanDeads;

        #endregion

        #region Properties

        /// <summary>
        /// Obtient une copie de la liste des threads actuellements suivis.
        /// </summary>
        public static ReadOnlyCollection<ThreadLink> ThreadsLink
        {
            get
            {
                return new List<ThreadLink>(_threadsLink).AsReadOnly();
            }
        }

        #endregion

        #region Constructors
        
        public static void Init()
        {
            _threadsLink = new List<ThreadLink>();

            _linkCleanDeads = CreateThread(link => CleanDeads());
            _linkCleanDeads.Name = "Nettoyage des threads terminés";
            _linkCleanDeads.StartInfiniteLoop(new TimeSpan(0, 0, 1));
        }

        #endregion

        #region Public methods
        
        /// <summary>
        /// Crée un thread et retourne le lien.
        /// </summary>
        /// <param name="call">Appel à executer par le thread.</param>
        /// <returns>Lien vers le thread créé.</returns>
        public static ThreadLink CreateThread(ThreadLink.CallBack call)
        {
            ThreadLink link = new ThreadLink(call);
            _threadsLink.Add(link);

            return link;
        }

        ///// <summary>
        ///// Lance un thread sur un appel unique.
        ///// </summary>
        ///// <param name="call">Appel à executer.</param>
        ///// <returns>Lien vers le thread d'execution.</returns>
        //public static ThreadLink StartThread(ThreadLink.CallBack call)
        //{
        //    ThreadLink link = new ThreadLink(call);
        //    _threadsLink.Add(link);
        //    link.StartThread();

        //    return link;
        //}

        ///// <summary>
        ///// Lance un thread sur un nombre déterminé d'appels en boucle.
        ///// </summary>
        ///// <param name="call">Appel à executer.</param>
        ///// <param name="interval">Intervalle passif entre chaque appel.</param>
        ///// <param name="executions">Nombre d'executions à effectuer.</param>
        ///// <returns>Lien vers le thread d'execution.</returns>
        //public static ThreadLink StartLoop(ThreadLink.CallBack call, TimeSpan interval, int executions)
        //{
        //    ThreadLink link = new ThreadLink(call);
        //    _threadsLink.Add(link);
        //    link.StartLoop(interval, executions);

        //    return link;
        //}

        ///// <summary>
        ///// Lance un thread sur un nombre indéterminé d'appels en boucle.
        ///// </summary>
        ///// <param name="call">Appel à executer.</param>
        ///// <param name="interval">Intervalle passif entre chaque appel.</param>
        ///// <returns>Lien vers le thread d'execution.</returns>
        //public static ThreadLink StartInfiniteLoop(ThreadLink.CallBack call, TimeSpan interval)
        //{
        //    ThreadLink link = new ThreadLink(call);
        //    _threadsLink.Add(link);
        //    link.StartInfiniteLoop(interval);

        //    return link;
        //}


        /// <summary>
        /// Demande à chaque thread de se couper. L'extinction doit être réalisée par la méthode appellée en testant si le lien vers le thread a été annulé.
        /// Patiente jusqu'à la fin d'execution de tous les threads pendant un certain temps.
        /// Cette durée représente la durée totale d'attente et donc représente la cumul d'attente de fin de chaque thread.
        /// </summary>
        /// <param name="timeout">Nombre de millisecondes maximum à attendre.</param>
        /// <returns>Retourne vrai si tous les threads ont correctement été terminés avant le timeout.</returns>
        public static bool ExitAll(int timeout = 5000)
        {
            _threadsLink.ForEach(t => t.Cancel());

            // Le timeout est global, donc tout le monde doit avoir terminé avant la fin
            Stopwatch sw = Stopwatch.StartNew();
            bool ended = true;

            _threadsLink.ForEach(t => ended = ended && t.WaitEnd((int)Math.Max(0, timeout - sw.ElapsedMilliseconds)));

            return ended;
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Permet de supprimer les liens vers les threads terminés depuis un certain temps.
        /// </summary>
        private static void CleanDeads()
        {
            _threadsLink.RemoveAll(t => t.Ended && t.EndDate < (DateTime.Now - new TimeSpan(0, 1, 0)));
        }

        private static void PrintThreads()
        {
            Console.WriteLine("--------------------------------------------");

            foreach (ThreadLink l in ThreadManager.ThreadsLink)
            {
                Console.WriteLine(l.ToString());
            }
        }

        #endregion
    }
}<file_sep>/GoBot/GoBot/IHM/Pages/PageReglageAsserv.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

using GoBot.Threading;

namespace GoBot.IHM.Pages
{
    public partial class PageReglageAsserv : UserControl
    {
        private Robot Robot { get; set; }

        public PageReglageAsserv()
        {
            InitializeComponent();

            numCoeffP.Value = Config.CurrentConfig.GRCoeffP;
            numCoeffI.Value = Config.CurrentConfig.GRCoeffI;
            numCoeffD.Value = Config.CurrentConfig.GRCoeffD;
            this.numCoeffD.ValueChanged += new System.EventHandler(this.btnOk_Click);
            this.numCoeffI.ValueChanged += new System.EventHandler(this.btnOk_Click);
            this.numCoeffP.ValueChanged += new System.EventHandler(this.btnOk_Click);
            Robot = Robots.MainRobot;
        }

        private void btnOk_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(link => EnvoiTestPid(link)).StartThread();
        }

        private void EnvoiTestPid(ThreadLink link)
        {
            link.RegisterName();

            Robot.SendPID((int)numCoeffP.Value, (int)numCoeffI.Value, (int)numCoeffD.Value);
            List<int>[] mesures = Robot.DiagnosticPID((int)numPasCodeurs.Value, SensAR.Avant, (int)numNbPoints.Value);
            // Afficher les courbes

            int derniereValeurGauche = mesures[0][mesures[0].Count - 1];
            int derniereValeurDroite = mesures[1][mesures[1].Count - 1];

            int premiereValeurGauche = mesures[0][0];
            int premiereValeurDroite = mesures[1][0];

            for (int i = 0; i < mesures[0].Count; i++)
                mesures[0][i] = mesures[0][i] - premiereValeurGauche;
            for (int i = 0; i < mesures[1].Count; i++)
                mesures[1][i] = mesures[1][i] - premiereValeurDroite;

            if (derniereValeurGauche == 0)
                derniereValeurGauche = 1;

            if (derniereValeurDroite == 0)
                derniereValeurDroite = 1;

            double depassementGauchePositif = (mesures[0].Max() - (int)numPasCodeurs.Value) / (double)numPasCodeurs.Value * 100;
            double depassementDroitePositif = (mesures[1].Max() - (int)numPasCodeurs.Value) / (double)numPasCodeurs.Value * 100;

            int tempsGaucheStable = mesures[0].Count - 1;
            while (tempsGaucheStable > 0 && mesures[0][tempsGaucheStable] < mesures[0][mesures[0].Count - 1] * 1.05 && mesures[0][tempsGaucheStable] > mesures[0][mesures[0].Count - 1] * 0.95)
                tempsGaucheStable--;

            int tempsDroiteStable = mesures[1].Count - 1;
            while (tempsDroiteStable > 0 && mesures[1][tempsDroiteStable] < mesures[1][mesures[1].Count - 1] * 1.05 && mesures[1][tempsDroiteStable] > mesures[1][mesures[1].Count - 1] * 0.95)
                tempsDroiteStable--;

            this.InvokeAuto(() =>
            {
                lblTpsStabilisationGauche.Text = tempsGaucheStable + "ms";
                lblTpsStabilisationDroite.Text = tempsDroiteStable + "ms";

                lblOvershootGauche.Text = depassementGauchePositif.ToString("0.00") + "%";
                lblOvershootDroite.Text = depassementDroitePositif.ToString("0.00") + "%";

                lblValeurFinGauche.Text = mesures[0][mesures[0].Count - 1].ToString();
                lblValeurFinDroite.Text = mesures[1][mesures[1].Count - 1].ToString();

                ctrlGraphique.DeleteCurve("Roue droite");
                ctrlGraphique.DeleteCurve("Roue gauche");


                for (int i = 0; i < mesures[0].Count; i++)
                {
                    if (boxMoyenne.Checked && i > 1 && i < mesures[0].Count - 2)
                    {
                        double valeur = (mesures[0][i - 2] + mesures[0][i - 1] + mesures[0][i] + mesures[0][i + 1] + mesures[0][i + 2]) / 5.0;
                        ctrlGraphique.AddPoint("Roue gauche", valeur, Color.Blue);
                    }
                    else
                        ctrlGraphique.AddPoint("Roue gauche", mesures[0][i], Color.Blue);
                }

                for (int i = 0; i < mesures[1].Count; i++)
                {
                    if (boxMoyenne.Checked && i > 1 && i < mesures[1].Count - 2)
                    {
                        double valeur = (mesures[1][i - 2] + mesures[1][i - 1] + mesures[1][i] + mesures[1][i + 1] + mesures[1][i + 2]) / 5.0;
                        ctrlGraphique.AddPoint("Roue droite", valeur, Color.Green);
                    }
                    else
                        ctrlGraphique.AddPoint("Roue droite", mesures[1][i], Color.Green);
                }

                ctrlGraphique.DrawCurves();
            });

            Robot.DiagnosticPID((int)numPasCodeurs.Value, SensAR.Arriere, (int)numNbPoints.Value);
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Forms/FenNomArchivage.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class FenNomArchivage : Form
    {
        public String Nom { get; set; }
        public bool OK { get; set; }

        public FenNomArchivage()
        {
            InitializeComponent();
            OK = false;
        }

        private void FenNomArchivage_Load(object sender, EventArgs e)
        {
            lblDate.Text = Nom;
        }

        private void btnOk_Click(object sender, EventArgs e)
        {
            Nom = txtNom.Text;
            OK = true;
            this.Close();
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/Arms.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actionneurs
{
    class Arms
    {
        protected ArmLeft _leftArm;
        protected ArmRight _rightArm;

        public Arms()
        {
            _leftArm = new ArmLeft();
            _rightArm = new ArmRight();
        }

        public void DoOpenLeft()
        {
            _leftArm.SendPosition(_leftArm.PositionOpen);
        }

        public void DoCloseLeft()
        {
            _leftArm.SendPosition(_leftArm.PositionClose);
        }

        public void DoOpenRight()
        {
            _rightArm.SendPosition(_rightArm.PositionOpen);
        }

        public void DoCloseRight()
        {
            _rightArm.SendPosition(_rightArm.PositionClose);
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/Elevators.cs
﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;

using Geometry;
using Geometry.Shapes;
using GoBot.BoardContext;
using GoBot.Devices;
using GoBot.GameElements;
using GoBot.Threading;

namespace GoBot.Actionneurs
{
    class ElevatorRight : Elevator
    {
        public ElevatorRight()
        {
            _servoPush = Config.CurrentConfig.ServoPushArmRight;
            _servoLocker = Config.CurrentConfig.ServoLockerRight;
            _servoGraber = Config.CurrentConfig.ServoGrabberRight;

            _makeVacuum = ActuatorOnOffID.MakeVacuumRightFront;
            _openVacuum = ActuatorOnOffID.OpenVacuumRightFront;

            _pressure = SensorOnOffID.PressureSensorRightFront;

            _elevator = Config.CurrentConfig.MotorElevatorRight;

            _sensor = SensorOnOffID.PresenceBuoyRight;
        }

        protected override RealPoint GetEntryFrontPoint()
        {
            return new RealPoint(Robots.MainRobot.Position.Coordinates.X + 100, Robots.MainRobot.Position.Coordinates.Y + 90).Rotation(new AngleDelta(Robots.MainRobot.Position.Angle), Robots.MainRobot.Position.Coordinates);
        }

        protected override RealPoint GetEntryBackPoint()
        {
            return new RealPoint(Robots.MainRobot.Position.Coordinates.X - 100, Robots.MainRobot.Position.Coordinates.Y + 90).Rotation(new AngleDelta(Robots.MainRobot.Position.Angle), Robots.MainRobot.Position.Coordinates);
        }
    }

    class ElevatorLeft : Elevator
    {
        public ElevatorLeft()
        {
            _servoPush = Config.CurrentConfig.ServoPushArmLeft;
            _servoLocker = Config.CurrentConfig.ServoLockerLeft;
            _servoGraber = Config.CurrentConfig.ServoGrabberLeft;

            _makeVacuum = ActuatorOnOffID.MakeVacuumLeftFront;
            _openVacuum = ActuatorOnOffID.OpenVacuumLeftFront;

            _pressure = SensorOnOffID.PressureSensorLeftFront;

            _elevator = Config.CurrentConfig.MotorElevatorLeft;

            _sensor = SensorOnOffID.PresenceBuoyRight;
        }

        protected override RealPoint GetEntryFrontPoint()
        {
            return new RealPoint(Robots.MainRobot.Position.Coordinates.X + 100, Robots.MainRobot.Position.Coordinates.Y - 90).Rotation(new AngleDelta(Robots.MainRobot.Position.Angle), Robots.MainRobot.Position.Coordinates);
        }

        protected override RealPoint GetEntryBackPoint()
        {
            return new RealPoint(Robots.MainRobot.Position.Coordinates.X - 100, Robots.MainRobot.Position.Coordinates.Y - 90).Rotation(new AngleDelta(Robots.MainRobot.Position.Angle), Robots.MainRobot.Position.Coordinates);
        }
    }

    abstract class Elevator
    {
        protected bool _isInitialized;

        protected ServoPushArm _servoPush;
        protected ServoLocker _servoLocker;
        protected ServoGrabber _servoGraber;
        protected ActuatorOnOffID _makeVacuum, _openVacuum;
        protected SensorOnOffID _pressure;
        protected MotorElevator _elevator;

        protected SensorOnOffID _sensor;

        protected List<Color> _buoysSecond; // 0 = en haut...1...2 = étages inférieurs
        protected List<Color> _buoysFirst; // 0 = en haut...1...2...3 = En bas, non monté

        protected List<Tuple<PositionLoad, PositionFloor>> _pickupOrder;
        protected List<Tuple<PositionLoad, PositionFloor>> _dropoffOrder;

        protected bool _isBusy;

        protected bool _grabberOpened;
        protected bool _armed;

        protected enum PositionLoad
        {
            First,
            Second
        }
        protected enum PositionFloor
        {
            Floor3 = 0,
            Floor2,
            Floor1,
            Ground
        }

        public Elevator()
        {
            _isInitialized = false;
            _grabberOpened = false;
            _armed = false;

            _buoysSecond = Enumerable.Repeat(Color.Transparent, 3).ToList();
            _buoysFirst = Enumerable.Repeat(Color.Transparent, 4).ToList();

            _pickupOrder = new List<Tuple<PositionLoad, PositionFloor>>();
            _pickupOrder.Add(Tuple.Create(PositionLoad.First, PositionFloor.Floor3));
            _pickupOrder.Add(Tuple.Create(PositionLoad.First, PositionFloor.Floor2));
            _pickupOrder.Add(Tuple.Create(PositionLoad.First, PositionFloor.Floor1));
            _pickupOrder.Add(Tuple.Create(PositionLoad.Second, PositionFloor.Floor3));
            _pickupOrder.Add(Tuple.Create(PositionLoad.Second, PositionFloor.Floor2));
            _pickupOrder.Add(Tuple.Create(PositionLoad.Second, PositionFloor.Floor1));
            _pickupOrder.Add(Tuple.Create(PositionLoad.First, PositionFloor.Ground));

            _dropoffOrder = new List<Tuple<PositionLoad, PositionFloor>>();
            _dropoffOrder.Add(Tuple.Create(PositionLoad.First, PositionFloor.Ground));
            _dropoffOrder.Add(Tuple.Create(PositionLoad.First, PositionFloor.Floor1));
            _dropoffOrder.Add(Tuple.Create(PositionLoad.First, PositionFloor.Floor2));
            _dropoffOrder.Add(Tuple.Create(PositionLoad.First, PositionFloor.Floor3));
            _dropoffOrder.Add(Tuple.Create(PositionLoad.Second, PositionFloor.Floor1));
            _dropoffOrder.Add(Tuple.Create(PositionLoad.Second, PositionFloor.Floor2));
            _dropoffOrder.Add(Tuple.Create(PositionLoad.Second, PositionFloor.Floor3));
        }

        public List<Color> LoadFirst => _buoysFirst;
        public List<Color> LoadSecond => _buoysSecond;
        public bool GrabberOpened => _grabberOpened;

        public bool Armed
        {
            get { return _armed; }
            set { _armed = value; }
        }

        public void FillWith(Color c)
        {
            _buoysFirst[(int)PositionFloor.Floor3] = c;
            _buoysFirst[(int)PositionFloor.Floor2] = c;
            _buoysFirst[(int)PositionFloor.Floor1] = c;
            _buoysFirst[(int)PositionFloor.Ground] = c;

            _buoysSecond[(int)PositionFloor.Floor3] = c;
            _buoysSecond[(int)PositionFloor.Floor2] = c;
            _buoysSecond[(int)PositionFloor.Floor1] = c;
        }

        public bool CanStoreMore => CountTotal < 7;
        public int CountTotal => _buoysSecond.Concat(_buoysFirst).Where(b => b != Color.Transparent).Count();
        public int CountSecond => _buoysSecond.Where(b => b != Color.Transparent).Count();
        public int CountFirst => _buoysFirst.Where(b => b != Color.Transparent).Count();
        public int CountRed => _buoysFirst.Where(b => b == Buoy.Red).Count();
        public int CountGreen => _buoysFirst.Where(b => b == Buoy.Green).Count();

        public static int MaxLoad => 7;

        public void DoGrabOpen()
        {
            _servoGraber.SendPosition(_servoGraber.PositionOpen);
            _grabberOpened = true;
        }

        public void DoTakeLevel0(Color c)
        {
            DoAirLock();
            DoGrabClose();
            BuoySet(Tuple.Create(PositionLoad.First, PositionFloor.Ground), c);
        }

        public void DoStoreBack(Color c)
        {
            DoAirLock();
            DoGrabClose();

            if (WaitSomething())
            {
                DoStoreBackColor(c); 
            }
            else
            {
                DoAirUnlock();
            }

            DoGrabClose();
        }

        public void DoGrabRelease()
        {
            _servoGraber.SendPosition(_servoGraber.PositionRelease);
            _grabberOpened = false;
        }

        public void DoGrabClose()
        {
            _servoGraber.SendPosition(_servoGraber.PositionClose);
            _grabberOpened = false;
        }

        public void DoGrabHide()
        {
            _servoGraber.SendPosition(_servoGraber.PositionHide);
            _grabberOpened = false;
        }
        public void DoLockerEngage()
        {
            _servoLocker.SendPosition(_servoLocker.PositionEngage);
        }

        public void DoStoreActuators()
        {
            DoLockerEngage();
            DoElevatorInit();
            DoElevatorGround();
            DoLockerDisengage();
            DoPushInside();
        }

        public void DoLockerDisengage()
        {
            _servoLocker.SendPosition(_servoLocker.PositionDisengage);
        }

        public void DoLockerMaintain()
        {
            _servoLocker.SendPosition(_servoLocker.PositionMaintain);
        }

        public void DoPushInside()
        {
            _servoPush.SendPosition(_servoPush.PositionClose);
            Thread.Sleep(750); // TODO régler la tempo
        }

        public void DoPushInsideFast()
        {
            _servoPush.SendPosition(_servoPush.PositionClose);
        }

        public void DoPushLight()
        {
            _servoPush.SendPosition(_servoPush.PositionLight);
        }

        public void DoPushOutside()
        {
            _servoPush.SendPosition(_servoPush.PositionOpen);
            Thread.Sleep(750); // TODO régler la tempo
        }

        public void DoPushOutsideFast()
        {
            _servoPush.SendPosition(_servoPush.PositionOpen);
        }

        public void DoAirLock()
        {
            Robots.MainRobot.SetActuatorOnOffValue(_makeVacuum, true);
            Robots.MainRobot.SetActuatorOnOffValue(_openVacuum, false);
            _servoLocker.SendPosition(_servoLocker.PositionEngage);
        }

        public void DoAirUnlock()
        {
            Robots.MainRobot.SetActuatorOnOffValue(_makeVacuum, false);
            Robots.MainRobot.SetActuatorOnOffValue(_openVacuum, true);
            _servoLocker.SendPosition(_servoLocker.PositionDisengage);
        }

        public void DoAirUnlockDropoff()
        {
            _servoLocker.SendPosition(_servoLocker.PositionEngage);
            Robots.MainRobot.SetActuatorOnOffValue(_makeVacuum, false);
            Robots.MainRobot.SetActuatorOnOffValue(_openVacuum, true);
            Thread.Sleep(100);
            _servoLocker.SendPosition(_servoLocker.PositionDisengage);
        }

        public void DoElevatorInit()
        {
            _elevator.OriginInit();
            _isInitialized = true;
        }

        public bool HasSomething()
        {
            return Robots.MainRobot.ReadSensorOnOff(_pressure);
        }

        public void DoElevatorStop()
        {
            _elevator.Stop(StopMode.Abrupt);
        }

        public void DoElevatorFree()
        {
            _elevator.Stop(StopMode.Freely);
        }

        public void DoElevatorGround()
        {
            if (!_isInitialized) DoElevatorInit();
            _elevator.SendPosition(_elevator.PositionFloor0);
        }

        public void DoElevatorFloor1()
        {
            if (!_isInitialized) DoElevatorInit();
            _elevator.SendPosition(_elevator.PositionFloor1);
        }

        public void DoElevatorFloor2()
        {
            if (!_isInitialized) DoElevatorInit();
            _elevator.SendPosition(_elevator.PositionFloor2);
        }

        public void DoElevatorFloor3()
        {
            if (!_isInitialized) DoElevatorInit();
            _elevator.SendPosition(_elevator.PositionFloor3);
        }

        public void DoDemoLoad3()
        {
            ThreadLink left, right;

            Robots.MainRobot.SetSpeedSlow();

            while (CountFirst < 3)
            {
                Robots.MainRobot.Move(85);
                Actionneur.ElevatorLeft.DoElevatorGround();
                Actionneur.ElevatorRight.DoElevatorGround();
                left = ThreadManager.CreateThread(link => Actionneur.ElevatorLeft.DoSequencePickup());
                right = ThreadManager.CreateThread(link => Actionneur.ElevatorRight.DoSequencePickup());
                left.StartThread();
                right.StartThread();
                left.WaitEnd();
                right.WaitEnd();
            }

            Robots.MainRobot.SetSpeedFast();
        }

        public void DoDemoLoad7()
        {
            ThreadLink left, right;

            while (CountTotal < 7)
            {
                Actionneur.ElevatorLeft.DoGrabOpen();
                Actionneur.ElevatorRight.DoGrabOpen();
                Robots.MainRobot.Move(85);
                Actionneur.ElevatorLeft.DoElevatorGround();
                Actionneur.ElevatorRight.DoElevatorGround();
                left = ThreadManager.CreateThread(link => Actionneur.ElevatorLeft.DoSequencePickup());
                right = ThreadManager.CreateThread(link => Actionneur.ElevatorRight.DoSequencePickup());
                left.StartThread();
                right.StartThread();
                left.WaitEnd();
                right.WaitEnd();
            }
        }

        public void DoDemoPickup()
        {
            Robots.MainRobot.SetSpeedSlow();
            DoGrabOpen();
            Thread.Sleep(200);
            Robots.MainRobot.MoveForward(85);
            DoSequencePickupColorThread(Buoy.Red);
            Thread.Sleep(350);
            Robots.MainRobot.MoveBackward(85);
            Robots.MainRobot.SetSpeedFast();
        }

        public void DoDemoUnload7()
        {
            ThreadLink left, right;

            while (CountTotal > 0)
            {
                Robots.MainRobot.Move(-85);
                left = ThreadManager.CreateThread(link => Actionneur.ElevatorLeft.DoSequenceDropOff());
                right = ThreadManager.CreateThread(link => Actionneur.ElevatorRight.DoSequenceDropOff());
                left.StartThread();
                right.StartThread();
                left.WaitEnd();
                right.WaitEnd();
            }
        }

        public void DoSequencePickupColor(Color c)
        {
            DoAirLock();
            DoGrabClose();

            if (WaitSomething())
            {
                DoLockerMaintain();
                DoStoreColor(c);
                DoGrabClose();
            }
            else
            {
                DoAirUnlock();
            }
        }

        public void DoSequencePickupColorThread(Color c)
        {
            DoAirLock();
            DoGrabClose();
            Thread.Sleep(250);

            ThreadManager.CreateThread(link =>
            {
                while (_isBusy) ;
                _isBusy = true;
                if (WaitSomething())
                {
                    DoLockerMaintain();
                    DoStoreColor(c);
                    DoGrabClose();
                }
                else
                {
                    DoAirUnlock();
                }
                _isBusy = false;
            }).StartThread();
        }

        public void DoSequencePickup()
        {
            DoAirLock();
            DoGrabClose();

            if (WaitSomething())
            {
                DoLockerMaintain();
                DoStoreColor(Buoy.Red); // TODO détecter la couleur avec le capteur de couleur
            }
            else
            {
                Console.WriteLine("Fin de l'attente, pas de gobelet détecté");
                DoAirUnlock();
            }

            DoGrabClose();
        }

        private void BuoySet(Tuple<PositionLoad, PositionFloor> place, Color c)
        {
            if (place.Item1 == PositionLoad.First)
                _buoysFirst[(int)place.Item2] = c;
            else
                _buoysSecond[(int)place.Item2] = c;
        }

        private void BuoyRemove(Tuple<PositionLoad, PositionFloor> place)
        {
            if (place.Item1 == PositionLoad.First)
                _buoysFirst[(int)place.Item2] = Color.Transparent;
            else
                _buoysSecond[(int)place.Item2] = Color.Transparent;
        }

        private bool IsPositionLoadSecond()
        {
            return _servoPush.GetLastPosition() == _servoPush.PositionOpen;
        }

        public void DoStoreColor(Color c)
        {
            DoElevatorStop();
            DoGrabRelease();

            Tuple<PositionLoad, PositionFloor> place = PlaceToPickup();

            DoPlaceLoad(place.Item1, true);
            DoPlaceFloor(place.Item2);

            DoAirUnlock();
            BuoySet(place, c);

            DoElevatorGround();
            DoPlaceLoad(PositionLoad.First, false);
        }

        public void DoStoreBackColor(Color c)
        {
            DoElevatorStop();
            DoGrabRelease();

            Tuple<PositionLoad, PositionFloor> place = PlaceToPickupBack();

            DoPlaceLoad(place.Item1, true);
            DoPlaceFloor(place.Item2);

            DoAirUnlock();
            BuoySet(place, c);

            DoElevatorGround();
            DoPlaceLoad(PositionLoad.First, false);
        }

        public void DoStorageReset()
        {
            _buoysSecond = Enumerable.Repeat(Color.Transparent, 3).ToList();
            _buoysFirst = Enumerable.Repeat(Color.Transparent, 4).ToList();
        }

        public bool WaitSomething(int timeout = 1000)
        {
            Stopwatch sw = Stopwatch.StartNew();

            while (sw.ElapsedMilliseconds < timeout && !HasSomething())
                Thread.Sleep(50);

            return HasSomething();
        }

        public bool DoSearchBuoy(Color color, IShape containerZone = null)
        {
            List<RealPoint> pts = AllDevices.LidarGround.GetPoints();

            pts = pts.Where(o => GameBoard.IsInside(o, 50)).ToList();
            if (containerZone != null)
            {
                pts = pts.Where(o => containerZone.Contains(o)).ToList();
            }

            List<List<RealPoint>> groups = pts.GroupByDistance(80);
            List<Tuple<Circle, Color>> buoys = new List<Tuple<Circle, Color>>();

            bool output = false;

            for (int i = 0; i < groups.Count; i++)
            {
                if (groups[i].Count > 4)
                {
                    RealPoint center = groups[i].GetBarycenter();
                    double var = Math.Sqrt(groups[i].Average(p => p.Distance(center) * p.Distance(center))) * 2;

                    buoys.Add(Tuple.Create(new Circle(center, var), var > 35 ? Buoy.Green : Buoy.Red));
                }
            }

            if (buoys.Count > 0 && buoys.Exists(b => b.Item2 == color))
            {
                Circle buoy = buoys.OrderBy(b => b.Item1.Distance(Robots.MainRobot.Position.Coordinates)).First(b => b.Item2 == color).Item1;

                RealPoint entryFrontPoint = GetEntryFrontPoint();
                RealPoint entryBackPoint = GetEntryBackPoint();

                AngleDelta bestAngle = 0;
                double bestError = int.MaxValue;

                for (AngleDelta i = 0; i < 360; i++)
                {
                    Segment inter = new Segment(entryBackPoint.Rotation(i, Robots.MainRobot.Position.Coordinates), buoy.Center);
                    double error = inter.Distance(entryFrontPoint.Rotation(i, Robots.MainRobot.Position.Coordinates));
                    if (error < bestError)
                    {
                        bestError = error;
                        bestAngle = i;
                    }
                }

                bestAngle = -bestAngle.Modulo();

                if (bestAngle < 0)
                    Robots.MainRobot.PivotRight(-bestAngle);
                else
                    Robots.MainRobot.PivotLeft(bestAngle);

                DoGrabOpen();

                int dist = (int)GetEntryFrontPoint().Distance(buoy.Center) + 50;
                Robots.MainRobot.Move(dist);

                DoSequencePickupColor(color); // TODO Détecter la couleur au lidar ?
                //DoSequencePickup();// ... ou pas...

                DoGrabClose();
                Robots.MainRobot.Move(-dist);

                //DoSearchBuoy();
                output = true;

                if (Robots.Simulation)
                    GameBoard.RemoveVirtualBuoy(buoy.Center);
            }
            else
            {
                output = false;
            }

            return output;
        }

        protected abstract RealPoint GetEntryFrontPoint();
        protected abstract RealPoint GetEntryBackPoint();

        public bool DetectSomething()
        {
            return Robots.MainRobot.ReadSensorOnOff(_sensor);
        }

        public void DoDemoGrabLoop()
        {
            int delay = 220;
            DoGrabOpen();
            Thread.Sleep(delay);
            DoGrabClose();
            Thread.Sleep(delay);
            DoGrabOpen();
            Thread.Sleep(delay);
            DoGrabClose();
            Thread.Sleep(delay);
        }

        public void DoDemoDropoff()
        {
            Robots.MainRobot.SetSpeedSlow();
            DoSequenceDropOff();
            DoGrabRelease();
            Robots.MainRobot.MoveForward(85);
            Robots.MainRobot.MoveBackward(85);
            Robots.MainRobot.SetSpeedFast();
        }

        public Color DoSequenceDropOff()
        {
            DoElevatorStop();
            DoGrabRelease();

            Tuple<PositionLoad, PositionFloor> place = PlaceToDropoff();
            Color c = GetColor(place);

            DoPlaceLoad(place.Item1, true);
            DoPlaceFloor(place.Item2);

            if (place.Item2 != PositionFloor.Ground)
            {
                DoAirLock();

                WaitSomething(500);
                DoLockerMaintain();
                Robots.MainRobot.SetMotorAtPosition(_elevator.ID, _elevator.PositionFloor0, true);
            }

            DoAirUnlockDropoff();
            BuoyRemove(place);

            return c;
        }

        private void DoPlaceLoad(PositionLoad load, bool wait)
        {
            if (IsPositionLoadSecond())
            {
                if (load == PositionLoad.First)
                {
                    DoElevatorGround();
                    DoPushInside();
                    if (wait) Thread.Sleep(750); // TODO régler la tempo
                }
            }
            else
            {
                if (load == PositionLoad.Second)
                {
                    DoElevatorGround();
                    DoPushOutside();
                    if (wait) Thread.Sleep(750); // TODO régler la tempo
                }
            }
        }

        private void DoPlaceFloor(PositionFloor floor)
        {
            switch (floor)
            {
                case PositionFloor.Ground:
                    DoElevatorGround();
                    break;
                case PositionFloor.Floor1:
                    DoElevatorFloor1();
                    break;
                case PositionFloor.Floor2:
                    DoElevatorFloor2();
                    break;
                case PositionFloor.Floor3:
                    DoElevatorFloor3();
                    break;
            }
        }

        public void DoSequenceDropOff3()
        {
            Robots.MainRobot.SetSpeedSlow();
            DoSequenceDropOff();
            Robots.MainRobot.Move(-85, false);
            DoSequenceDropOff();
            Robots.MainRobot.Move(-85, false);
            DoSequenceDropOff();
            Robots.MainRobot.Move(-85, false);
            Robots.MainRobot.SetSpeedFast();
        }

        private Tuple<PositionLoad, PositionFloor> PlaceToPickup()
        {
            return _pickupOrder.Find(o => (o.Item1 == PositionLoad.First ? _buoysFirst[(int)o.Item2] : _buoysSecond[(int)o.Item2]) == Color.Transparent);
        }

        private Tuple<PositionLoad, PositionFloor> PlaceToPickupBack()
        {
            return _pickupOrder.Find(o => o.Item1 == PositionLoad.Second && _buoysSecond[(int)o.Item2] == Color.Transparent);
        }

        private Tuple<PositionLoad, PositionFloor> PlaceToDropoff()
        {
            return _dropoffOrder.Find(o => (o.Item1 == PositionLoad.First ? _buoysFirst[(int)o.Item2] : _buoysSecond[(int)o.Item2]) != Color.Transparent);
        }

        private Color GetColor(Tuple<PositionLoad, PositionFloor> place)
        {
            if (place.Item1 == PositionLoad.First)
                return _buoysFirst[(int)place.Item2];
            else
                return _buoysSecond[(int)place.Item2];
        }
    }
}
<file_sep>/GoBot/GoBot/Strategies/StrategyRandomMoves.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using Geometry;
using Geometry.Shapes;
using AStarFolder;

namespace GoBot.Strategies
{
    /// <summary>
    /// Stratégie qui consiste à se déplacer alétoirement sur la piste
    /// </summary>
    class StrategyRandomMoves : Strategy
    {
        Random rand = new Random();

        public override bool AvoidElements => false;

        protected override void SequenceBegin()
        {
            Robots.MainRobot.SpeedConfig.SetParams(500, 2000, 2000, 800, 2000, 2000);
            
            // Sortir ICI de la zone de départ pour commencer
        }

        protected override void SequenceCore()
        {
            while (IsRunning)
            {
                int next = rand.Next(Robots.MainRobot.Graph.Nodes.Count);
                if (!((Node)Robots.MainRobot.Graph.Nodes[next]).Passable)
                    continue;

                Position destination = new Position(rand.Next(360), ((Node)Robots.MainRobot.Graph.Nodes[next]).Position);

                Robots.MainRobot.Historique.Log("Nouvelle destination " + destination.ToString());
                Robots.MainRobot.GoToPosition(destination);
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/RealPointWithSegment.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class RealPointWithSegment
    {
        public static bool Contains(RealPoint containingPoint, Segment containedSegment)
        {
            // Un point qui contient un segment c'est que les deux extremités du segment sont ce même point...
            return (containingPoint == containedSegment.StartPoint) && (containingPoint == containedSegment.EndPoint);
        }

        public static bool Cross(RealPoint point, Segment segment)
        {
            return SegmentWithRealPoint.Cross(segment, point);
        }

        public static double Distance(RealPoint point, Segment segment)
        {
            return SegmentWithRealPoint.Distance(segment, point);
        }

        public static List<RealPoint> GetCrossingPoints(RealPoint point, Segment segment)
        {
            return SegmentWithRealPoint.GetCrossingPoints(segment, point);
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/Lifter.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Devices;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace GoBot.Actionneurs
{
    class Lifter
    {
        private List<ServoClamp> _clamps;
        private ServoLifter _lifter;
        private ServoTilter _tilter;

        private List<Color> _load;

        private bool _tilterStored, _lifterStored;

        public Lifter()
        {
            _clamps = new List<ServoClamp> { Config.CurrentConfig.ServoClamp1, Config.CurrentConfig.ServoClamp2, Config.CurrentConfig.ServoClamp3, Config.CurrentConfig.ServoClamp4, Config.CurrentConfig.ServoClamp5 };
            _lifter = Config.CurrentConfig.ServoLifter;
            _tilter = Config.CurrentConfig.ServoTilter;

            _tilterStored = true;
            _lifterStored = true;
        }

        public bool Loaded => _load != null;
        public bool Opened => !_tilterStored || !_lifterStored;

        public List<Color> Load
        {
            get { return _load; }
            set { _load = value; }
        }

        public void DoOpenAll()
        {
            _clamps.ForEach(o => o.SendPosition(o.PositionOpen));
        }

        public void DoMaintainAll()
        {
            _clamps.ForEach(o => o.SendPosition(o.PositionMaintain));
        }

        public void DoCloseAll()
        {
            _clamps.ForEach(o => o.SendPosition(o.PositionClose));
        }

        public void DoStoreAll()
        {
            _clamps.ForEach(o => o.SendPosition(o.PositionStore));
        }

        public void DoDisableAll()
        {
            _clamps.ForEach(o => o.DisableTorque());
        }

        public void DoLoop()
        {
            for (int i = 0; i < 10; i++)
            {
                _clamps.ForEach(o =>
                {
                    o.SendPosition(o.PositionOpen);
                    Thread.Sleep(100);
                });

                _clamps.ForEach(o =>
                {
                    o.SendPosition(o.PositionStore);
                    Thread.Sleep(100);
                });
            }
        }

        public void DoLifterPositionExtract()
        {
            _lifterStored = false;
            _lifter.SendPosition(_lifter.PositionExtract);
        }

        public void DoLifterPositionStore()
        {
            _lifterStored = true;
            _lifter.SendPosition(_lifter.PositionStore);
        }

        public void DoTilterPositionStore()
        {
            _tilterStored = true;
            _tilter.SendPosition(_tilter.PositionStore);
        }

        public void DoTilterPositionPickup()
        {
            _tilterStored = false;
            _tilter.SendPosition(_tilter.PositionPickup);
        }

        public void DoTilterPositionExtract()
        {
            _tilterStored = false;
            _tilter.SendPosition(_tilter.PositionExtract);
        }

        public void DoTilterPositionDropoff()
        {
            _tilterStored = false;
            _tilter.SendPosition(_tilter.PositionDropoff);
        }

        public void DoSequencePickup()
        {
            Robots.MainRobot.SetSpeedVerySlow();
            Robots.MainRobot.Recalibration(SensAR.Arriere, true, true);

            Robots.MainRobot.SetSpeedSlow();
            Robots.MainRobot.Move(20);
            DoOpenAll();
            DoTilterPositionPickup();
            Thread.Sleep(500);
            DoCloseAll();
            Thread.Sleep(400);
            DoLifterPositionExtract();
            Thread.Sleep(450);
            Robots.MainRobot.Move(100);
            DoLifterPositionStore();
            Thread.Sleep(150);
            DoTilterPositionStore();
            Robots.MainRobot.SetSpeedFast();
        }

        public void DoSequenceDropOff()
        {
            DoTilterPositionDropoff();
            Thread.Sleep(500);
            DoOpenAll();
            DoTilterPositionStore();
            Thread.Sleep(150);
            DoStoreAll();
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/Flags.cs
﻿namespace GoBot.Actionneurs
{
    class Flags
    {
        bool _leftOpened, _rightOpened;

        public bool LeftOpened => _leftOpened;
        public bool RightOpened => _rightOpened;

        public Flags()
        {
            _leftOpened = false;
            _rightOpened = false;
        }

        public void DoCloseRight()
        {
            Config.CurrentConfig.ServoFlagRight.SendPosition(Config.CurrentConfig.ServoFlagRight.PositionClose);
            _rightOpened = false;
        }

        public void DoOpenRight()
        {
            Config.CurrentConfig.ServoFlagRight.SendPosition(Config.CurrentConfig.ServoFlagRight.PositionOpen);
            _rightOpened = true;
        }

        public void DoCloseLeft()
        {
            Config.CurrentConfig.ServoFlagLeft.SendPosition(Config.CurrentConfig.ServoFlagLeft.PositionClose);
            _leftOpened = false;
        }

        public void DoOpenLeft()
        {
            Config.CurrentConfig.ServoFlagLeft.SendPosition(Config.CurrentConfig.ServoFlagLeft.PositionOpen);
            _leftOpened = true;
        }
    }
}
<file_sep>/GoBot/Extensions/ColorPlus.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;

internal struct ColorPlus
{
    #region Fields

    private Color _innerColor;

    #endregion

    #region Properties

    /// <summary>
    /// Obtient ou définit la couleur selon sa valeur 32 bits.
    /// </summary>
    public int ARGB
    {
        get
        {
            return _innerColor.ToArgb();
        }
        set
        {
            _innerColor = Color.FromArgb(value);
        }
    }

    /// <summary>
    /// Obtient ou définit la transparence de la couleur (0 = Transparent, 255 = Opaque).
    /// </summary>
    public byte Alpha
    {
        get
        {
            return _innerColor.A;
        }

        set
        {
            _innerColor = Color.FromArgb(value, _innerColor.R, _innerColor.G, _innerColor.B);
        }
    }

    /// <summary>
    /// Obtient ou définit la composante rouge de la couleur.
    /// </summary>
    public byte Red
    {
        get
        {
            return _innerColor.R;
        }

        set
        {
            _innerColor = Color.FromArgb(_innerColor.A, value, _innerColor.G, _innerColor.B);
        }
    }

    /// <summary>
    /// Obtient ou définit la composante verte de la couleur.
    /// </summary>
    public byte Green
    {
        get
        {
            return _innerColor.G;
        }

        set
        {
            _innerColor = Color.FromArgb(_innerColor.A, _innerColor.R, value, _innerColor.B);
        }
    }

    /// <summary>
    /// Obtient ou définit la composante bleue de la couleur.
    /// </summary>
    public byte Blue
    {
        get
        {
            return _innerColor.B;
        }

        set
        {
            _innerColor = Color.FromArgb(_innerColor.A, _innerColor.R, _innerColor.G, value);
        }
    }

    /// <summary>
    /// Obtient ou définit la teinte de la couleur (0° à 360°).
    /// </summary>
    public double Hue
    {
        get
        {
            return _innerColor.GetHue();
        }

        set
        {
            _innerColor = ColorPlus.FromAhsl(_innerColor.A, value, _innerColor.GetSaturation(), _innerColor.GetBrightness());
        }
    }


    /// <summary>
    /// Obtient ou définit la saturation de la couleur (0 = Terne, 1 = Vive).
    /// </summary>
    public double Saturation
    {
        get
        {
            return _innerColor.GetSaturation();
        }

        set
        {
            _innerColor = ColorPlus.FromAhsl(_innerColor.A, _innerColor.GetHue(), value, _innerColor.GetBrightness());
        }
    }

    /// <summary>
    /// Obtient ou définit la luminosité de la couleur (0 = Sombre, 1 = Claire)
    /// </summary>
    public double Lightness
    {
        get
        {
            return _innerColor.GetBrightness();
        }

        set
        {
            _innerColor = ColorPlus.FromAhsl(_innerColor.A, _innerColor.GetHue(), _innerColor.GetSaturation(), value);
        }
    }

    #endregion

    #region Factory

    /// <summary>
    /// Créée une couleur depuis les paramètres de rouge, bleu et vert.
    /// </summary>
    /// <param name="r">Composante rouge, de 0 à 255.</param>
    /// <param name="g">Composante verte, de 0 à 255.</param>
    /// <param name="b">Composante bleue, de 0 à 255.</param>
    /// <returns>Couleur correspondante.</returns>
    public static ColorPlus FromRgb(int r, int g, int b)
    {
        return FromArgb(255, r, g, b);
    }

    /// <summary>
    /// Créée une couleur depuis les paramètres de transparence, rouge, bleu et vert.
    /// </summary>
    /// <param name="alpha">Transparence, de 0 à 255.</param>
    /// <param name="r">Composante rouge, de 0 à 255.</param>
    /// <param name="g">Composante verte, de 0 à 255.</param>
    /// <param name="b">Composante bleue, de 0 à 255.</param>
    /// <returns>Couleur correspondante.</returns>
    public static ColorPlus FromArgb(int alpha, int r, int g, int b)
    {
        if (0 > alpha || 255 < alpha)
            throw new ArgumentOutOfRangeException(nameof(alpha));
        if (0 > r || 255 < r)
            throw new ArgumentOutOfRangeException(nameof(r));
        if (0 > g || 255 < g)
            throw new ArgumentOutOfRangeException(nameof(g));
        if (0 > b || 255 < b)
            throw new ArgumentOutOfRangeException(nameof(b));

        ColorPlus color = new ColorPlus();
        color._innerColor = Color.FromArgb(alpha, r, g, b);

        return color;
    }

    /// <summary>
    /// Créée une couleur depuis les paramètres de teinte, saturation et luminosité.
    /// </summary>
    /// <param name="hue">Teinte de 0° à 360°.</param>
    /// <param name="saturation">Saturation de 0 à 1.</param>
    /// <param name="lighting">Luminosité de 0 à 1.</param>
    /// <returns>Couleur correspondante.</returns>
    public static ColorPlus FromHsl(double hue, double saturation, double lighting)
    {
        return FromAhsl(255, hue, saturation, lighting);
    }

    /// <summary>
    /// Créée une couleur depuis les paramètres de transparence, teinte, saturation et luminosité.
    /// </summary>
    /// <param name="alpha">Transparence, de 0 à 255.</param>
    /// <param name="hue">Teinte de 0° à 360°.</param>
    /// <param name="saturation">Saturation de 0 à 1.</param>
    /// <param name="lighting">Luminosité de 0 à 1.</param>
    /// <returns>Couleur correspondante.</returns>
    public static ColorPlus FromAhsl(int alpha, double hue, double saturation, double lighting)
    {
        if (0 > alpha || 255 < alpha)
            throw new ArgumentOutOfRangeException(nameof(alpha));
        if (0f > hue || 360f < hue)
            throw new ArgumentOutOfRangeException(nameof(hue));
        if (0f > saturation || 1f < saturation)
            throw new ArgumentOutOfRangeException(nameof(saturation));
        if (0f > lighting || 1f < lighting)
            throw new ArgumentOutOfRangeException(nameof(lighting));

        if (0 == saturation)
        {
            return Color.FromArgb(alpha, Convert.ToInt32(lighting * 255), Convert.ToInt32(lighting * 255), Convert.ToInt32(lighting * 255));
        }

        double fMax, fMid, fMin;
        int sextant, iMax, iMid, iMin;

        if (0.5 < lighting)
        {
            fMax = lighting - (lighting * saturation) + saturation;
            fMin = lighting + (lighting * saturation) - saturation;
        }
        else
        {
            fMax = lighting + (lighting * saturation);
            fMin = lighting - (lighting * saturation);
        }

        sextant = (int)Math.Floor(hue / 60f);

        if (300f <= hue)
            hue -= 360f;

        hue /= 60f;
        hue -= 2f * (double)Math.Floor(((sextant + 1f) % 6f) / 2f);

        if (0 == sextant % 2)
            fMid = hue * (fMax - fMin) + fMin;
        else
            fMid = fMin - hue * (fMax - fMin);

        iMax = Convert.ToInt32(fMax * 255);
        iMid = Convert.ToInt32(fMid * 255);
        iMin = Convert.ToInt32(fMin * 255);

        ColorPlus color = new ColorPlus();

        switch (sextant)
        {
            case 1:
                color._innerColor = Color.FromArgb(alpha, iMid, iMax, iMin);
                break;
            case 2:
                color._innerColor = Color.FromArgb(alpha, iMin, iMax, iMid);
                break;
            case 3:
                color._innerColor = Color.FromArgb(alpha, iMin, iMid, iMax);
                break;
            case 4:
                color._innerColor = Color.FromArgb(alpha, iMid, iMin, iMax);
                break;
            case 5:
                color._innerColor = Color.FromArgb(alpha, iMax, iMin, iMid);
                break;
            default:
                color._innerColor = Color.FromArgb(alpha, iMax, iMid, iMin);
                break;
        }

        return color;
    }

    /// <summary>
    /// Retourne la couleur fluo équivalente.
    /// </summary>
    /// <param name="color">Couleur d'origine.</param>
    /// <returns>Couleur fluo.</returns>
    public static ColorPlus GetFluo(ColorPlus color)
    {
        return ColorPlus.FromHsl(color.Hue, 1, 0.6);
    }

    /// <summary>
    /// Retourne la couleur intense équivalente.
    /// </summary>
    /// <param name="color">Couleur d'origine.</param>
    /// <returns>Couleur intense.</returns>
    public static ColorPlus GetIntense(ColorPlus color)
    {
        return ColorPlus.FromHsl(color.Hue, 1, 0.4);
    }

    /// <summary>
    /// Retourne la couleur pastel équivalente.
    /// </summary>
    /// <param name="color">Couleur d'origine.</param>
    /// <returns>Couleur pastel.</returns>
    public static ColorPlus GetPastel(ColorPlus color)
    {
        return ColorPlus.FromHsl(color.Hue, 1, 0.85);
    }

    /// <summary>
    /// Retourne la couleur très estompée équivalente.
    /// </summary>
    /// <param name="color">Couleur d'origine.</param>
    /// <returns>Couleur très estompée.</returns>
    public static ColorPlus GetVeryLight(ColorPlus color)
    {
        return ColorPlus.FromHsl(color.Hue, 1, 0.93);
    }

    /// <summary>
    /// Retourne la couleur sombre équivalente.
    /// </summary>
    /// <param name="color">Couleur d'origine.</param>
    /// <returns>Couleur sombre.</returns>
    public static ColorPlus GetDark(ColorPlus color)
    {
        return ColorPlus.FromHsl(color.Hue, 1, 0.3);
    }

    /// <summary>
    /// Retourne la couleur terne équivalente.
    /// </summary>
    /// <param name="color">Couleur d'origine.</param>
    /// <returns>Couleur terne.</returns>
    public static ColorPlus GetDull(ColorPlus color)
    {
        return ColorPlus.FromHsl(color.Hue, 0.2, 0.75);
    }

    /// <summary>
    /// Retourne la couleur connue la plus similaire à la couleur inconnue.
    /// </summary>
    /// <param name="unknown">Couleur inconnue.</param>
    /// <param name="knowns">Couleurs connues.</param>
    /// <returns>Couleur connue la plus proche de la couleur inconnue.</returns>
    public static ColorPlus GuessColor(ColorPlus unknown, IEnumerable<ColorPlus> knowns)
    {
        return knowns.OrderBy(known => HueDelta(unknown, known)).First();
    }

    #endregion

    #region Public methods

    /// <summary>
    /// Calcule la différence angulaire en degrés de la teinte entre deux couleurs.
    /// </summary>
    /// <param name="c1">Première couleur.</param>
    /// <param name="c2">Deuxième couleur.</param>
    /// <returns>Différence angulaire.</returns>
    public static double HueDelta(ColorPlus c1, ColorPlus c2)
    {
        double d1 = Math.Abs(c1.Hue - c2.Hue);
        double d2 = Math.Abs(c1.Hue - (c2.Hue - 360));
        double d3 = Math.Abs(c1.Hue - (c2.Hue + 360));

        return new double[] { d1, d2, d3 }.Min();
    }

    #endregion

    #region Operators

    public static implicit operator ColorPlus(Color color)
    {
        return new ColorPlus { _innerColor = color };
    }

    public static implicit operator Color(ColorPlus color)
    {
        return Color.FromArgb(color.Alpha, color.Red, color.Green, color.Blue);
    }

    public static implicit operator ColorPlus(int color)
    {
        return new ColorPlus { ARGB = color };
    }

    public static implicit operator int(ColorPlus color)
    {
        return color.ARGB;
    }

    public override string ToString()
    {
        return "{R = " + this.Red.ToString() + ", G = " + this.Green.ToString() + ", B = " + this.Blue.ToString() + "} - {H = " + this.Hue.ToString("0") + "°, S = " + (this.Saturation * 100).ToString("0") + "%, L = " + (this.Lightness * 100).ToString("0") + "%}";
    }

    #endregion
}
<file_sep>/GoBot/Geometry/README.txt
﻿Le précision des calculs (et principalement pour valider une proximité ou comparer des données très proches) est volontairement bridée.
La constante RealPoint.PRECISION est à utiliser partout où on veut utiliser cette marge de précision. (Tests d'égalité, Tests unitaires, etc)

IShape.GetCrossingPoints 
	- Retourne une liste exhaustive et non dupliquée des croisements entre les deux formes.
	- Si les formes ne se croisent pas, le résultat attendu est une liste vide.
	- En cas de forme identique, le comportement est à définir par le concepteur, mais la liste doit contenir au minimum un point.

IShape.Contains
	- Une forme contient forcément une forme identique

IShape.Cross
	- Doit être utilisé plutôt que de tester si GetCrossingPoints retourne une liste vide
	- Est implémenté le plus simplement possible, pour être moins couteux à l'appel que GetCrossingPoints

IShape.Distance
	- Retourne la distance entre les deux points les plus proches du contour des deux formes concernées
	- Retourne 0 si les formes se croisent
	- Retourne 0 si les formes se contiennent<file_sep>/GoBot/GoBot/Movements/MovementRedDropoff.cs
﻿using Geometry.Shapes;
using GoBot.Actionneurs;
using GoBot.BoardContext;
using GoBot.GameElements;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace GoBot.Movements
{
    class MovementRedDropoff : Movement
    {
        private RandomDropOff _randomDropOff;

        public MovementRedDropoff(RandomDropOff dropoff)
        {
            _randomDropOff = dropoff;

            Positions.Add(new Geometry.Position(90, new RealPoint(_randomDropOff.Position.X + (_randomDropOff.Position.X < 1500 ? 50 : -50), 730 + 600 - 30)));
        }

        public override bool CanExecute => !_randomDropOff.HasLoadOnBottom && Actionneur.Lifter.Loaded;

        public override int Score
        {
            get
            {
                if (!_randomDropOff.HasLoadOnTop)
                    return 8;
                else
                    return 22 - 8;
            }
        }

        public override double Value => 2;

        public override GameElement Element => _randomDropOff;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => _randomDropOff.Owner;

        protected override void MovementBegin()
        {
        }

        protected override bool MovementCore()
        {
            Actionneur.Lifter.DoSequenceDropOff();
            _randomDropOff.SetLoadBottom(Actionneur.Lifter.Load);
            Actionneur.Lifter.Load = null;
            GameBoard.AddObstacle(new Segment(new RealPoint(_randomDropOff.Position.X - 200, 525 + 600 - 30), new RealPoint(_randomDropOff.Position.X + 200, 525 + 600 - 30)));

            if (_randomDropOff.Owner == GameBoard.ColorLeftBlue)
            {
                GameBoard.Elements.FindBuoy(new RealPoint(300, 1200)).IsAvailable = false;
                GameBoard.Elements.FindBuoy(new RealPoint(450, 1100)).IsAvailable = false;
            }
            else
            {
                GameBoard.Elements.FindBuoy(new RealPoint(3000 - 300, 1200)).IsAvailable = false;
                GameBoard.Elements.FindBuoy(new RealPoint(3000 - 450, 1100)).IsAvailable = false;
            }

            return true;
        }

        protected override void MovementEnd()
        {
        }
    }
}
<file_sep>/GoBot/GoBot/BoardContext/Obstacles.cs
﻿using Geometry.Shapes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GoBot.GameElements;

namespace GoBot.BoardContext
{
    public class Obstacles
    {
        private List<IShape> _boardObstacles;
        private Dictionary<ColorPlus, IEnumerable<IShape>> _colorObstacles;

        private IEnumerable<IShape> _detectionObstacles;

        private AllGameElements _elements;

        public delegate void ObstaclesChangedDelegate();
        public event ObstaclesChangedDelegate ObstaclesChanged;

        public Obstacles(AllGameElements elements)
        {
            _boardObstacles = CreateBoardObstacles();
            _colorObstacles = CreateColorObstacles();
            _elements = elements;
            _elements.ObstaclesChanged += _elements_ObstaclesChanged;
            _detectionObstacles = new List<IShape>();
        }

        public IEnumerable<IShape> FromAll
        {
            get
            {
                IEnumerable<IShape> all = _boardObstacles;

                all = all.Concat(_elements.AsObstacles);
                if (_colorObstacles.ContainsKey(GameBoard.MyColor)) all = all.Concat(_colorObstacles[GameBoard.MyColor]);
                if (_detectionObstacles != null) all = all.Concat(_detectionObstacles);

                return all.ToList();
            }
        }

        public IEnumerable<IShape> FromAllExceptBoard
        {
            get
            {
                IEnumerable<IShape> all = _elements.AsObstacles;

                if (_colorObstacles.ContainsKey(GameBoard.MyColor)) all = all.Concat(_colorObstacles[GameBoard.MyColor]);
                if (_detectionObstacles != null) all = all.Concat(_detectionObstacles);

                return all.ToList();
            }
        }

        public IEnumerable<IShape> FromBoardConstruction
        {
            get
            {
                return _boardObstacles.Skip(4).ToList(); // 4 = les 4 contours
            }
        }

        public IEnumerable<IShape> FromBoard
        {
            get
            {
                return _boardObstacles;
            }
        }

        public IEnumerable<IShape> FromColor
        {
            get
            {
                return _colorObstacles[GameBoard.MyColor];
            }
        }

        public IEnumerable<IShape> FromDetection
        {
            get
            {
                return _detectionObstacles;
            }
        }

        public IEnumerable<IShape> FromElements
        {
            get
            {
                return GameBoard.Elements.AsObstacles;
            }
        }

        public void AddObstacle(IShape shape)
        {
            _boardObstacles.Add(shape);
        }

        public void SetDetections(IEnumerable<IShape> detections)
        {
            _detectionObstacles = detections;
            this.OnObstaclesChanged();
        }

        protected void OnObstaclesChanged()
        {
            ObstaclesChanged?.Invoke();
        }

        private List<IShape> CreateBoardObstacles()
        {
            List<IShape> obstacles = new List<IShape>();

            // Contours du plateau
            obstacles.Add(new Segment(new RealPoint(0, 0), new RealPoint(GameBoard.Width - 4, 0)));
            obstacles.Add(new Segment(new RealPoint(GameBoard.Width - 4, 0), new RealPoint(GameBoard.Width - 4, GameBoard.Height - 4)));
            obstacles.Add(new Segment(new RealPoint(GameBoard.Width - 4, GameBoard.Height - 4), new RealPoint(0, GameBoard.Height - 4)));
            obstacles.Add(new Segment(new RealPoint(0, GameBoard.Height - 4), new RealPoint(0, 0)));

            // TODOEACHYEAR : créer les obstacles fixes du plateau

            // Récifs
            obstacles.Add(new PolygonRectangle(new RealPoint(889, 1850), 22, 150));
            obstacles.Add(new PolygonRectangle(new RealPoint(1489, 1700), 22, 300));
            obstacles.Add(new PolygonRectangle(new RealPoint(2089, 1850), 22, 150));

            //Boussole
            obstacles.Add(new PolygonRectangle(new RealPoint(3000/2-300/2, 0), 300, 30));

            return obstacles;
        }

        private Dictionary<ColorPlus, IEnumerable<IShape>> CreateColorObstacles()
        {
            Dictionary<ColorPlus, IEnumerable<IShape>> obstacles = new Dictionary<ColorPlus, IEnumerable<IShape>>();

            // TODOEACHYEAR : créer les obstacles fixes en fonction de la couleur (genre zones réservées à l'adversaire)

            List<IShape> obsLeft = new List<IShape>();
            List<IShape> obsRight = new List<IShape>();

            //Bande de droite (zone de départ etc)
            obsLeft.Add(new PolygonRectangle(new RealPoint(2550, 0), 450, 2000));
            //Port secondaire
            obsLeft.Add(new Circle(new RealPoint(1200, 1800), 150));
            
            //Bande de gauche (zone de départ etc)
            obsRight.Add(new PolygonRectangle(new RealPoint(0, 0), 450, 2000));
            //Port secondaire
            obsRight.Add(new Circle(new RealPoint(1800, 1800), 150));
            
            obstacles.Add(GameBoard.ColorLeftBlue, obsLeft);
            obstacles.Add(GameBoard.ColorRightYellow, obsRight);

            return obstacles;
        }

        private void _elements_ObstaclesChanged()
        {
            this.OnObstaclesChanged();
        }
    }
}
<file_sep>/GoBot/GoBot/GameElements/GameElementZone.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Geometry.Shapes;
using System.Drawing;
using Geometry;

namespace GoBot.GameElements
{
    public class GameElementZone : GameElement
    {
        /// <summary>
        /// Construit une zone de jeu à partir de sa position et de son rayon
        /// </summary>
        /// <param name="position">Position du centre de la zone de jeu</param>
        /// <param name="color">Couleur d'appartenance de la zone</param>
        /// <param name="radius">Rayon de la zone</param>
        public GameElementZone(RealPoint position, Color color, int radius) 
            : base(position, color, radius)
        {
            IsHover = false;
            Owner = color;
        }
        
        /// <summary>
        /// Peint l'élément sur le Graphic donné à l'échelle donnée
        /// </summary>
        /// <param name="g">Graphic sur lequel peindre</param>
        /// <param name="scale">Echelle de peinture</param>
        public override void Paint(Graphics g, WorldScale scale)
        {
            Pen pBlack = new Pen(Color.Black);
            Pen pWhite = new Pen(Color.White);
            Pen pWhiteBold = new Pen(Color.White);

            pWhite.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
            pWhiteBold.Width = 3;

            Rectangle rect = new Rectangle(scale.RealToScreenPosition(Position.Translation(-HoverRadius, -HoverRadius)), scale.RealToScreenSize(new SizeF(HoverRadius * 2, HoverRadius * 2)));

            if (IsHover)
            {
                g.DrawEllipse(pWhiteBold, rect);
            }
            else
            {
                //g.DrawEllipse(pBlack, rect);
                //g.DrawEllipse(pWhite, rect);
            }

            pBlack.Dispose();
            pWhite.Dispose();
            pWhiteBold.Dispose();
        }

        public bool Contains(RealPoint p)
        {
            return new Circle(_position, _hoverRadius).Contains(p);
        }
    }
}
<file_sep>/GoBot/GoBot/PathFinding/Node.cs
using Geometry.Shapes;
using System;
using System.Collections.Generic;


namespace AStarFolder
{
	public class Node
	{
        RealPoint _pos;
		bool _passable;
        
        List<Arc> _incomingArcs;
        List<Arc> _outgoingArcs;
        
        public Node()
        {
            _pos = new RealPoint();
            _passable = true;
            _incomingArcs = new List<Arc>();
            _outgoingArcs = new List<Arc>();
        }

        public Node(RealPoint pos) : this()
        {
            _pos = new RealPoint(pos);
        }

        public Node(double x, double y) : this(new RealPoint(x, y))
        {
        }
        
		public List<Arc> IncomingArcs { get { return _incomingArcs; } }
        
		public List<Arc> OutgoingArcs { get { return _outgoingArcs; } }
        
		public bool Passable
		{
			set
			{
				foreach (Arc A in _incomingArcs) A.Passable = value;
				foreach (Arc A in _outgoingArcs) A.Passable = value;
				_passable = value;
			}
			get
            {
                return _passable;
            }
		}
        
		public double X { get { return _pos.X; } }
        
		public double Y { get { return _pos.Y; } }
        
        public RealPoint Position { get { return _pos; } }
        
		public override string ToString()
        {
            return _pos.ToString();
        }
        
		public override bool Equals(object o)
		{
			Node other = (Node)o;

            return (other != null) && (_pos == other.Position);
		}
        
		public override int GetHashCode()
        {
            return _pos.GetHashCode();
        }
        
		public static double EuclidianDistance(Node n1, Node n2)
		{
			return n1.Position.Distance(n2.Position);
		}
	}
}

<file_sep>/GoBot/GoBot/IHM/Pages/PageLogThreads.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;

using GoBot.Threading;

namespace GoBot.IHM.Pages
{
    public partial class PageLogThreads : UserControl
    {
        private System.Windows.Forms.Timer _timerDisplay;

        public PageLogThreads()
        {
            InitializeComponent();
        }

        private void PanelLogThreads_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                dataGridViewLog.Columns.Add("Id", "Id");
                dataGridViewLog.Columns[0].Width = 30;
                dataGridViewLog.Columns.Add("Nom", "Nom");
                dataGridViewLog.Columns[1].Width = 200;
                dataGridViewLog.Columns.Add("Etat", "Etat");
                dataGridViewLog.Columns[2].Width = 140;
                dataGridViewLog.Columns.Add("Début", "Début");
                dataGridViewLog.Columns[3].Width = 50;
                dataGridViewLog.Columns.Add("Fin", "Fin");
                dataGridViewLog.Columns[4].Width = 50;
                dataGridViewLog.Columns.Add("Durée", "Durée");
                dataGridViewLog.Columns[5].Width = 75;
                dataGridViewLog.Columns.Add("Executions", "Executions");
                dataGridViewLog.Columns[6].Width = 65;
            }
        }

        private void _timerDisplay_Tick(object sender, EventArgs e)
        {
            dataGridViewLog.Rows.Clear();

            foreach (ThreadLink link in ThreadManager.ThreadsLink)
            {
                int row = dataGridViewLog.Rows.Add(
                    link.Id.ToString(), 
                    link.Name, 
                    GetLinkState(link), 
                    link.Started ? link.StartDate.ToString("HH:mm:ss") : "", 
                    link.Ended ? link.EndDate.ToString("HH:mm:ss") : "", 
                    link.Duration.ToString(@"hh\:mm\:ss\.fff"), 
                    (link.LoopsCount > 0 ? link.LoopsCount.ToString() : "") + (link.LoopsTarget > 0 ? " / " + link.LoopsTarget.ToString() : ""));

                dataGridViewLog.Rows[row].DefaultCellStyle.BackColor = GetLinkColor(link);
            }
        }

        private string GetLinkState(ThreadLink link)
        {
            String state = "";

            if (!link.Started)
                state = "Initialisé";
            else if (link.Ended)
                state = "Terminé";
            else if (link.Cancelled)
                state = "Annulé";
            else if (link.LoopPaused)
                state = "En pause";
            else
                state = "En cours d'execution";

            return state;
        }

        private ColorPlus GetLinkColor(ThreadLink link)
        {
            ColorPlus color;

            if (!link.Started)
                color = ColorPlus.GetVeryLight(Color.Purple);
            else if (link.Ended)
                color = Color.LightGray;
            else if (link.Cancelled)
                color = ColorPlus.GetVeryLight(Color.Red);
            else if (link.LoopPaused)
                color = ColorPlus.GetVeryLight(Color.Orange);
            else
                color = ColorPlus.GetVeryLight(Color.Green);

            return color;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if(_timerDisplay == null || _timerDisplay.Enabled == false)
            {
                _timerDisplay = new Timer();
                _timerDisplay.Interval = 1000;
                _timerDisplay.Tick += _timerDisplay_Tick;
                _timerDisplay.Start();

                btnStart.Text = "Stop";
                btnStart.Image = Properties.Resources.Pause16;
            }
            else
            {
                _timerDisplay.Stop();

                btnStart.Text = "Afficher";
                btnStart.Image = Properties.Resources.Play16;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Recalibration.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Devices;
using GoBot.BoardContext;
using System;

namespace GoBot
{
    public static class Recalibration
    {
        private static Position PositionLeft { get; set; }
        private static Position PositionRight { get; set; }

        public static Position StartPosition
        {
            get
            {
                return GameBoard.MyColor == GameBoard.ColorLeftBlue ? PositionLeft : PositionRight;
            }
        }

        public static void Init()
        {
            if (Config.CurrentConfig.IsMiniRobot)
            {
                PositionLeft = new Position(0, new RealPoint(Robots.MainRobot.LenghtTotal / 2, Robots.MainRobot.Width / 2 + 530 + 10 + 250));
                PositionRight = new Position(180, new RealPoint(3000 - PositionLeft.Coordinates.X, PositionLeft.Coordinates.Y + 250));
            }
            else
            {
                PositionLeft = new Position(0, new RealPoint(250, 690));
                PositionRight = new Position(180, new RealPoint(3000 - PositionLeft.Coordinates.X, PositionLeft.Coordinates.Y));
            }
        }

        public static bool GoToCalibration()
        {
            if (Config.CurrentConfig.IsMiniRobot)
            {
                if (GameBoard.ColorLeftBlue == GameBoard.MyColor)
                    return Robots.MainRobot.GoToPosition(new Position(90, new RealPoint(Robots.MainRobot.Width, Robots.MainRobot.Width)));
                else
                    return Robots.MainRobot.GoToPosition(new Position(90, new RealPoint(3000 - Robots.MainRobot.Width, Robots.MainRobot.Width)));
            }
            else
            {
                if (GameBoard.ColorLeftBlue == GameBoard.MyColor)
                    return Robots.MainRobot.GoToPosition(new Position(90, new RealPoint(Robots.MainRobot.Width, Robots.MainRobot.Width)));
                else
                    return Robots.MainRobot.GoToPosition(new Position(90, new RealPoint(3000 - Robots.MainRobot.Width, Robots.MainRobot.Width)));
            }
        }

        public static void Calibration()
        {
            if (Config.CurrentConfig.IsMiniRobot)
            {
                Robots.MainRobot.SendPID(Config.CurrentConfig.GRCoeffP, Config.CurrentConfig.GRCoeffI, Config.CurrentConfig.GRCoeffD);
                Robots.MainRobot.Stop();

                Robots.MainRobot.SetSpeedVerySlow();
                Robots.MainRobot.MoveForward(10);
                Robots.MainRobot.Recalibration(SensAR.Arriere);
                Robots.MainRobot.SetSpeedFast();
                Robots.MainRobot.SetAsservOffset(new Position(Math.Round(Robots.MainRobot.Position.Angle.InPositiveDegrees / 90) * 90,
                    new RealPoint(Robots.MainRobot.Position.Coordinates.X, Robots.MainRobot.LenghtBack)));

                Robots.MainRobot.MoveForward((int)(StartPosition.Coordinates.Y - Robots.MainRobot.LenghtBack));

                if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
                    Robots.MainRobot.PivotRight(90);
                else
                    Robots.MainRobot.PivotLeft(90);

                Robots.MainRobot.SetSpeedVerySlow();
                Robots.MainRobot.Recalibration(SensAR.Arriere);

                Robots.MainRobot.SetAsservOffset(StartPosition);

                Robots.MainRobot.EnableStartTrigger();

                Robots.MainRobot.SetSpeedFast();
            }
            else
            {
                Robots.MainRobot.SetAsservOffset(new Position(92, new RealPoint(GameBoard.MyColor == GameBoard.ColorLeftBlue ? 500 : 2500, 500)));

                Robots.MainRobot.SendPID(Config.CurrentConfig.GRCoeffP, Config.CurrentConfig.GRCoeffI, Config.CurrentConfig.GRCoeffD);
                Robots.MainRobot.Stop();

                Robots.MainRobot.SetSpeedVerySlow();
                Robots.MainRobot.MoveForward(10);
                Robots.MainRobot.Recalibration(SensAR.Arriere, true, true);

                Robots.MainRobot.SetSpeedSlow();
                Robots.MainRobot.MoveForward((int)(StartPosition.Coordinates.Y - Robots.MainRobot.LenghtBack));

                if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
                    Robots.MainRobot.PivotLeft(90);
                else
                    Robots.MainRobot.PivotRight(90);

                Robots.MainRobot.SetSpeedVerySlow();
                Robots.MainRobot.Recalibration(SensAR.Arriere, true, true);

                if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
                    Robots.MainRobot.Move((int)Math.Abs(StartPosition.Coordinates.X - Robots.MainRobot.LenghtBack));
                else
                    Robots.MainRobot.Move((int)Math.Abs((3000 - StartPosition.Coordinates.X) - Robots.MainRobot.LenghtBack));

                Robots.MainRobot.SetAsservOffset(StartPosition);
                Robots.MainRobot.EnableStartTrigger();
                Robots.MainRobot.SetSpeedFast();
            }
        }
    }
}
<file_sep>/GoBot/Composants/ConnectionIndicator.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;

namespace Composants
{
    public partial class ConnectionIndicator : PictureBox
    {
        private Timer BlinkTimer { get; set; }
        private int BlinkCounter { get; set; } = 0;

        /// <summary>
        /// Obtient l'état actuel de la connexion entrante
        /// </summary>
        public bool StateIn { get; protected set; }

        /// <summary>
        /// Obtient l'état actuel de la connexion sortante
        /// </summary>
        public bool StateOut { get; protected set; }

        public ConnectionIndicator()
        {
            InitializeComponent();
            BlinkTimer = new Timer();
            BlinkTimer.Interval = 100;
            BlinkTimer.Tick += new EventHandler(timer_Tick);
            SetConnectionState(false, false);
        }

        void timer_Tick(object sender, EventArgs e)
        {
            Visible = !Visible;
            BlinkCounter++;
            if (BlinkCounter > 7)
                BlinkTimer.Stop();
        }

        /// <summary>
        /// Permet de déterminer l'état des connexions
        /// </summary>
        /// <param name="stateIn">Etat de la connexion entrante</param>
        /// <param name="stateOut">Etat de la connexion sortante</param>
        /// <param name="blink">Si vrai, clignotement si changement d'état</param>
        public void SetConnectionState(bool stateIn, bool stateOut, bool blink = false)
        {
            if (stateIn != StateIn || stateOut != StateOut)
            {
                StateIn = stateIn;
                StateOut = stateOut;

                if (StateIn && StateOut)
                    SetImage(Properties.Resources.ConnectionOk, blink);
                else if (!StateIn && !StateOut)
                    SetImage(Properties.Resources.ConnectionNok, blink);
                else
                    SetImage(Properties.Resources.ConnectionHalf, blink);
            }
        }

        private void SetImage(Image img, bool blink = false)
        {
            BlinkTimer.Stop();
            Visible = true;
            BlinkCounter = 0;
            Image = img;
            if (blink)
                BlinkTimer.Start();
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/AllDevices.cs
﻿using Geometry;
using GoBot.Communications;
using GoBot.Devices.CAN;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows.Forms;

namespace GoBot.Devices
{
    static class AllDevices
    {
        private static CanServos _canServos;
        private static Lidar _lidarGround, _lidarAvoid;
        private static Buzzer _buzzer;

        public static void Init()
        {
            _canServos = new CanServos(Connections.ConnectionCan);
            _buzzer = new Buzzer(Connections.ConnectionCan);

            try
            {
                _lidarGround = new HokuyoRec(LidarID.Ground);

                if (Config.CurrentConfig.IsMiniRobot)
                {
                    _lidarAvoid = new Hokuyo(LidarID.Avoid, "COM3");
                }
                else
                {
                    _lidarAvoid = new Pepperl(IPAddress.Parse("10.1.0.50"));
                    ((Pepperl)_lidarAvoid).SetFrequency(PepperlFreq.Hz20);
                    ((Pepperl)_lidarAvoid).SetFilter(PepperlFilter.Average, 3);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("ERREUR INIT LIDAR : " + ex.Message);
            }
        }

        public static void InitSimu()
        {
            _canServos = new CanServos(Connections.ConnectionCan);
            _lidarGround = new LidarSimu();
            _lidarAvoid = new LidarSimu();
        }

        public static void Close()
        {
            _lidarAvoid?.StopLoopMeasure();
            _lidarGround?.StopLoopMeasure();
        }

        public static CanServos CanServos
        {
            get
            {
                return _canServos;
            }
        }

        public static Buzzer Buzzer => _buzzer;

        public static Lidar LidarGround
        {
            get { return _lidarGround; }
            set { _lidarGround = value; }
        }

        public static Lidar LidarAvoid
        {
            get { return _lidarAvoid; }
            set { _lidarAvoid = value; }
        }

        private static Hokuyo CreateHokuyo(String portCom, LidarID id)
        {
            Hokuyo hok = null;

            try
            {
                hok = new Hokuyo(id, portCom);
            }
            catch (Exception)
            {
            }

            return hok;
        }

        public static void SetRobotPosition(Position pos)
        {
            if (Config.CurrentConfig.IsMiniRobot)
            {
                if (_lidarAvoid != null)
                    _lidarAvoid.Position = new Position(pos.Angle - new AngleDelta(90), pos.Coordinates);
            }
            else
            {
                if (_lidarAvoid != null)
                    _lidarAvoid.Position = new Position(pos.Angle - new AngleDelta(180), pos.Coordinates);
                if (_lidarGround != null)
                {
                    _lidarGround.Position.Coordinates = new Geometry.Shapes.RealPoint(pos.Coordinates.X + Math.Cos(pos.Angle) * 109, pos.Coordinates.Y + Math.Sin(pos.Angle) * 109);
                    _lidarGround.Position.Angle = pos.Angle;
                }
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Robots/Robots.cs
﻿using AStarFolder;
using GoBot.Communications;
using GoBot.Devices;
using System;
using System.Collections.Generic;

namespace GoBot
{
    [Serializable]
    public enum IDRobot
    {
        GrosRobot
    }

    static class Robots
    {
        public static Dictionary<IDRobot, Robot> DicRobots { get; set; }

        public static Robot MainRobot { get; set; }
        public static bool Simulation { get; set; }

        public static void Init()
        {
            Simulation = false;
            CreateRobots();
        }

        private static void CreateRobots()
        {
            Graph graphBackup = null;

            if (Robots.MainRobot != null) graphBackup = Robots.MainRobot.Graph;

            Robots.MainRobot?.DeInit();

            if (!Simulation)
                MainRobot = new RobotReel(IDRobot.GrosRobot, Board.RecMove);
            else
                MainRobot = new RobotSimu(IDRobot.GrosRobot);

            if (Config.CurrentConfig.IsMiniRobot)
            {
                MainRobot.SetDimensions(220, 160, 160, 143.8, 346);
            }
            else
            {
                MainRobot.SetDimensions(335, 135, 130.5, 295, 420);
            }

            MainRobot.PositionChanged += MainRobot_PositionChanged;

            DicRobots = new Dictionary<IDRobot, Robot>();
            DicRobots.Add(IDRobot.GrosRobot, MainRobot);

            MainRobot.Init();
            if (graphBackup != null) Robots.MainRobot.Graph = graphBackup;
            MainRobot.SetSpeedFast();
        }

        private static void MainRobot_PositionChanged(Geometry.Position position)
        {
            AllDevices.SetRobotPosition(position);
        }

        public static void EnableSimulation(bool isSimulation)
        {
            if (Simulation == isSimulation)
                return;

            Simulation = isSimulation;

            CreateRobots();
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/Deplacement/ActionRecallage.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionRecallage : IAction
    {
        private SensAR sens;
        private Robot robot;

        public ActionRecallage(Robot r, SensAR s)
        {
            robot = r;
            sens = s;
        }

        public override String ToString()
        {
            return robot.Name + " recallage " + sens.ToString().ToLower();
        }

        void IAction.Executer()
        {
            robot.Recalibration(sens);
        }

        public System.Drawing.Image Image
        {
            get
            {
                return GoBot.Properties.Resources.BottomLine16;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/UDP/UDPConnection.cs
﻿using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;

namespace GoBot.Communications.UDP
{

    public class UDPConnection : Connection
    {
        private String _name;

        private class UdpState
        {
            public UdpClient Client { get; set; }
            public IPEndPoint EndPoint { get; set; }

            public UdpState(UdpClient client, IPEndPoint endPoint)
            {
                Client = client;
                EndPoint = endPoint;
            }
        }

        public override string Name { get => _name; set => _name = value; }

        public enum ConnectionState
        {
            Ok,
            Error
        }

        /// <summary>
        /// Adresse IP du client
        /// </summary>
        public IPAddress IPAddress { get; private set; }

        /// <summary>
        /// Port écouté par le PC pour la réception de messages
        /// </summary>
        public int InputPort { get; private set; }

        /// <summary>
        /// Port du client sur lequel envoyer les messages
        /// </summary>
        public int OutputPort { get; private set; }

        /// <summary>
        /// Client connecté
        /// </summary>
        private UdpClient Client { get; set; }

        IPEndPoint e;
        UdpClient u;

        public UDPConnection()
        {
            ConnectionChecker = new ConnectionChecker(this, 500);
        }

        /// <summary>
        /// Initialise la connexion vers le client pour l'envoi de données
        /// </summary>
        /// <param name="ipAddress">Adresse Ip du client</param>
        /// <param name="inputPort">Port à écouter</param>
        /// <param name="outputPort">Port sur lequel envoyer les messages</param>
        /// <returns>Etat de la connexion</returns>
        public ConnectionState Connect(IPAddress ipAddress, int inputPort, int outputPort)
        {
            ConnectionState state = ConnectionState.Error;

            IPAddress = ipAddress;
            OutputPort = outputPort;
            InputPort = inputPort;

            lock (this)
            {
                try
                {
                    Client = new UdpClient();
                    Client.Connect(IPAddress, OutputPort);
                    Connected = true;
                    state = ConnectionState.Ok;
                }
                catch (Exception)
                {
                    Connected = false;
                }
            }

            return state;
        }

        /// <summary>
        /// Envoi le message au client actuellement connecté
        /// </summary>
        /// <param name="frame">Message à envoyer au client</param>
        /// <returns>Nombre de caractères envoyés</returns>
        public override bool SendMessage(Frame frame)
        {
            bool ok = false;

            //if (Connections.EnableConnection[frame.Board])
            {
                try
                {
                    if (!Connected)
                        if (Connect(IPAddress, OutputPort, InputPort) != ConnectionState.Ok)
                            return false;

                    byte[] envoi = frame.ToBytes();

                    ok = Client.Send(envoi, envoi.Length) > 0;
                    OnFrameSend(frame);
                }
                catch (SocketException)
                {
                    Connected = false;
                    ok = false;
                }
            }

            return ok;
        }

        /// <summary>
        /// Lance la réception de trames sur le port actuel
        /// </summary>
        public override void StartReception()
        {
            e = new IPEndPoint(IPAddress.Any, InputPort);
            if (u != null)
                u.Close();
            u = new UdpClient(e);

            u.BeginReceive(new AsyncCallback(ReceptionCallback), new UdpState(u, e));
        }

        /// <summary>
        /// Libère la connexion vers le client
        /// </summary>
        public override void Close()
        {
            Client.Close();
        }

        /// <summary>
        /// Callback appelée par la disponibilité d'une trame
        /// </summary>
        /// <param name="ar"></param>
        private void ReceptionCallback(IAsyncResult ar)
        {
            try
            {
                UdpClient u = ((UdpState)(ar.AsyncState)).Client;
                IPEndPoint e = new IPEndPoint(IPAddress.Any, InputPort);
                Byte[] receiveBytes = u.EndReceive(ar, ref e);
                Frame trameRecue = new Frame(receiveBytes);

                ConnectionChecker.NotifyAlive();

                try
                {
                    OnFrameReceived(trameRecue);
                }
                catch (Exception e1)
                {
                    if (Debugger.IsAttached) MessageBox.Show("Trame reçue buguée : " + trameRecue.ToString() + Environment.NewLine + e1.Message, "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

                u.BeginReceive(ReceptionCallback, new UdpState(u, e));
            }
            catch (Exception e)
            {
                Console.WriteLine("ERREUR UDP : " + e.ToString());
            }
        }
    }
}<file_sep>/GoBot/GoBot/Devices/CAN/CanServos.cs
﻿using GoBot.Communications;
using GoBot.Communications.CAN;
using System;
using System.Collections.Generic;
using System.Linq;

namespace GoBot.Devices.CAN
{
    /// <summary>
    /// Dictionnaire à servomoteurs en bus CAN
    /// </summary>
    class CanServos
    {
        private Dictionary<ServomoteurID, CanServo> _servos;

        private CanConnection _communication;
        private List<CanBoard> _canBoards;

        public CanServos(CanConnection comm)
        {
            _communication = comm;
            _communication.FrameReceived += _communication_FrameReceived;

            _servos = new Dictionary<ServomoteurID, CanServo>();
            _canBoards = new List<CanBoard> { CanBoard.CanServo1, CanBoard.CanServo2, CanBoard.CanServo3, CanBoard.CanServo4, CanBoard.CanServo5, CanBoard.CanServo6 };

            Enum.GetValues(typeof(ServomoteurID)).Cast<ServomoteurID>().ToList().ForEach(id => _servos.Add(id, new CanServo(id, _communication)));
        }

        public CanServo this[ServomoteurID servoGlobalId]
        {
            get
            {
                return _servos[servoGlobalId];
            }
        }

        private void _communication_FrameReceived(Frame frame)
        {
            try
            {
                CanBoard idCan = CanFrameFactory.ExtractBoard(frame);

                if (_canBoards.Contains(idCan))
                {
                    ServomoteurID servoGlobalId = CanFrameFactory.ExtractServomoteurID(frame);
                    _servos[servoGlobalId].FrameReceived(frame);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("ERREUR CAN : " + frame.ToString() + " - " + e.Message);
            }
        }

        public void DisableAll()
        {
            _servos.Values.ToList().ForEach(o => o.DisableOutput());
        }
    }
}
<file_sep>/GoBot/Composants/GraphPanel.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Drawing2D;

namespace Composants
{
    public partial class GraphPanel : UserControl
    {
        public enum ScaleType
        {
            DynamicGlobal, // Le min et le max sont fixés par la courbe la plus haute et la courbe la plus basse
            DynamicPerCurve, // Le min et le max sont propre à chaque courbe qui a donc sa propre échelle. Min et max ne sont pas affichés dans ce cas
            Fixed, // Le min et le max sont fixés par MinLimit et MaxLimit
            FixedIfEnough // Le min et le max sont fixés par MinLimit et MaxLimit mais sont outrepassés si insufisants pour couvrir la dynamique
        }

        private Dictionary<String, List<double>> CurvesData { get; set; }
        private Dictionary<String, bool> CurvesDisplayed { get; set; }
        private Dictionary<String, Pen> CurvesPen { get; set; }
        private Dictionary<String, bool> CurvesFilled { get; set; }

        /// <summary>
        /// Définit le type d'échelle utilisée par le graph
        /// </summary>
        public ScaleType ScaleMode { get; set; }

        /// <summary>
        /// Définit la limite inférieur du graph dans le cas où l'échelle est fixe
        /// </summary>
        public double MinLimit { get; set; }

        /// <summary>
        /// Définit la limite supérieure du graph dans le cas où l'échelle est fixe
        /// </summary>
        public double MaxLimit { get; set; }

        /// <summary>
        /// Définit si le nom des courbes est affiché
        /// </summary>
        public bool NamesVisible { get; set; }

        /// <summary>
        /// Définit l'endroit où sont affichés les noms des courbes
        /// </summary>
        public ContentAlignment NamesAlignment { get; set; } = ContentAlignment.BottomLeft;

        /// <summary>
        /// Définit si les limites de l'échelle sont affichées dans le cas où l'échelle est commune
        /// </summary>
        public bool LimitsVisible { get; set; }

        /// <summary>
        /// Définit la couleur de la bordure du graph et de la bordure des libellés
        /// </summary>
        public Color BorderColor { get; set; }

        /// <summary>
        /// Définit si le contour du graph est visible
        /// </summary>
        public bool BorderVisible { get; set; }

        public GraphPanel()
        {
            InitializeComponent();
            CurvesData = new Dictionary<string, List<double>>();
            CurvesDisplayed = new Dictionary<string, bool>();
            CurvesFilled = new Dictionary<string, bool>();

            CurvesPen = new Dictionary<string, Pen>();
            ScaleMode = ScaleType.DynamicGlobal;
            BackColor = Color.White;
            BorderColor = Color.LightGray;
            BorderVisible = false;

            MaxLimit = 1;
            MinLimit = 0;
        }

        /// <summary>
        /// Ajouter un point à la courbe spécifiée
        /// Ajoute la courbe si elle n'existe pas encore
        /// </summary>
        /// <param name="curveName">Nom de la courbe auquel ajouter un point</param>
        /// <param name="value">Valeur à ajouter à la courbe</param>
        /// <param name="col">Couleur à associer à la courbe (null pour ne pas changer la couleur)</param>
        public void AddPoint(String curveName, double value, Color? col = null, bool fill = false)
        {
            lock (CurvesData)
            {
                List<double> data;

                if (CurvesData.ContainsKey(curveName))
                {
                    data = CurvesData[curveName];
                    if (col != null)
                        CurvesPen[curveName] = new Pen(col.Value);

                    CurvesFilled[curveName] = fill;
                }
                else
                {
                    data = new List<double>();
                    CurvesData.Add(curveName, data);
                    CurvesDisplayed.Add(curveName, true);
                    if (col != null)
                        CurvesPen.Add(curveName, new Pen(col.Value));
                    else
                        CurvesPen.Add(curveName, new Pen(Color.Black));

                    CurvesFilled.Add(curveName, fill);
                }

                data.Add(value);

                while (data.Count > pictureBox.Width)
                    data.RemoveAt(0);
            }
        }

        /// <summary>
        /// Met à jour l'affichage des courbes
        /// </summary>
        public void DrawCurves()
        {
            lock (CurvesData)
            {
                Bitmap bmp = new Bitmap(Width, Height);
                Graphics gTemp = Graphics.FromImage(bmp);

                gTemp.SmoothingMode = SmoothingMode.AntiAlias;
                gTemp.Clear(BackColor);

                double min = double.MaxValue;
                double max = double.MinValue;

                if (ScaleMode == ScaleType.Fixed)
                {
                    min = MinLimit;
                    max = MaxLimit;
                }
                else if (ScaleMode == ScaleType.DynamicGlobal)
                {
                    foreach (KeyValuePair<String, List<double>> courbe in CurvesData)
                    {
                        if (CurvesDisplayed[courbe.Key] && courbe.Value.Count > 1)
                        {
                            min = Math.Min(min, courbe.Value.Min());
                            max = Math.Max(max, courbe.Value.Max());
                        }
                    }
                }
                else if (ScaleMode == ScaleType.FixedIfEnough)
                {
                    foreach (KeyValuePair<String, List<double>> courbe in CurvesData)
                    {
                        if (CurvesDisplayed[courbe.Key] && courbe.Value.Count > 1)
                        {
                            min = Math.Min(min, courbe.Value.Min());
                            max = Math.Max(max, courbe.Value.Max());
                        }
                    }

                    min = Math.Min(min, MinLimit);
                    max = Math.Max(max, MaxLimit);
                }

                double coef = max == min ? 1 : (float)(pictureBox.Height - 1) / (max - min);

                foreach (KeyValuePair<String, List<double>> courbe in CurvesData)
                {
                    if (CurvesDisplayed[courbe.Key] && courbe.Value.Count > 1)
                    {
                        if (ScaleMode == ScaleType.DynamicPerCurve)
                        {
                            coef = courbe.Value.Max() == courbe.Value.Min() ? 1 : (float)(pictureBox.Height - 1) / (courbe.Value.Max() - courbe.Value.Min());
                            min = courbe.Value.Min();
                        }

                        ColorPlus startCol, endCol;
                        startCol = CurvesPen[courbe.Key].Color;
                        endCol = startCol;
                        endCol.Lightness = 0.95;

                        List<Point> pts = new List<Point>();

                        for (int i = 0; i < courbe.Value.Count; i++)
                        {
                            pts.Add(new Point(i, (int)((pictureBox.Height - 1) - coef * (courbe.Value[i] - min))));
                            //gTemp.DrawLine(CurvesPen[courbe.Key], new Point(i - 1, (int)((pictureBox.Height - 1) - coef * (courbe.Value[i - 1] - min))), new Point(i, (int)((pictureBox.Height - 1) - coef * (courbe.Value[i] - min))));
                        }

                        if (CurvesFilled[courbe.Key])
                        {
                            List<Point> ptsFill = new List<Point>(pts);
                            
                            ptsFill.Insert(0, new Point(0, (int)((pictureBox.Height - 1) - coef * (0 - min))));
                            ptsFill.Add(new Point(pts[pts.Count - 1].X, (int)((pictureBox.Height - 1) - coef * (0 - min))));
                            
                            Brush b = new LinearGradientBrush(new PointF(0, 0), new PointF(0, pictureBox.Height), endCol, startCol);
                            gTemp.FillPolygon(b, ptsFill.ToArray());
                            b.Dispose();
                        }

                        gTemp.DrawLines(CurvesPen[courbe.Key], pts.ToArray());
                    }
                }

                gTemp.SmoothingMode = SmoothingMode.None;

                Font myFont = new Font("Calibri", 9);

                List<String> namesVisible = CurvesDisplayed.Where(o => CurvesDisplayed[o.Key]).Select(o => o.Key).ToList();

                if (NamesVisible && namesVisible.Count > 0)
                {
                    int maxWidth = namesVisible.Max(c => (int)gTemp.MeasureString(c, myFont).Width);
                    int margin = 5, hPerRow = 10;

                    Rectangle txtRect;
                    Size sz = new Size(maxWidth + margin, (namesVisible.Count * hPerRow) + margin - 1);

                    switch (NamesAlignment)
                    {
                        case ContentAlignment.BottomLeft:
                            txtRect = new Rectangle(0, pictureBox.Height - hPerRow * namesVisible.Count - margin, sz.Width, sz.Height);
                            break;
                        case ContentAlignment.TopLeft:
                            txtRect = new Rectangle(0, 0, sz.Width, sz.Height);
                            break;
                        case ContentAlignment.MiddleLeft:
                            txtRect = new Rectangle(0, pictureBox.Height / 2 - sz.Height / 2, sz.Width, sz.Height);
                            break;
                        default:
                            txtRect = new Rectangle(0, 0, pictureBox.Width, pictureBox.Height);
                            break;
                    }

                    Brush backBsh = new SolidBrush(Color.FromArgb(200, BackColor));
                    gTemp.FillRectangle(backBsh, new Rectangle(txtRect.X, txtRect.Y, txtRect.Width + 1, txtRect.Height + 1));
                    backBsh.Dispose();

                    Pen backPen = new Pen(BorderColor);
                    backPen.DashStyle = DashStyle.Dot;
                    gTemp.DrawRectangle(backPen, txtRect);
                    backPen.Dispose();

                    Point p = new Point(txtRect.X, txtRect.Y);
                    foreach (String courbe in namesVisible)
                    {
                        if (CurvesDisplayed[courbe])
                        {
                            Brush b = new SolidBrush(CurvesPen[courbe].Color);
                            gTemp.DrawString(courbe, myFont, b, 0, p.Y);
                            p.Y += 10;
                            b.Dispose();
                        }
                    }
                }

                if (ScaleMode != ScaleType.DynamicPerCurve && LimitsVisible)
                {
                    String minText = min.ToString("G3");
                    String maxText = max.ToString("G3");
                    SizeF minSize = gTemp.MeasureString(minText, myFont);
                    SizeF maxSize = gTemp.MeasureString(maxText, myFont);

                    gTemp.DrawString(minText, myFont, Brushes.Black, this.Width - 2 - minSize.Width, Height - maxSize.Height - 2);
                    gTemp.DrawString(maxText, myFont, Brushes.Black, this.Width - 2 - maxSize.Width, 2);
                }

                if (BorderVisible)
                {
                    Pen borderPen = new Pen(BorderColor);
                    gTemp.DrawRectangle(borderPen, new Rectangle(0, 0, Width - 1, Height - 1));
                    borderPen.Dispose();
                }

                myFont.Dispose();

                pictureBox.Image = bmp;
            }
        }

        /// <summary>
        /// Supprimer une courbe
        /// </summary>
        /// <param name="curveName">Nom de la courbe à supprimer</param>
        public void DeleteCurve(String curveName)
        {
            if (CurvesData.ContainsKey(curveName))
            {
                CurvesData.Remove(curveName);
                CurvesFilled.Remove(curveName);
                CurvesPen.Remove(curveName);
            }
            if (CurvesDisplayed.ContainsKey(curveName))
            {
                CurvesDisplayed.Remove(curveName);
            }
        }

        /// <summary>
        /// Masque ou affiche une courbe
        /// </summary>
        /// <param name="curveName">Nom de la courbe à masquer ou afficher</param>
        public void ShowCurve(String curveName, bool showed = true)
        {
            if (CurvesDisplayed.ContainsKey(curveName))
            {
                CurvesDisplayed[curveName] = showed;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/GameElements/LightHouse.cs
﻿using System.Drawing;

using Geometry;
using Geometry.Shapes;

namespace GoBot.GameElements
{
    public class LightHouse : GameElement
    {
        private bool _enable;

        public bool Enable
        {
            get { return _enable; }
            set { _enable = value; }
        }

        public LightHouse(RealPoint position, Color owner) : base(position, owner, 100)
        {
            _enable = false;
        }

        public override void Paint(Graphics g, WorldScale scale)
        {
            new Circle(new RealPoint(Position.X, Position.Y), _hoverRadius).Paint(g, Pens.Black, _enable ? Brushes.LimeGreen : Brushes.WhiteSmoke, scale);
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/LineWithPolygon.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class LineWithPolygon
    {
        public static bool Contains(Line containingLine, Polygon containedPolygon)
        {
            // Contenir un polygone revient à contenir tous les cotés du polygone

            return containedPolygon.Sides.TrueForAll(s => containingLine.Contains(s));
        }

        public static bool Cross(Line line, Polygon polygon)
        {
            // On teste si la ligne croise un des cotés du polygone

            return polygon.Sides.Exists(s => s.Cross(line));
        }
        
        public static double Distance(Line line, Polygon polygon)
        {
            // Distance jusqu'au segment le plus proche

            return polygon.Sides.Min(s => s.Distance(line));
        }

        public static List<RealPoint> GetCrossingPoints(Line line, Polygon polygon)
        {
            // Croisements avec tous les segments du polygone

            return polygon.Sides.SelectMany(s => s.GetCrossingPoints(line)).ToList();
        }
    }
}
<file_sep>/GoBot/GoBot/NameFinder.cs
﻿using System;
using System.Reflection;
using GoBot.Actionneurs;
using GoBot.Communications;
using GoBot.Communications.UDP;

namespace GoBot
{
    static class NameFinder
    {
        /// <summary>
        /// Nomme à partir d'un type inconnu en recherchant la meilleure surcharge
        /// </summary>
        /// <param name="o">Objet à nommer</param>
        /// <returns>Nom de l'objet si trouvé, sinon chaine vide</returns>
        public static String GetNameUnknow(object o)
        {
            return GetName(ToRealType(o));
        }

        private static dynamic ToRealType(Object o)
        {
            Type type = o.GetType();
            dynamic pp = Convert.ChangeType(o, type);
            return pp;
        }

        /// <summary>
        /// Nommage d'un objet pour lequel on n'a à priori pas de surcharge adaptée, donc retourne une chaine vide
        /// Fonction privée car aucun intêret de l'appeler depuis l'exterieur
        /// </summary>
        /// <param name="o">Objet à nommer qui n'a pas trouvé de surcharge</param>
        /// <returns>Chaine vide</returns>
        private static String GetName(object o)
        {
            return "";
        }

        /// <summary>
        /// Retourne le nom usuel du servomoteur
        /// </summary>
        /// <param name="servo">Servomoteur recherché</param>
        /// <returns>Nom usuel</returns>
        public static String GetName(ServomoteurID servo)
        {
            switch (servo)
            {
                case ServomoteurID.Clamp1:
                    return "crochet gauche";
                case ServomoteurID.Clamp2:
                    return "crochet milieu gauche";
                case ServomoteurID.Clamp3:
                    return "crochet milieu";
                case ServomoteurID.Clamp4:
                    return "crochet milieu droite";
                case ServomoteurID.Clamp5:
                    return "crochet droite";
                case ServomoteurID.FingerLeft:
                    return "doigt gauche";
                case ServomoteurID.FingerRight:
                    return "doigt droite";
                case ServomoteurID.FlagLeft:
                    return "drapeau gauche";
                case ServomoteurID.FlagRight:
                    return "drapeau droite";
                case ServomoteurID.GrabberLeft:
                    return "rabatteur gauche";
                case ServomoteurID.GrabberRight:
                    return "rabatteur droite";
                case ServomoteurID.Lifter:
                    return "monteur crochets";
                case ServomoteurID.LockerLeft:
                    return "verrouilleur gauche";
                case ServomoteurID.LockerRight:
                    return "verrouilleur droite";
                case ServomoteurID.PushArmLeft:
                    return "bras sortie gauche";
                case ServomoteurID.PushArmRight:
                    return "bras sortie droite";
                case ServomoteurID.Tilter:
                    return "rotationneur crochets";
                default:
                        return servo.ToString();
            }
        }

        /// <summary>
        /// Retourne le nom de la position en fonction du servomoteur (Ouvert, fermé, etc...)
        /// </summary>
        /// <param name="position">Position du servomoteur</param>
        /// <param name="servo">Servomoteur</param>
        /// <returns>Nom de la position</returns>
        public static String GetName(int position, ServomoteurID servo)
        {
            PropertyInfo[] properties = typeof(Config).GetProperties();
            foreach (PropertyInfo p in properties)
            {
                if (p.PropertyType.IsSubclassOf(typeof(PositionableServo)))
                {
                    PositionableServo positionnableServo = (PositionableServo)(p.GetValue(Config.CurrentConfig, null));

                    if (positionnableServo.ID == servo)
                    {
                        PropertyInfo[] proprietesServo = positionnableServo.GetType().GetProperties();
                        foreach (PropertyInfo ps in proprietesServo)
                        {
                            if (ps.Name.StartsWith("Position") && ((int)(ps.GetValue(positionnableServo, null)) == position))
                                return Config.PropertyNameToScreen(ps) + " (" + position + ")";
                        }
                    }
                }
            }
                    
            return position.ToString();
        }

        /// <summary>
        /// Retourne le nom de la position en fonction du moteur (Haut, Bas, etc...)
        /// </summary>
        /// <param name="position">Position du moteur</param>
        /// <param name="moteur">Moteur</param>
        /// <returns>Nom de la position</returns>
        public static String GetName(int position, MotorID moteur)
        {
            PropertyInfo[] properties = typeof(Config).GetProperties();
            foreach (PropertyInfo p in properties)
            {
                if (p.PropertyType.IsSubclassOf(typeof(PositionableMotorPosition)))
                {
                    PositionableMotorPosition positionnableMoteur = (PositionableMotorPosition)(p.GetValue(Config.CurrentConfig, null));

                    if (positionnableMoteur.ID == moteur)
                    {
                        PropertyInfo[] proprietesMoteur = positionnableMoteur.GetType().GetProperties();
                        foreach (PropertyInfo ps in proprietesMoteur)
                        {
                            if (ps.Name.StartsWith("Position") && ((int)(ps.GetValue(positionnableMoteur, null)) == position))
                                return Config.PropertyNameToScreen(ps) + " (" + position + ")";
                        }
                    }
                }
            }

            return position.ToString();
        }

        /// <summary>
        /// Retourne le nom usuel d'un capteur on off
        /// </summary>
        /// <param name="sensor">Capteur on off à nommer</param>
        /// <returns>Nom du capteur on off</returns>
        public static String GetName(SensorOnOffID sensor)
        {
            switch (sensor)
            {
                case SensorOnOffID.StartTrigger:
                    return "jack";
                case SensorOnOffID.PressureSensorLeftBack:
                    return "pression arrière gauche";
                case SensorOnOffID.PressureSensorLeftFront:
                    return "pression avant gauche";
                case SensorOnOffID.PressureSensorRightBack:
                    return "pression arrière droit";
                case SensorOnOffID.PressureSensorRightFront:
                    return "pression avant droit";
                default:
                    return sensor.ToString();
            }
        }

        /// <summary>
        /// Retourne le nom usuel d'un codeur
        /// </summary>
        /// <param name="capteur">Codeur à nommer</param>
        /// <returns>Nom du codeur</returns>
        public static String GetName(CodeurID encoder)
        {
            switch (encoder)
            {
                case CodeurID.Manuel:
                    return "manuel";
                default:
                    return encoder.ToString();
            }
        }

        /// <summary>
        /// Retourne le nom usuel d'un bolléen On/Off
        /// </summary>
        /// <param name="capteur">Valeur à nommer</param>
        /// <returns>"On" ou "Off"</returns>
        public static String GetName(bool val)
        {
            return val ? "On" : "Off";
        }

        /// <summary>
        /// Retourne le nom usuel d'un moteur
        /// </summary>
        /// <param name="capteur">Moteur à nommer</param>
        /// <returns>Nom du Moteur</returns>
        public static String GetName(MotorID motor)
        {
            switch (motor)
            {
                case MotorID.ElevatorLeft:
                    return "ascenseur gauche";
                case MotorID.ElevatorRight:
                    return "ascenseur droite";
                case MotorID.AvailableOnRecIO2:
                    return "RecIO 2";
                case MotorID.AvailableOnRecIO3:
                    return "RecIO 3";
                case MotorID.AvailableOnRecMove11:
                    return "RecMove 1";
                case MotorID.AvailableOnRecMove12:
                    return "RecMove 2";
                default:
                    return motor.ToString();
            }
        }

        /// <summary>
        /// Retourne le nom usuel d'un actionneur On/Off
        /// </summary>
        /// <param name="capteur">Actionneur à nommer</param>
        /// <returns>Nom de l'actionneur</returns>
        public static String GetName(ActuatorOnOffID actuator)
        {
            switch (actuator)
            {
                case ActuatorOnOffID.PowerSensorColorBuoyRight:
                    return "alimentation capteur couleur bouée droite";
                case ActuatorOnOffID.PowerSensorColorBuoyLeft:
                    return "alimentation capteur couleur bouée gauche";
                case ActuatorOnOffID.MakeVacuumLeftBack:
                    return "aspiration arrière gauche";
                case ActuatorOnOffID.MakeVacuumRightBack:
                    return "aspiration arrière droite";
                case ActuatorOnOffID.MakeVacuumLeftFront:
                    return "aspiration avant gauche";
                case ActuatorOnOffID.MakeVacuumRightFront:
                    return "aspiration avant droite";
                case ActuatorOnOffID.OpenVacuumLeftBack:
                    return "electrovanne arrière gauche";
                case ActuatorOnOffID.OpenVacuumRightBack:
                    return "electrovanne arrière droite";
                case ActuatorOnOffID.OpenVacuumLeftFront:
                    return "electrovanne avant gauche";
                case ActuatorOnOffID.OpenVacuumRightFront:
                    return "electrovanne avant droite";
                default:
                    return actuator.ToString();
            }
        }

        /// <summary>
        /// Retourne le nom usuel d'un capteur couleur
        /// </summary>
        /// <param name="sensor">Capteur couleur à nommer</param>
        /// <returns>Nom du capteur couleur</returns>
        public static String GetName(SensorColorID sensor)
        {
            switch (sensor)
            {
                case SensorColorID.BuoyRight:
                    return "bouée droite";
                case SensorColorID.BuoyLeft:
                    return "bouée gauche";
                default:
                    return sensor.ToString();
            }
        }

        /// <summary>
        /// Retourne le nom usuel d'une balise
        /// </summary>
        /// <param name="capteur">Balise à nommer</param>
        /// <returns>Nom de la balise</returns>
        public static String GetName(BaliseID balise)
        {
            switch (balise)
            {
                case BaliseID.Principale:
                    return "principale";
                default:
                    return balise.ToString();
            }
        }

        /// <summary>
        /// Retourne le nom usuel d'un Lidar
        /// </summary>
        /// <param name="capteur">Lidar à nommer</param>
        /// <returns>Nom du Lidar</returns>
        public static String GetName(LidarID lidar)
        {
            switch (lidar)
            {
                case LidarID.Ground:
                    return "scan sol";
                case LidarID.Avoid:
                    return "évitement";
                default:
                    return lidar.ToString();
            }
        }

        /// <summary>
        /// Retourne le template de décodage d'une fonction dans le protocole de communication UDP
        /// </summary>
        /// <param name="capteur">Fonction à décoder</param>
        /// <returns>Template de décodage</returns>
        public static String GetName(UdpFrameFunction frame)
        {
            return UdpFrameDecoder.GetMessage(frame);
        }
    }
}
<file_sep>/GoBot/GoBot/Util.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot
{
    public static class Util
    {
        public static dynamic ToRealType(Object o)
        {
            Type type = o.GetType();
            dynamic pp = Convert.ChangeType(o, type);
            return pp;
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/Pepperl/PepperlEnums.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace GoBot.Devices
{
    public enum PepperlCmd
    {
        [Description(PepperlConst.CmdReboot)]
        Reboot,
        [Description(PepperlConst.CmfFactoryReset)]
        FactoryReset,
        [Description(PepperlConst.CmdHandleUdp)]
        CreateChannelUDP,
        [Description(PepperlConst.CmdHandleTcp)]
        CreateChannelTCP,
        [Description(PepperlConst.CmdProtocolInfo)]
        ProtocolInfo,
        [Description(PepperlConst.CmdFeedWatchdog)]
        FeedWatchdog,
        [Description(PepperlConst.CmdListParameters)]
        ListParameters,
        [Description(PepperlConst.CmdGetParameter)]
        GetParameters,
        [Description(PepperlConst.CmdSetParameter)]
        SetParameters,
        [Description(PepperlConst.CmdResetParameter)]
        ResetParameters,
        [Description(PepperlConst.CmdReleaseHandle)]
        CloseChannel,
        [Description(PepperlConst.CmdStartScan)]
        ScanStart,
        [Description(PepperlConst.CmdStopScan)]
        ScanStop,
        [Description(PepperlConst.CmdGetScanConfig)]
        GetScanConfig,
        [Description(PepperlConst.CmdSetScanConfig)]
        SetScanConfig
    }

    public enum PepperlFreq
    {
        Hz10,
        Hz11,
        Hz13,
        Hz15,
        Hz16,
        Hz20,
        Hz23,
        Hz26,
        Hz30,
        Hz33,
        Hz35,
        Hz40,
        Hz46,
        Hz50
    }

    public enum PepperlFilter
    {
        [Description(PepperlConst.ValueFilterTypeNone)]
        None,
        [Description(PepperlConst.ValueFilterTypeAverage)]
        Average,
        [Description(PepperlConst.ValueFilterTypeMedian)]
        Median,
        [Description(PepperlConst.ValueFilterTypeMaximum)]
        Maximum,
        [Description(PepperlConst.ValueFilterTypeRemission)]
        Remission
    }

    public static class PepperEnumExtensions
    {
        public static string GetText(this PepperlCmd val)
        {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])val
               .GetType()
               .GetField(val.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Length > 0 ? attributes[0].Description : string.Empty;
        }

        public static string GetText(this PepperlFilter val)
        {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])val
               .GetType()
               .GetField(val.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Length > 0 ? attributes[0].Description : string.Empty;
        }

        public static int SamplesPerScan(this PepperlFreq freq)
        {
            switch (freq)
            {
                case PepperlFreq.Hz10: return 8400;
                case PepperlFreq.Hz11: return 7200;
                case PepperlFreq.Hz13: return 6300;
                case PepperlFreq.Hz15: return 5600;
                case PepperlFreq.Hz16: return 5040;
                case PepperlFreq.Hz20: return 4200;
                case PepperlFreq.Hz23: return 3600;
                case PepperlFreq.Hz26: return 3150;
                case PepperlFreq.Hz30: return 2800;
                case PepperlFreq.Hz33: return 2520;
                case PepperlFreq.Hz35: return 2400;
                case PepperlFreq.Hz40: return 2100;
                case PepperlFreq.Hz46: return 1800;
                case PepperlFreq.Hz50: return 1680;
                default: return 0;
            }
        }

        public static int Frequency(this PepperlFreq freq)
        {
            switch (freq)
            {
                case PepperlFreq.Hz10: return 10;
                case PepperlFreq.Hz11: return 11;
                case PepperlFreq.Hz13: return 13;
                case PepperlFreq.Hz15: return 15;
                case PepperlFreq.Hz16: return 16;
                case PepperlFreq.Hz20: return 20;
                case PepperlFreq.Hz23: return 23;
                case PepperlFreq.Hz26: return 26;
                case PepperlFreq.Hz30: return 30;
                case PepperlFreq.Hz33: return 33;
                case PepperlFreq.Hz35: return 35;
                case PepperlFreq.Hz40: return 40;
                case PepperlFreq.Hz46: return 46;
                case PepperlFreq.Hz50: return 50;
                default: return 0;
            }
        }
    }
}
<file_sep>/GoBot/Composants/RichTextBoxPlus.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace Composants
{
    public partial class RichTextBoxPlus : RichTextBox
    {
        public RichTextBoxPlus()
        {
            InitializeComponent();
        }

        public RichTextBoxPlus(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
        }

        /// <summary>
        /// Ajoute une ligne de teste dans le couleur spécifiée. L'heure peut etre auomatiquement ajoutée en début de ligne.
        /// </summary>
        /// <param name="text">Texte à ajouter</param>
        /// <param name="color">Couleur du texte à ajouter</param>
        /// <param name="withDate">Si vrain la date est ajoutée au début de la ligne</param>
        public void AddLine(String text, Color color, bool withDate = true)
        {
            text = withDate ? DateTime.Now.ToLongTimeString() + " > " + text + Environment.NewLine : text + Environment.NewLine;
            SuspendLayout();

            SelectionStart = TextLength;
            SelectedText = text;

            SelectionStart = TextLength - text.Length + 1;
            SelectionLength = text.Length;
            SelectionColor = color;

            ResumeLayout();

            Select(TextLength, 0);

            SelectionStart = TextLength;
            ScrollToCaret();
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesCrossingPoints.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes
{
    internal static class ShapesCrossingPoints
    {
        public static List<RealPoint> CircleAndCircle(Circle circle1, Circle circle2)
        {
            List<RealPoint> output = new List<RealPoint>();

            bool aligned = Math.Abs(circle2.Center.Y - circle1.Center.Y) < RealPoint.PRECISION;

            if (aligned)// Cercles non alignés horizontalement (on pivote pour les calculs, sinon division par 0)
                circle2 = circle2.Rotation(90, circle1.Center);

            RealPoint oc1 = new RealPoint(circle2.Center), oc2 = new RealPoint(circle1.Center);
            double b = circle2.Radius, c = circle1.Radius;

            double a = (-(Math.Pow(oc1.X, 2)) - (Math.Pow(oc1.Y, 2)) + Math.Pow(oc2.X, 2) + Math.Pow(oc2.Y, 2) + Math.Pow(b, 2) - Math.Pow(c, 2)) / (2 * (oc2.Y - oc1.Y));
            double d = ((oc2.X - oc1.X) / (oc2.Y - oc1.Y));

            double A = Math.Pow(d, 2) + 1;
            double B = -2 * oc1.X + 2 * oc1.Y * d - 2 * a * d;
            double C = Math.Pow(oc1.X, 2) + Math.Pow(oc1.Y, 2) - 2 * oc1.Y * a + Math.Pow(a, 2) - Math.Pow(b, 2);

            double delta = Math.Pow(B, 2) - 4 * A * C;

            if (delta >= 0)
            {
                double x1 = (-B + Math.Sqrt(delta)) / (2 * A);
                double y1 = a - x1 * d;
                output.Add(new RealPoint(x1, y1));

                if (delta > 0)
                {
                    double x2 = (-B - Math.Sqrt(delta)) / (2 * A);
                    double y2 = a - x2 * d;
                    output.Add(new RealPoint(x2, y2));
                }
            }

            if (aligned)
                output = output.ConvertAll(p => p.Rotation(-90, circle1.Center));

            return output;
        }

        public static List<RealPoint> CircleAndSegment(Circle circle, Segment segment)
        {
            List<RealPoint> intersectsPoints = new List<RealPoint>();
            double dx = segment.EndPoint.X - segment.StartPoint.X;
            double dy = segment.EndPoint.Y - segment.StartPoint.Y;
            double Ox = segment.StartPoint.X - circle.Center.X;
            double Oy = segment.StartPoint.Y - circle.Center.Y;
            double A = dx * dx + dy * dy;
            double B = 2 * (dx * Ox + dy * Oy);
            double C = Ox * Ox + Oy * Oy - circle.Radius * circle.Radius;
            double delta = B * B - 4 * A * C;

            if (delta < 0 + double.Epsilon && delta > 0 - double.Epsilon)
            {
                double t = -B / (2 * A);
                if (t >= 0 && t <= 1)
                    intersectsPoints.Add(new RealPoint(segment.StartPoint.X + t * dx, segment.StartPoint.Y + t * dy));
            }
            if (delta > 0)
            {
                double t1 = (double)((-B - Math.Sqrt(delta)) / (2 * A));
                double t2 = (double)((-B + Math.Sqrt(delta)) / (2 * A));
                if (t1 >= 0 && t1 <= 1)
                    intersectsPoints.Add(new RealPoint(segment.StartPoint.X + t1 * dx, segment.StartPoint.Y + t1 * dy));
                if (t2 >= 0 && t2 <= 1)
                    intersectsPoints.Add(new RealPoint(segment.StartPoint.X + t2 * dx, segment.StartPoint.Y + t2 * dy));
            }

            return intersectsPoints;
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/LiaisonDataCheck.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Communications
{
    class LiaisonDataCheck
    {
        private Connexion Connexion { get; set; }
        private Carte Carte { get; set; }
        private bool Bloquant { get; set; }

        public byte IDTestEmissionActuel { get; set; }
        private byte IDTestReceptionActuel { get; set; }
        public int NombreMessagesTotal { get; private set; }
        public int NombreMessagesCorrects { get; private set; }
        public int NombreMessagesPerdusEmission { get; private set; }
        public int NombreMessagesCorrompusEmission { get; private set; }
        public int NombreMessagesPerdusReception { get; private set; }
        public int NombreMessagesCorrompusReception { get; private set; }

        public Dictionary<int, List<ReponseLiaison>> Reponses { get; set; }

        public enum ReponseLiaison
        {
            OK,
            PerduEmission,
            PerduReception,
            CorrompuEmission,
            CorrompuReception,
        }

        public LiaisonDataCheck(Connexion connexion, Carte carte, bool bloquant)
        {
            Reponses = new Dictionary<int, List<ReponseLiaison>>();
            for (int i = 0; i < 255; i++ )
                Reponses.Add(i, new List<ReponseLiaison>());

            Connexion = connexion;
            Carte = carte;
            Bloquant = bloquant;

            NombreMessagesTotal = 0;
            NombreMessagesCorrects = 0;
            NombreMessagesPerdusEmission = 0;
            NombreMessagesCorrompusEmission = 0;
            NombreMessagesPerdusReception = 0;
            NombreMessagesCorrompusReception = 0;
            IDTestEmissionActuel = 255;
            IDTestReceptionActuel = 0;
        }

        public void EnvoiTest()
        {
            NombreMessagesTotal++;
            IDTestEmissionActuel++;

            Trame t = TrameFactory.TestEmission(Carte, IDTestEmissionActuel);
            Connexion.SendMessage(t);
        }

        public void MessageRecu(Trame trame)
        {
            if ((FonctionBalise)trame[1] == FonctionBalise.TestEmissionCorrompu)
            {
                Reponses[trame[2]].Add(ReponseLiaison.CorrompuEmission);

                NombreMessagesCorrompusEmission++;
                if (trame[2] != IDTestReceptionActuel)
                {
                    if (trame[2] < IDTestReceptionActuel)
                    {
                        NombreMessagesPerdusReception += 255 - IDTestReceptionActuel;
                        NombreMessagesPerdusReception += trame[2];

                        for (int i = IDTestReceptionActuel + 1; i < 256; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);

                        for (int i = 0; i < trame[2]; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);
                    }
                    else
                    {
                        NombreMessagesPerdusReception += trame[2] - IDTestReceptionActuel;

                        for (int i = IDTestReceptionActuel; i < trame[2]; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);
                    }
                }

                IDTestReceptionActuel = (byte)(trame[2] + 1);
            }
            if ((FonctionBalise)trame[1] == FonctionBalise.TestEmissionReussi)
            {
                if (trame[2] != IDTestReceptionActuel)
                {
                    if (trame[2] < IDTestReceptionActuel)
                    {
                        NombreMessagesPerdusReception += 255 - IDTestReceptionActuel;
                        NombreMessagesPerdusReception += trame[2];

                        for (int i = IDTestReceptionActuel + 1; i < 256; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);

                        for (int i = 0; i < trame[2]; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);
                    }
                    else
                    {
                        NombreMessagesPerdusReception += trame[2] - IDTestReceptionActuel;

                        for (int i = IDTestReceptionActuel; i < trame[2]; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);
                    }
                }

                bool verif = true;
                if (trame.Length == 19)
                {
                    for (int i = 0; i < 16; i++)
                        if (trame[i + 3] != i)
                            verif = false;

                    if (verif)
                    {
                        NombreMessagesCorrects++;
                        Reponses[trame[2]].Add(ReponseLiaison.OK);
                    }
                    else
                    {
                        NombreMessagesCorrompusReception++;
                        Reponses[trame[2]].Add(ReponseLiaison.CorrompuReception);
                    }
                }

                IDTestReceptionActuel = (byte)(trame[2] + 1);
            }
            if ((FonctionBalise)trame[1] == FonctionBalise.TestEmissionPerdu)
            {
                byte idAttendu = trame[2];
                byte idRecu = trame[3];

                if (idRecu > idAttendu)
                    NombreMessagesPerdusEmission += idRecu - idAttendu;
                else
                {
                    NombreMessagesPerdusEmission += 255 - idAttendu;
                    NombreMessagesPerdusEmission += idRecu;
                }

                if (trame[2] != IDTestReceptionActuel)
                {
                    if (trame[2] < IDTestReceptionActuel)
                    {
                        NombreMessagesPerdusReception += 255 - IDTestReceptionActuel;
                        NombreMessagesPerdusReception += trame[2];

                        for (int i = IDTestReceptionActuel + 1; i < 256; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);

                        for (int i = 0; i < trame[2]; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);
                    }
                    else
                    {
                        NombreMessagesPerdusReception += trame[2] - IDTestReceptionActuel;

                        for (int i = IDTestReceptionActuel; i < trame[2]; i++)
                            Reponses[i].Add(ReponseLiaison.PerduReception);
                    }
                }

                IDTestReceptionActuel = (byte)(idRecu + 1);
            }
        }

        public void CalculerResultats()
        {
            foreach(KeyValuePair<int, List<ReponseLiaison>> pair in Reponses)
            {
                if (Reponses[pair.Key].Count == 0)
                    Reponses[pair.Key].Add(LiaisonDataCheck.ReponseLiaison.PerduEmission);
            }

            for (int i = 0; i < 255; i++)
            {
                int nbOk = 0;
                foreach (LiaisonDataCheck.ReponseLiaison rep in Reponses[i])
                {
                    if (rep == LiaisonDataCheck.ReponseLiaison.OK)
                        nbOk++;
                    if (rep != LiaisonDataCheck.ReponseLiaison.OK)
                        Console.WriteLine(i + " " + rep.ToString());
                }

                if (nbOk > 1)
                    Console.WriteLine(i + " " + nbOk + " OK");
            }
        }
    }
}
<file_sep>/GoBot/GoBot/GameElements/ColorDropOff.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.BoardContext;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace GoBot.GameElements
{
    public class ColorDropOff : GameElementZone
    {
        private List<Color> _loadOnRed; // 0 = Contre la bordure ...1...2...3 = vers la table
        private List<Color> _loadOnGreen;

        private Buoy _buoyOutside1, _buoyOutside2;
        private Buoy _buoyInside1, _buoyInside2;

        private Color _pendingLeft, _pendingRight;
        private RealPoint _pendingLeftPos, _pendingRightPos;

        public ColorDropOff(RealPoint position, Color color, Buoy buoyOutside1, Buoy buoyOutside2, Buoy buoyInside1, Buoy buoyInside2) : base(position, color, 150)
        {
            _loadOnRed = Enumerable.Repeat(Color.Transparent, 5).ToList();
            _loadOnGreen = Enumerable.Repeat(Color.Transparent, 5).ToList();

            _buoyOutside1 = buoyOutside1;
            _buoyOutside2 = buoyOutside2;
            _buoyInside1 = buoyInside1;
            _buoyInside2 = buoyInside2;

            _pendingLeft = Color.Transparent;
            _pendingRight = Color.Transparent;
        }

        public int LoadsOnRed => _loadOnRed.Count(c => c != Color.Transparent);
        public int LoadsOnGreen => _loadOnGreen.Count(c => c != Color.Transparent);

        public void SetPendingLeft(Color c, RealPoint point)
        {
            _pendingLeft = c;
            _pendingLeftPos = point;
        }

        public void SetPendingRight(Color c, RealPoint point)
        {
            _pendingRight = c;
            _pendingRightPos = point;
        }

        public void RemovePending()
        {
            _pendingLeft = Color.Transparent;
            _pendingRight = Color.Transparent;
        }

        public override void Paint(Graphics g, WorldScale scale)
        {
            base.Paint(g, scale);

            double x = _position.X - 110;
            double y = 2000 - 10 - 85/2;
            double dy = -85;

            if (_loadOnGreen != null)
                for (int j = 0; j < _loadOnGreen.Count; j++)
                    if (_loadOnGreen[j] != Color.Transparent)
                        new Circle(new RealPoint(x, y + dy * j), 36).Paint(g, Color.Black, 1, _loadOnGreen[j], scale);

            x = _position.X + 110;

            if (_loadOnRed != null)
                for (int j = 0; j < _loadOnRed.Count; j++)
                    if (_loadOnRed[j] != Color.Transparent)
                        new Circle(new RealPoint(x, y + dy * j), 36).Paint(g, Color.Black, 1, _loadOnRed[j], scale);

            if (_pendingLeft != Color.Transparent)
                new Circle(_pendingLeftPos, 36).Paint(g, Color.Black, 1, _pendingLeft, scale);

            if (_pendingRight != Color.Transparent)
                new Circle(_pendingRightPos, 36).Paint(g, Color.Black, 1, _pendingRight, scale);
        }

        public void SetBuoyOnRed(Color c, int level)
        {
            if (level < _loadOnRed.Count)
                _loadOnRed[level] = c;
        }

        public void SetBuoyOnGreen(Color c, int level)
        {
            if (level < _loadOnGreen.Count)
                _loadOnGreen[level] = c;
        }

        public int GetAvailableLevel()
        {
            return Math.Max(_loadOnRed.FindIndex(c => c == Color.Transparent), _loadOnGreen.FindIndex(c => c == Color.Transparent));
        }

        public bool HasInsideBuoys => _buoyInside1.IsAvailable || _buoyInside2.IsAvailable;
        public bool HasOutsideBuoys => _buoyOutside1.IsAvailable || _buoyOutside2.IsAvailable;

        public void TakeInsideBuoys()
        {
            _buoyInside1.IsAvailable = false;
            _buoyInside2.IsAvailable = false;
        }

        public void TakeOutsideBuoys()
        {
            _buoyOutside1.IsAvailable = false;
            _buoyOutside2.IsAvailable = false;
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelDisplacement.cs
﻿using System;
using System.Windows.Forms;

using Geometry;
using Geometry.Shapes;
using GoBot.Threading;

namespace GoBot.IHM
{
    public partial class PanelDisplacement : UserControl
    {
        private ToolTip tooltip;

        public Robot Robot { get; set; }

        public PanelDisplacement()
        {
            InitializeComponent();

            tooltip = new ToolTip();
            tooltip.InitialDelay = 1500;

            tooltip.SetToolTip(btnForward, "Avancer");
            tooltip.SetToolTip(btnBackward, "Reculer");
            tooltip.SetToolTip(btnPivotRight, "Pivoter vers la droite");
            tooltip.SetToolTip(btnPivotLeft, "Pivoter vers la gauche");
            tooltip.SetToolTip(btnTurnBackwardRight, "Virage vers l'arrière droite");
            tooltip.SetToolTip(btnTurnForwardRight, "Virage vers l'avant droite");
            tooltip.SetToolTip(btnTurnBackwardLeft, "Virage vers l'arrière gauche");
            tooltip.SetToolTip(btnTurnForwardLeft, "Virage vers l'avant droite");
            tooltip.SetToolTip(btnStop, "Stop");
        }

        public virtual void Init()
        {
            // Charger la config

            if (Robot == Robots.MainRobot)
            {
                trkLineSpeed.SetValue(Config.CurrentConfig.ConfigRapide.LineSpeed, false);
                trkLineAccel.SetValue(Config.CurrentConfig.ConfigRapide.LineAcceleration, false);
                trkLineDecel.SetValue(Config.CurrentConfig.ConfigRapide.LineDeceleration, false);
                trkPivotSpeed.SetValue(Config.CurrentConfig.ConfigRapide.PivotSpeed, false);
                trkPivotAccel.SetValue(Config.CurrentConfig.ConfigRapide.PivotAcceleration, false);

                numCoeffP.Value = Config.CurrentConfig.GRCoeffP;
                numCoeffI.Value = Config.CurrentConfig.GRCoeffI;
                numCoeffD.Value = Config.CurrentConfig.GRCoeffD;
                Robot.SendPID(Config.CurrentConfig.GRCoeffP, Config.CurrentConfig.GRCoeffI, Config.CurrentConfig.GRCoeffD);
            }
        }

        #region Boutons pilotage

        protected virtual void btnForward_Click(object sender, EventArgs e)
        {
            int distance;
            if (!(Int32.TryParse(txtDistance.Text, out distance) && distance != 0))
                txtDistance.ErrorMode = true;
            else
                Robot.MoveForward(distance, false);
        }

        protected virtual void btnBackward_Click(object sender, EventArgs e)
        {
            int distance;
            if (!(Int32.TryParse(txtDistance.Text, out distance) && distance != 0))
                txtDistance.ErrorMode = true;
            else
                Robot.MoveBackward(distance, false);
        }

        protected virtual void btnPivotLeft_Click(object sender, EventArgs e)
        {
            int angle;
            if (!(Int32.TryParse(txtAngle.Text, out angle) && angle != 0))
                txtAngle.ErrorMode = true;
            else
                Robot.PivotLeft(angle, false);
        }

        protected virtual void btnPivotRight_Click(object sender, EventArgs e)
        {
            int angle;
            if (!(Int32.TryParse(txtAngle.Text, out angle) && angle != 0))
                txtAngle.ErrorMode = true;
            else
                Robot.PivotRight(angle, false);
        }

        protected virtual void btnTurnForwardRight_Click(object sender, EventArgs e)
        {
            DoTurn(SensAR.Avant, SensGD.Droite);
        }

        protected virtual void btnTurnForwardLeft_Click(object sender, EventArgs e)
        {
            DoTurn(SensAR.Avant, SensGD.Gauche);
        }

        protected virtual void btnTurnBackwardLeft_Click(object sender, EventArgs e)
        {
            DoTurn(SensAR.Arriere, SensGD.Gauche);
        }

        protected virtual void btnTurnBackwardRight_Click(object sender, EventArgs e)
        {
            DoTurn(SensAR.Arriere, SensGD.Droite);
        }

        protected void DoTurn(SensAR sensAr, SensGD sensGd)
        {
            int distance;
            int angle;

            bool ok = true;
            if (!Int32.TryParse(txtDistance.Text, out distance) || distance == 0)
            {
                txtDistance.ErrorMode = true;
                ok = false;
            }
            if (!Int32.TryParse(txtAngle.Text, out angle) || angle == 0)
            {
                txtAngle.ErrorMode = true;
                ok = false;
            }

            if (ok)
                Robot.Turn(sensAr, sensGd, distance, angle, false);
        }

        protected virtual void btnStopSmooth_Click(object sender, EventArgs e)
        {
            if (freelyToolStripMenuItem.Checked)
                Robot.Stop(StopMode.Freely);
            else if (smoothToolStripMenuItem.Checked)
                Robot.Stop(StopMode.Smooth);
            else if (abruptToolStripMenuItem.Checked)
                Robot.Stop(StopMode.Abrupt);
        }

        private void stopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            freelyToolStripMenuItem.Checked = false;
            smoothToolStripMenuItem.Checked = false;
            abruptToolStripMenuItem.Checked = false;
            ((ToolStripMenuItem)sender).Checked = true;

            btnStop.PerformClick();
        }

        #endregion

        #region Vitesse ligne

        protected virtual void trkLineSpeed_TickValueChanged(object sender, double value)
        {
            Robot.SpeedConfig.LineSpeed = (int)value;
        }

        private void trkLineSpeed_ValueChanged(object sender, double value)
        {
            numLineSpeed.Value = (int)value;
        }

        private void numLineSpeed_ValueChanged(object sender, EventArgs e)
        {
            if (numLineSpeed.Focused)
                trkLineSpeed.SetValue((double)numLineSpeed.Value);
        }

        #endregion

        #region Accélération ligne

        protected virtual void trkLineAccel_TickValueChanged(object sender, double value)
        {
            Robot.SpeedConfig.LineAcceleration = (int)value;
        }

        private void trkLineAccel_ValueChanged(object sender, double value)
        {
            numLineAccel.Value = (int)value;
        }

        private void numLineAccel_ValueChanged(object sender, EventArgs e)
        {
            if (numLineAccel.Focused)
                trkLineAccel.SetValue((double)numLineAccel.Value);
        }

        #endregion

        #region Décélération ligne

        private void trkLineDecel_TickValueChanged(object sender, double value)
        {
            Robot.SpeedConfig.LineDeceleration = (int)value;
        }

        private void trkLineDecel_ValueChanged(object sender, double value)
        {
            numLineDecel.Value = (int)value;
        }

        private void numLineDecel_ValueChanged(object sender, EventArgs e)
        {
            if (numLineDecel.Focused)
                trkLineDecel.SetValue((double)numLineDecel.Value);
        }

        #endregion

        #region Vitesse pivot

        private void trkPivotSpeed_TickValueChanged(object sender, double value)
        {
            Robot.SpeedConfig.PivotSpeed = (int)value;
        }

        private void trkPivotSpeed_ValueChanged(object sender, double value)
        {
            numPivotSpeed.Value = (int)value;
        }

        private void numPivotSpeed_ValueChanged(object sender, EventArgs e)
        {
            if (numPivotSpeed.Focused)
                trkPivotSpeed.SetValue((double)numPivotSpeed.Value);
        }

        #endregion

        #region Acceleration pivot

        private void trkPivotAccel_TickValueChanged(object sender, double value)
        {
            Robot.SpeedConfig.PivotAcceleration = (int)value;
            Robot.SpeedConfig.PivotDeceleration = (int)value;
        }

        private void trkPivotAccel_ValueChanged(object sender, double value)
        {
            numPivotAccel.Value = (int)value;
        }

        private void numPivotAccel_ValueChanged(object sender, EventArgs e)
        {
            if (numPivotAccel.Focused)
                trkPivotAccel.SetValue((double)numPivotAccel.Value);
        }

        #endregion

        #region Pilotage manuel

        protected virtual void pnlManual_KeyUp(object sender, KeyEventArgs e)
        {
            e.Handled = true;
            if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
                Robot.Stop();
        }

        protected virtual void pnlManual_KeyPressed(PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Down)
            {
                // Reculer
                Robot.MoveBackward(10000, false);
            }
            else if (e.KeyCode == Keys.Up)
            {
                // Avancer
                Robot.MoveForward(10000, false);
            }
            else if (e.KeyCode == Keys.Left)
            {
                // Pivot gauche
                Robot.PivotLeft(90, false);
            }
            else if (e.KeyCode == Keys.Right)
            {
                // Pivot droit
                Robot.PivotRight(90, false);
            }
        }

        #endregion

        private void btnCalibration_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(link =>
            {
                btnCalibration.InvokeAuto(() => btnCalibration.Enabled = false);
                Robot.MoveForward(10);
                Robot.SetSpeedSlow();
                Robot.Recalibration(SensAR.Arriere, true, false);
                Robot.SetSpeedFast();
                btnCalibration.InvokeAuto(() => btnCalibration.Enabled = true);
            }).StartThread();
        }

        private void btnCalibrationForward_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(link =>
            {
                btnCalibration.InvokeAuto(() => btnCalibration.Enabled = false);
                Robot.MoveBackward(10);
                Robot.SetSpeedSlow();
                Robot.Recalibration(SensAR.Avant, true, true);
                Robot.SetSpeedFast();
                btnCalibration.InvokeAuto(() => btnCalibration.Enabled = true);
            }).StartThread();
        }

        private void btnGoTo_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(link => ThreadGoTo(link)).StartThread();
        }

        private void ThreadGoTo(ThreadLink link)
        {
            link.RegisterName();
            Robot.GoToPosition(new Position((double)numTeta.Value, new RealPoint((double)numX.Value, (double)numY.Value)));
        }

        private void btnLow_Click(object sender, EventArgs e)
        {
            Robot.SetSpeedSlow();
            trkLineSpeed.SetValue(Robot.SpeedConfig.LineSpeed, false);
            trkLineAccel.SetValue(Robot.SpeedConfig.LineAcceleration, false);
            trkLineDecel.SetValue(Robot.SpeedConfig.LineDeceleration, false);
            trkPivotSpeed.SetValue(Robot.SpeedConfig.LineSpeed, false);
            trkPivotAccel.SetValue(Robot.SpeedConfig.LineSpeed, false);
        }

        private void btnFast_Click(object sender, EventArgs e)
        {
            Robot.SetSpeedFast();
            trkLineSpeed.SetValue(Robot.SpeedConfig.LineSpeed, false);
            trkLineAccel.SetValue(Robot.SpeedConfig.LineAcceleration, false);
            trkLineDecel.SetValue(Robot.SpeedConfig.LineDeceleration, false);
            trkPivotSpeed.SetValue(Robot.SpeedConfig.PivotSpeed, false);
            trkPivotAccel.SetValue(Robot.SpeedConfig.PivotAcceleration, false);
        }

        private void numCoeffPID_ValueChanged(object sender, EventArgs e)
        {
            btnPIDXY.Enabled = true;
        }

        private void btnPIDPol_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.SendPIDCap((int)numCoeffP.Value, (int)numCoeffI.Value, (int)numCoeffD.Value);
        }

        private void btnPIDVit_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.SendPIDSpeed((int)numCoeffP.Value, (int)numCoeffI.Value, (int)numCoeffD.Value);
        }

        private void btnPIDXY_Click(object sender, EventArgs e)
        {
            Robot.SendPID((int)numCoeffP.Value, (int)numCoeffI.Value, (int)numCoeffD.Value);

            if (Robot == Robots.MainRobot)
            {
                Config.CurrentConfig.GRCoeffP = (int)numCoeffP.Value;
                Config.CurrentConfig.GRCoeffI = (int)numCoeffI.Value;
                Config.CurrentConfig.GRCoeffD = (int)numCoeffD.Value;
                Config.Save();
            }

            btnPIDXY.Enabled = false;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/RealPointWithCircle.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class RealPointWithCircle
    {
        public static bool Contains(RealPoint containingPoint, Circle containedCircle)
        {
            return containedCircle.Center == containingPoint && containedCircle.Radius == 0;
        }

        public static bool Cross(RealPoint point, Circle circle)
        {
            return CircleWithRealPoint.Cross(circle, point);
        }

        public static double Distance(RealPoint point, Circle circle)
        {
            return CircleWithRealPoint.Distance(circle, point);
        }

        public static List<RealPoint> GetCrossingPoints(RealPoint point, Circle circle)
        {
            return CircleWithRealPoint.GetCrossingPoints(circle, point);
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Forms/SplashScreen.cs
﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace GoBot.IHM
{
    /// <summary>
    /// Fenêtre de lancement de l'application avec image et messages personnalisés.
    /// </summary>
    public static class SplashScreen
    {
        private static SplashForm _form;
        private static Thread _formThread;

        private static Bitmap _image;
        private static Rectangle _messageRect;

        /// <summary>
        /// Affiche l'écran de lancement.
        /// </summary>
        /// <param name="image">Image de fond de l'écran de lancement.</param>
        /// <param name="messageRect">Coordonnée du rectangle d'affichage de messages.</param>
        /// <param name="speed">L'image apparait par fondu en gagnant une opacité équivalente au paramètre speed à chaque itération.</param>
        public static void ShowSplash(Bitmap image, Rectangle messageRect, int speed = 7)
        {
            _image = image;
            _messageRect = messageRect;

            _formThread = new Thread(Open);
            _formThread.SetApartmentState(ApartmentState.STA);
            _formThread.Start(speed);

            while (_form == null) ;
        }

        /// <summary>
        /// Ferme l'écran de lancement.
        /// </summary>
        /// <param name="speed">L'image disparait par fondu en perdant une opacité équivalente au paramètre speed à chaque itération.</param>
        public static void CloseSplash(int speed = 10)
        {
            if (speed == -1)
                _form.InvokeAuto(() => _form.Close());
            else
            {
                _form.Speed = -speed;
                _form.StartTimer();
            }
        }

        public static void SetImage(Image img)
        {
            _form.SetImage(img);
            Thread.Sleep(500);
        }

        /// <summary>
        /// Affiche le message donné dans la couleur choisie.
        /// </summary>
        /// <param name="text">Message à afficher.</param>
        /// <param name="color">Couleur du message à afficher.</param>
        public static void SetMessage(String text, Color color)
        {
            _form.SetText(text, color);
        }

        private static void Open(object speed)
        {
            _form = new SplashForm(_image);
            _form.Speed = (int)speed;
            _form.MessageRect = _messageRect;
            _form.ShowDialog();
        }

        private class SplashForm : Form
        {
            private int _speed;
            private int _oppacity;

            private Image _currentBitmap;
            private Image _originalBitmap;

            private string _text;
            private Color _textColor;

            private Rectangle _messageRect;

            private System.Timers.Timer _timerOpacity;

            public int Speed
            {
                get
                {
                    return _speed;
                }
                set
                {
                    _speed = value;
                }
            }

            public Rectangle MessageRect
            {
                get
                {
                    return _messageRect;
                }
                set
                {
                    _messageRect = value;
                }
            }

            public SplashForm(Bitmap img)
            {
                InitializeComponent(img.Size);

                //img = WriteVersion(img);

                _oppacity = 0;
                _originalBitmap = img;

                _text = "";
                _textColor = Color.Black;

                _currentBitmap = new Bitmap(_originalBitmap);

                _timerOpacity = new System.Timers.Timer(15);
                _timerOpacity.Elapsed += _timerOpacity_Elapsed;
                _timerOpacity.Interval = 15;

                this.StartTimer();
            }

            public void SetImage(Image img)
            {
                _originalBitmap = new Bitmap(img);

                Image lastImage;

                lock (_form)
                {
                    _currentBitmap.Dispose();
                    _currentBitmap = GenerateImage();
                    lastImage = _currentBitmap;
                }

                this.ShowBitmap(lastImage, (byte)_oppacity);
            }

            public void SetText(String text, Color color)
            {
                _text = text;
                _textColor = color;

                Image lastImage;

                lock (_form)
                {
                    _currentBitmap.Dispose();
                    _currentBitmap = GenerateImage();
                    lastImage = _currentBitmap;
                }

                this.ShowBitmap(lastImage, (byte)_oppacity);
            }

            public void StartTimer()
            {
                _timerOpacity.Start();
            }

            private Image GenerateImage()
            {
                Bitmap newBitmap = new Bitmap(_originalBitmap);
                Graphics g = Graphics.FromImage(newBitmap);
                StringFormat fmt = new StringFormat();
                fmt.LineAlignment = StringAlignment.Center;
                fmt.Alignment = StringAlignment.Center;

                g.DrawString(_text, new Font("Ink free", 22, FontStyle.Bold), new SolidBrush(_textColor), _messageRect, fmt);
                g.Dispose();

                return newBitmap;
            }

            private void InitializeComponent(Size sz)
            {
                this.SuspendLayout();
                this.Size = sz;
                this.Cursor = Cursors.AppStarting;
                this.FormBorderStyle = FormBorderStyle.None;
                this.ShowInTaskbar = false;
                this.StartPosition = FormStartPosition.CenterScreen;
                this.ResumeLayout(false);
            }

            private void _timerOpacity_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
            {
                Image lastImg;

                _oppacity += _speed;

                if (_oppacity < 0)
                {
                    _timerOpacity.Stop();
                    _oppacity = 0;
                    this.InvokeAuto(() => this.Close());
                }
                else if (_oppacity > 255)
                {
                    _timerOpacity.Stop();
                    _oppacity = 255;

                    lock (_form)
                        lastImg = _currentBitmap;

                    this.ShowBitmap(lastImg, (byte)(_oppacity));
                }
                else
                {
                    lock (_form)
                        lastImg = _currentBitmap;

                    this.ShowBitmap(lastImg, (byte)(_oppacity));
                }
            }

            private Bitmap WriteVersion(Bitmap img)
            {
                Graphics g = Graphics.FromImage(img);
                g.DrawString(Application.ProductVersion.Substring(0, Application.ProductVersion.LastIndexOf('.')), new Font("Calibri", 16, FontStyle.Bold), new SolidBrush(Color.FromArgb(67, 78, 84)), new PointF(82, 212));

                return img;
            }

            private void ShowBitmap(Image bitmap, byte opacity)
            {
                Bitmap copyBmp;

                // The idea of this is very simple,
                // 1. Create a compatible DC with screen;
                // 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
                // 3. Call the UpdateLayeredWindow.

                try
                {

                    lock (_form)
                    {
                        // On copie l'image parce qu'avec l'invocation on sait pas trop quand ça va être executé et l'image aura peut être été détruite.
                        copyBmp = new Bitmap(bitmap);
                    }

                    IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
                    IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
                    IntPtr hBitmap = IntPtr.Zero;
                    IntPtr oldBitmap = IntPtr.Zero;

                    try
                    {
                        hBitmap = copyBmp.GetHbitmap(Color.FromArgb(0));  // grab a GDI handle from this GDI+ bitmap
                        oldBitmap = Win32.SelectObject(memDc, hBitmap);

                        Win32.Size size = new Win32.Size(copyBmp.Width, copyBmp.Height);
                        Win32.Point pointSource = new Win32.Point(0, 0);
                        Win32.Point topPos = new Win32.Point(Left, Top);
                        Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
                        blend.BlendOp = Win32.AC_SRC_OVER;
                        blend.BlendFlags = 0;
                        blend.SourceConstantAlpha = opacity;
                        blend.AlphaFormat = Win32.AC_SRC_ALPHA;

                        this.InvokeAuto(() => Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA));
                    }
                    finally
                    {
                        Win32.ReleaseDC(IntPtr.Zero, screenDc);
                        if (hBitmap != IntPtr.Zero)
                        {
                            Win32.SelectObject(memDc, oldBitmap);
                            Win32.DeleteObject(hBitmap);
                        }
                        Win32.DeleteDC(memDc);

                        copyBmp.Dispose();
                    }
                }
                catch { }
            }

            protected override CreateParams CreateParams
            {
                get
                {
                    CreateParams cp = base.CreateParams;
                    cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style
                    cp.ExStyle |= 0x00000008; // WS_EX_TOPMOST
                    return cp;
                }
            }

            private class Win32
            {
                public enum Bool
                {
                    False = 0,
                    True
                };

                [StructLayout(LayoutKind.Sequential)]
                public struct Point
                {
                    public Int32 x;
                    public Int32 y;

                    public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
                }

                [StructLayout(LayoutKind.Sequential)]
                public struct Size
                {
                    public Int32 cx;
                    public Int32 cy;

                    public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }
                }

                [StructLayout(LayoutKind.Sequential, Pack = 1)]
                struct ARGB
                {
                    public byte Blue;
                    public byte Green;
                    public byte Red;
                    public byte Alpha;
                }

                [StructLayout(LayoutKind.Sequential, Pack = 1)]
                public struct BLENDFUNCTION
                {
                    public byte BlendOp;
                    public byte BlendFlags;
                    public byte SourceConstantAlpha;
                    public byte AlphaFormat;
                }

                public const Int32 ULW_COLORKEY = 0x00000001;
                public const Int32 ULW_ALPHA = 0x00000002;
                public const Int32 ULW_OPAQUE = 0x00000004;

                public const byte AC_SRC_OVER = 0x00;
                public const byte AC_SRC_ALPHA = 0x01;

                [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
                public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);

                [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
                public static extern IntPtr GetDC(IntPtr hWnd);

                [DllImport("user32.dll", ExactSpelling = true)]
                public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

                [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
                public static extern IntPtr CreateCompatibleDC(IntPtr hDC);

                [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
                public static extern Bool DeleteDC(IntPtr hdc);

                [DllImport("gdi32.dll", ExactSpelling = true)]
                public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

                [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
                public static extern Bool DeleteObject(IntPtr hObject);
            }
        }
    }
}
<file_sep>/GoBot/Extensions/StringExtensions.cs
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Extensions
{
    internal static class StringExtensions
    {
        public static String FirstCamelWord(this String txt)
        {
            string word = string.Empty;

            if (!string.IsNullOrEmpty(txt))
            {
                foreach (char ch in txt)
                {
                    if (char.IsLower(ch))
                        word += ch.ToString();
                    else
                        break;

                }
            }

            return word;
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageStorage.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PageStorage : UserControl
    {
        public PageStorage()
        {
            InitializeComponent();
        }
    }
}
<file_sep>/GoBot/GoBot/PathFinding/Graph.cs
using System;
using System.Collections;

using System.Collections.Generic;
using Geometry.Shapes;
using System.Linq;

namespace AStarFolder
{
    public class Graph
    {
        private double _resolution, _distanceMax;

        private List<Arc> _arcs, _tmpArcs;
        private List<Node> _nodes, _tmpNodes;

        public Graph(double resolution, double distanceMaxRaccordable)
        {
            _resolution = resolution;
            _distanceMax = distanceMaxRaccordable;

            _tmpArcs = new List<Arc>();
            _tmpNodes = new List<Node>();
            _arcs = new List<Arc>();
            _nodes = new List<Node>();
        }

        double Resolution { get { return _resolution; } }
        double DistanceMaxRaccordable { get { return _distanceMax; } }
        public IList<Node> Nodes { get { return _nodes; } }
        public IList<Arc> Arcs { get { return _arcs; } }

        public void Clear()
        {
            _nodes.Clear();
            _arcs.Clear();
        }

        /// <summary>
        /// Ajoute un noeud au graph en reliant tous les points à une distance maximale et en prenant en compte les obstacles à éviter
        /// Si permanent == false, le point sera supprimé au prochain appel de @CleanNodesArcsAdd
        /// </summary>
        /// <param name="node">Noeud à ajouter</param>
        /// <param name="obstacles">Obstacles à éviter</param>
        /// <param name="safeDistance">Distance (mm) de sécurité auour des obstacles</param>
        /// <param name="permnant">True si le point est ajouté de façon permanente et donc ne sera pas supprimé au prochain appel de @CleanNodesArcsAdd</param>
        /// <returns>Nombre de points reliés au point ajouté</returns>
        public int AddNode(Node node, IEnumerable<IShape> obstacles, double safeDistance, bool isPermanent = false)
        {
            // Si un noeud est deja présent à cet endroit on ne l'ajoute pas
            if (_nodes.Contains(node))
                return 0;

            // Teste si le noeud est franchissable avec la liste des obstacles
            if (obstacles.Any(o => o.Distance(node.Position) < safeDistance))
                return 0;

            _nodes.Add(node);
            if (!isPermanent) _tmpNodes.Add(node);

            int connections = 0;

            // Liaisons avec les autres noeuds du graph
            foreach (Node no in _nodes.Where(iNode => iNode != node
                                    && node.Position.Distance(iNode.Position) < _distanceMax
                                    && !obstacles.Any(iObstacle => iObstacle.Distance(new Segment(iNode.Position, node.Position)) < safeDistance)))
            {
                AddLiaison(node, no, isPermanent);
                connections++;
            }

            return connections;
        }

        /// <summary>
        /// Retourne vrai si le noeud peut se lier avec au moins un autre noeud du graph
        /// </summary>
        /// <param name="node"></param>
        /// <param name="obstacles"></param>
        /// <param name="distanceSecurite"></param>
        /// <param name="distanceMax"></param>
        /// <returns></returns>
        public bool Raccordable(Node node, IEnumerable<IShape> obstacles, double distanceSecurite)
        {
            return _nodes.Any(iNode => iNode != node
                                && node.Position.Distance(iNode.Position) < _distanceMax
                                && !obstacles.Any(iObstacle => iObstacle.Distance(new Segment(iNode.Position, node.Position)) < distanceSecurite));
        }

        /// <summary>
        /// Nettoie les arcs et noeuds ajoutés temporairement
        /// </summary>
        public void CleanNodesArcsAdd()
        {
            foreach (Arc a in _tmpArcs)
                RemoveArc(a);
            _tmpArcs.Clear();

            foreach (Node n in _tmpNodes)
                RemoveNode(n);
            _tmpNodes.Clear();
        }

        public Arc AddArc(Node startNode, Node endNode, bool isPermanent)
        {
            Arc arc = new Arc(startNode, endNode);

            _arcs.Add(arc);
            if (!isPermanent) _tmpArcs.Add(arc);

            return arc;
        }

        public void AddLiaison(Node node1, Node node2, bool isPermanent)
        {
            AddArc(node1, node2, isPermanent);
            AddArc(node2, node1, isPermanent);
        }

        public void RemoveNode(Node node)
        {
            RemoveNodeAt(_nodes.IndexOf(node));
        }

        public void RemoveNodeAt(int index)
        {
            Node node = _nodes[index];

            foreach (Arc intput in node.IncomingArcs)
            {
                intput.StartNode.OutgoingArcs.Remove(intput);
                _arcs.Remove(intput);
            }
            foreach (Arc output in node.OutgoingArcs)
            {
                output.EndNode.IncomingArcs.Remove(output);
                _arcs.Remove(output);
            }

            _nodes.RemoveAt(index);
        }

        public void RemoveArc(Arc arc)
        {
            RemoveArcAt(_arcs.IndexOf(arc));
        }

        public void RemoveArcAt(int index)
        {
            Arc ArcToRemove = (Arc)_arcs[index];

                _arcs.RemoveAt(index);
                ArcToRemove.StartNode.OutgoingArcs.Remove(ArcToRemove);
                ArcToRemove.EndNode.IncomingArcs.Remove(ArcToRemove);
        }

        public Node ClosestNode(RealPoint p, out double distance)
        {
            Node closest = null;
            distance = double.MaxValue;

            foreach (Node node in _nodes.Where(o => o.Passable))
            {
                double currentDistance = node.Position.Distance(p);

                if (distance > currentDistance)
                {
                    distance = currentDistance;
                    closest = node;
                }
            }

            return closest;
        }
    }
}
<file_sep>/GoBot/GoBot/Strategies/StrategyTest.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using GoBot.Movements;

namespace GoBot.Strategies
{
    class StrategyTest : Strategy
    {
        public override bool AvoidElements => false;

        protected override void SequenceBegin()
        {
            List<Movement> mouvements = new List<Movement>();

            // Charger ICI les mouvements à tester

        }

        protected override void SequenceCore()
        {
            foreach (Movement move in Movements)
            {
                for (int i = 0; i < move.Positions.Count; i++)
                {
                    Robots.MainRobot.SetAsservOffset(move.Positions[i]);
                    Thread.Sleep(500);
                    move.Execute();
                }
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Elements/WorldPanel.cs
﻿using Geometry;
using Geometry.Shapes;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class WorldPanel : PictureBox
    {
        private RealPoint _pointClicked, _centerAtStart;
        private WorldScale _scaleAtStart;

        public WorldDimensions Dimensions { get; protected set; }

        public delegate void WorldChangeDelegate();
        public event WorldChangeDelegate WorldChange;

        public delegate void StartMoveDelegate();
        public event StartMoveDelegate StartMove;

        public delegate void StopMoveDelegate();
        public event StopMoveDelegate StopMove;

        public WorldPanel()
        {
            InitializeComponent();
            Dimensions = new WorldDimensions();
            Dimensions.WorldChange += Dimensions_WorldChange;
            _pointClicked = null;
        }

        public bool Moving
        {
            get
            {
                return _pointClicked != null;
            }
        }

        public RealPoint ClickedPoint
        {
            get
            {
                return _pointClicked;
            }
        }

        protected void OnWorldChange()
        {
            WorldChange?.Invoke();
        }

        protected void OnStartMove()
        {
            StartMove?.Invoke();
        }

        protected void OnStopMove()
        {
            StopMove?.Invoke();
        }

        public WorldPanel(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
            Dimensions = new WorldDimensions();
            Dimensions.WorldChange += Dimensions_WorldChange;
            _pointClicked = null;
        }

        private void Dimensions_WorldChange()
        {
            OnWorldChange();
        }

        private void WorldPanel_MouseDown(object sender, MouseEventArgs e)
        {
            _pointClicked = Dimensions.WorldScale.ScreenToRealPosition(e.Location);
            _centerAtStart = Dimensions.WorldRect.Center();
            _scaleAtStart = new WorldScale(Dimensions.WorldScale);

            OnStartMove();
        }

        private void WorldPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (_pointClicked != null)
            {
                RealPoint newMousePosition = _scaleAtStart.ScreenToRealPosition(e.Location);

                RealPoint delta = _pointClicked - newMousePosition;

                Dimensions.SetWorldCenter(_centerAtStart + delta);
            }
        }

        private void WorldPanel_MouseUp(object sender, MouseEventArgs e)
        {
            _pointClicked = null;

            OnStopMove();
        }

        private void WorldPanel_SizeChanged(object sender, System.EventArgs e)
        {
            Dimensions.SetScreenSize(this.Size);
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/PagesPanda/PagePandaModel.cs
﻿using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PagePandaModel : UserControl
    {
        public PagePandaModel()
        {
            InitializeComponent();
        }
    }
}
<file_sep>/GoBot/Composants/BinaryGraph.cs
﻿using System.Drawing;
using System.Windows.Forms;

namespace Composants
{
    public partial class BinaryGraph : UserControl
    {
        public BinaryGraph()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Ajoute un point au graph
        /// </summary>
        /// <param name="value">Valeur binaire à ajouter</param>
        public void AddPoint(bool value)
        {
            if (value)
                led.Color = Color.LimeGreen;
            else
                led.Color = Color.Red;

            graph.AddPoint("Bin", value ? 1 : 0, Color.DodgerBlue);
            graph.DrawCurves();
        }
    }
}
<file_sep>/GoBot/Geometry/ListRealPoints.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Geometry.Shapes
{
    public static class ListRealPointsExtensions
    {
        /// <summary>
        /// Retourne le barycentre des points
        /// </summary>
        /// <param name="pts">Liste des points</param>
        /// <returns>Barycentre des points</returns>
        public static RealPoint GetBarycenter(this IEnumerable<RealPoint> pts)
        {
            return new RealPoint(pts.Average(p => p.X), pts.Average(p => p.Y));
        }

        /// <summary>
        /// Retourne une droite approximant la liste de points par la méthode des moindres carrés
        /// </summary>
        /// <param name="pts">Liste de points à approximer</param>
        /// <returns>Droite estimée</returns>
        public static Line FitLine(this IEnumerable<RealPoint> pts)
        {
            return new Line(pts);
        }

        /// <summary>
        /// Retourne un segment approximant la liste de points par la méthode des moindres carrés
        /// </summary>
        /// <param name="pts">Liste de points à approximer</param>
        /// <returns>Segment estimée</returns>
        public static Segment FitSegment(this IEnumerable<RealPoint> pts)
        {
            Segment res = null;

            if (pts.Count() >= 2)
            {
                Line line = new Line(pts);

                List<RealPoint> projections = pts.Select(o => line.GetProjection(o)).ToList();

                res = new Segment(projections[0], projections[1]);

                for (int i = 2; i < projections.Count; i++)
                {
                    res = res.Join(projections[i]);
                }
            }

            return res;
        }

        /// <summary>
        /// Retourne le plus petit cercle contenant tous les points et dont le centre est le barycentre des points
        /// </summary>
        /// <param name="pts">Liste des points à contenir</param>
        /// <returns>Cercle obtenu</returns>
        public static Circle GetContainingCircle(this IEnumerable<RealPoint> pts)
        {
            RealPoint center = pts.GetBarycenter();
            double ray = pts.Max(p => p.Distance(center));

            return new Circle(center, ray);
        }

        /// <summary>
        /// Retourne la distance entre les deux points les plus éloignés de la liste
        /// </summary>
        /// <param name="pts">Liste de points</param>
        /// <returns>Distance maximale</returns>
        public static double MaxDistance(this IEnumerable<RealPoint> pts)
        {
            return pts.Max(p1 => pts.Max(p2 => p1.Distance(p2)));
        }

        /// <summary>
        /// Retourne la distance entre les deux points les plus proches de la liste
        /// </summary>
        /// <param name="pts">Liste de points</param>
        /// <returns>Distance minimale</returns>
        public static double MinDistance(this IEnumerable<RealPoint> pts)
        {
            return pts.Min(p1 => pts.Min(p2 => p1.Distance(p2)));
        }

        /// <summary>
        /// Regroupe les points en différents groupes suivant leur proximité et une distance maximale de regroupement
        /// </summary>
        /// <param name="pts">Liste de points à regrouper</param>
        /// <param name="maxDistance">Distance maximale pour accrocher un point à un groupe. Représente donc également la distance minimale entre deux groupes.</param>
        /// <returns>Liste des listes de points pour chaque regroupement</returns>
        public static List<List<RealPoint>> GroupByDistance(this IEnumerable<RealPoint> pts, double maxDistance)
        {
            List<RealPoint> pool = new List<RealPoint>(pts);

            List<List<RealPoint>> fullGroups = new List<List<RealPoint>>();
            List<List<RealPoint>> groups = new List<List<RealPoint>>();
            
            while (pool.Count > 0)
            {
                List<RealPoint> group = new List<RealPoint>();
                groups.Add(group);
                group.Add(pool[0]);
                pool.RemoveAt(0);

                for (int i = 0; i < group.Count; i++)
                {
                    group.AddRange(pool.Where(p => p.Distance(group[i]) < maxDistance).ToList());
                    pool.RemoveAll(p => p.Distance(group[i]) < maxDistance);
                }
            }

            return groups;
        }

        /// <summary>
        /// Décalle tous les points dans une certaine direction
        /// </summary>
        /// <param name="pts">Points à décaller</param>
        /// <param name="deltaX">Delta sur l'axe des abscisses</param>
        /// <param name="deltaY">Delta sur l'axe des ordonnées</param>
        public static void Shift(this List<RealPoint> pts, double deltaX, double deltaY)
        {
            pts.ForEach(p => p.Set(p.X + deltaX, p.Y + deltaY));
        }

        /// <summary>
        /// Retourne les points qui sont proches d'une forme
        /// </summary>
        /// <param name="pts">Liste de points</param>
        /// <param name="shape">Forme dont les points doivent être proche</param>
        /// <param name="maxDistance">Distance maximale à la forme pour être sélectionné</param>
        /// <returns></returns>
        public static IEnumerable<RealPoint> GetPointsNearFrom(this IEnumerable<RealPoint> pts, IShape shape, double maxDistance)
        {
            return pts.Where(p => p.Distance(shape) <= maxDistance);
        }

        /// <summary>
        /// Retourne les points contenus dans une forme
        /// </summary>
        /// <param name="pts">Liste de points</param>
        /// <param name="shape">Forme qui doit contenir les points</param>
        /// <returns>Points contenus par la forme</returns>
        public static IEnumerable<RealPoint> GetPointsInside(this List<RealPoint> pts, IShape shape)
        {
            return pts.Where(p => shape.Contains(p));
        }

        /// <summary>
        /// Retourne le cercle correspondant le mieux aux points données.
        /// </summary>
        /// <param name="pts">Points dont on cherche un cercle approchant.</param>
        /// <returns>Cercle calculé</returns>
        public static Circle FitCircle(this List<RealPoint> pts)
        {
            double[,] m1 = new double[pts.Count, 3];
            double[] m2 = new double[pts.Count];
            
            for (int n = 0; n < pts.Count; n++)
            {
                m1[n, 0] = -2f * pts[n].X;
                m1[n, 1] = -2f * pts[n].Y;
                m1[n, 2] = 1;

                m2[n] = -(Math.Pow(pts[n].X, 2) + Math.Pow(pts[n].Y, 2));
            }

            double[,] m3 = Matrix.Transpose(m1);
            double[,] m4 = Matrix.Inverse3x3(Matrix.Multiply(m3, m1));
            double[] m5 = Matrix.Multiply(m3, m2);
            double[] m6 = Matrix.Multiply(m4, m5);

            RealPoint center = new RealPoint(m6[0], m6[1]);
            double radius = Math.Sqrt(Math.Pow(center.X, 2) + Math.Pow(center.Y, 2) - m6[2]);

            return new Circle(center, radius);
        }

        /// <summary>
        /// Retourne un score de correspondance des points à un cercle. (Meilleur score = Meilleure correspondance avec le cercle)
        /// </summary>
        /// <param name="pts">Points</param>
        /// <param name="circle">Cercle avec lequel calculer la correspondance</param>
        /// <returns>Score de correspondance</returns>
        public static double FitCircleScore(this List<RealPoint> pts, Circle circle)
        {
            return 1 - pts.Average(o => Math.Abs(o.Distance(circle.Center) - circle.Radius)) / circle.Radius * pts.Select(o => Math.Atan2(circle.Center.Y - o.Y, circle.Center.X - o.X)).ToList().StandardDeviation();
        }

        /// <summary>
        /// Calcule le facteur de corrélation de la régression linaire des points en une droite.
        /// </summary>
        /// <param name="pts">Points</param>
        /// <returns>Facteur de corrélation (-1 à 1)</returns>
        public static double FitLineCorrelation(this List<RealPoint> pts)
        {
            double varX = pts.Average(o => o.X * o.X) - Math.Pow(pts.Average(o => o.X), 2);
            double varY = pts.Average(o => o.Y * o.Y) - Math.Pow(pts.Average(o => o.Y), 2);
            double covar = pts.Average(o => o.X * o.Y) - pts.Average(o => o.X) * pts.Average(o => o.Y);

            return covar / (Math.Sqrt(varX) * Math.Sqrt(varY));
        }
    }
}<file_sep>/GoBot/GoBot/Movements/MovementBuoy.cs
﻿using Geometry;
using GoBot.Actionneurs;
using GoBot.GameElements;
using GoBot.Threading;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace GoBot.Movements
{
    class MovementBuoy : Movement
    {
        private Buoy _buoy;
        private Elevator _elevator;

        public MovementBuoy(Buoy buoy)
        {
            _buoy = buoy;
            _elevator = Actionneur.FindElevator(_buoy.Color);

            double delta = _elevator == Actionneur.ElevatorLeft ? 30 : -30;
            //for (int i = 0; i < 360; i += 60)
            //{
            //    Positions.Add(new Geometry.Position(i + delta + 180, new Geometry.Shapes.RealPoint(_buoy.Position.X + 200* new AnglePosition(delta + i).Cos, _buoy.Position.Y + 200 * new AnglePosition(delta + i).Sin)));
            //}

            for (int i = 0; i < 360; i += 60)
            {
                Positions.Add(new Geometry.Position(i + 180 + delta, new Geometry.Shapes.RealPoint(_buoy.Position.X + 250 * new AnglePosition(i).Cos, _buoy.Position.Y + 250 * new AnglePosition(i).Sin)));

            }

            //AnglePosition a = 0;
            //Positions.Add(new Geometry.Position(a + 180 + delta, new Geometry.Shapes.RealPoint(_buoy.Position.X + 250 * new AnglePosition(a).Cos, _buoy.Position.Y + 250 * new AnglePosition(a).Sin)));
            //a = 180;
            //Positions.Add(new Geometry.Position(a + 180 + delta, new Geometry.Shapes.RealPoint(_buoy.Position.X + 250 * new AnglePosition(a).Cos, _buoy.Position.Y + 250 * new AnglePosition(a).Sin)));

        }

        public override bool CanExecute => _buoy.IsAvailable && _elevator.CountTotal < Elevator.MaxLoad - 1;

        public override int Score => 0;

        public override double Value => 0.5;

        public override GameElement Element => _buoy;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => Color.White;

        protected override void MovementBegin()
        {
        }

        protected override bool MovementCore()
        {
            _elevator.DoGrabOpen();
            Robot.SetSpeedSlow();
            Robot.MoveForward(150);
            Robot.SetSpeedFast();
            _elevator.DoSequencePickupColorThread(_buoy.Color);
            _buoy.IsAvailable = false;

            return true;
        }

        protected override void MovementEnd()
        {
        }
    }
}
<file_sep>/GoBot/Geometry/ListDouble.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Geometry
{
    public static class ListDoubleExtensions
    {

        /// <summary>
        /// Retourne l'écart-type d'une liste de valeurs décimales
        /// </summary>
        /// <param name="values">Valeurs décimales</param>
        /// <returns>Ecart-type</returns>
        public static double StandardDeviation(this IEnumerable<double> values)
        {
            if (values.Count() > 0)
            {
                double avg = values.Average();
                double diffs = 0;

                foreach (double val in values)
                    diffs += (val - avg) * (val - avg);

                diffs /= values.Count();
                diffs = Math.Sqrt(diffs);

                return diffs;
            }
            else
                return 0;
        }
    }
}
<file_sep>/GoBot/GoBot/FenGoBot.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace IhmRobot
{
    public partial class FenGoBot : Form
    {
        public FenGoBot()
        {
            InitializeComponent();
        }

        private void groupBox1_Enter(object sender, EventArgs e)
        {

        }

        private void Depl_Enter(object sender, EventArgs e)
        {

        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnClose2_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnClose3_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        #region Texte par défaut TextBox
        private void txtDistanceGR_Enter(object sender, EventArgs e)
        {
            if (txtDistanceGR.Text == "Distance")
            {
                txtDistanceGR.ForeColor = Color.Black;
                txtDistanceGR.Text = "";
            }
        }

        private void txtDistanceGR_Leave(object sender, EventArgs e)
        {
            if (txtDistanceGR.Text == "")
            {
                txtDistanceGR.ForeColor = Color.LightGray;
                txtDistanceGR.Text = "Distance";
            }
        }

        private void txtAngleGR_Enter(object sender, EventArgs e)
        {
            if (txtAngleGR.Text == "Angle")
            {
                txtAngleGR.ForeColor = Color.Black;
                txtAngleGR.Text = "";
            }
        }

        private void txtAngleGR_Leave(object sender, EventArgs e)
        {
            if (txtAngleGR.Text == "")
            {
                txtAngleGR.ForeColor = Color.LightGray;
                txtAngleGR.Text = "Angle";
            }
        }
        #endregion

        private void btnAvanceGR_Click(object sender, EventArgs e)
        {
            int resultat;
            if (Int32.TryParse(txtDistanceGR.Text, out resultat) && resultat != 0)
                // Robot.avance(resultat);
                resultat = resultat;
            else
                txtDistanceGR.BackColor = Color.Red;
        }
    }
}
<file_sep>/GoBot/GoBot/Strategies/StrategyMatch.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using GoBot.Movements;
using GoBot.BoardContext;
using Geometry;
using Geometry.Shapes;
using GoBot.Actionneurs;
using GoBot.GameElements;
using GoBot.Threading;

namespace GoBot.Strategies
{
    class StrategyMatch : Strategy
    {
        private List<Movement> fixedMovements;

        public override bool AvoidElements => true;

        protected override void SequenceBegin()
        {
            // TODOEACHYEAR Actions fixes au lancement du match

            Robot robot = Robots.MainRobot;

            fixedMovements = new List<Movement>();

            // Ajouter les points fixes au score (non forfait, elements posés etc)
            int initScore = 0;
            initScore += 2; // Phare posé
            initScore += 15; // 2 manches à air
            GameBoard.Score = initScore;

            // Sortir ICI de la zonde de départ
            robot.UpdateGraph(GameBoard.ObstaclesAll);

            // codé en bleu avec miroir
            Elevator left = GameBoard.MyColor == GameBoard.ColorLeftBlue ? (Elevator)Actionneur.ElevatorLeft : Actionneur.ElevatorRight;
            Elevator right = GameBoard.MyColor == GameBoard.ColorLeftBlue ? (Elevator)Actionneur.ElevatorRight : Actionneur.ElevatorLeft;

            ThreadManager.CreateThread(link =>
            {
                left.DoGrabOpen();
                Thread.Sleep(750);
                left.DoGrabClose();
            }).StartThread();

            robot.MoveForward((int)(850 - 120 - Robots.MainRobot.LenghtBack + 50)); // Pour s'aligner sur le centre de l'écueil

            left.DoGrabOpen();

            robot.MoveBackward(50);

            right.DoGrabOpen();

            if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
                GameBoard.Elements.FindBuoy(new RealPoint(450, 510)).IsAvailable = false;
            else
                GameBoard.Elements.FindBuoy(new RealPoint(3000 - 450, 510)).IsAvailable = false;

            robot.GoToAngle(-90);
            robot.SetSpeedSlow();
            robot.MoveForward(50);

            ThreadLink grabLink = ThreadManager.CreateThread(l => right.DoSequencePickupColor(GameBoard.MyColor == GameBoard.ColorLeftBlue ? Buoy.Green : Buoy.Red));
            grabLink.StartThread();
            Thread.Sleep(1000);

            Actionneur.ElevatorLeft.DoGrabOpen();
            Actionneur.ElevatorRight.DoGrabOpen();
            robot.MoveForward(450);
            grabLink.WaitEnd();




            //robot.MoveForward((int)(850 - 120 - Robots.MainRobot.LenghtBack)); // Pour s'aligner sur le centre de l'écueil

            //robot.GoToAngle(-90);
            //robot.SetSpeedSlow();
            //Actionneur.ElevatorLeft.DoGrabOpen();
            //Actionneur.ElevatorRight.DoGrabOpen();
            //robot.MoveForward(500);

            Threading.ThreadManager.CreateThread(link => { Actionneur.ElevatorLeft.DoSequencePickupColor(Buoy.Red); }).StartThread();
            Threading.ThreadManager.CreateThread(link => { Actionneur.ElevatorRight.DoSequencePickupColor(Buoy.Green); }).StartThread();

            List<Buoy> taken = GameBoard.Elements.Buoys.Where(b => b.Position.X < robot.Position.Coordinates.X + 200 && b.Position.X > robot.Position.Coordinates.X - 200 && b.Position.Y < 1000 && b.Position.Y > 0).ToList();
            taken.ForEach(b => b.IsAvailable = false);

            Thread.Sleep(500);

            robot.MoveBackward(35);
            robot.SetSpeedFast();
            robot.Pivot(180);

            // Ajouter ICI l'ordre de la strat fixe avant détection d'adversaire

            if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
            {
                fixedMovements.Add(new MovementGroundedZone(GameBoard.Elements.GroundedZones[2]));
                fixedMovements.Add(new MovementBuoy(GameBoard.Elements.FindBuoy(new RealPoint(300, 400))));
                fixedMovements.Add(new MovementLightHouse(GameBoard.Elements.LightHouses[0]));
                fixedMovements.Add(new MovementGreenDropoff(GameBoard.Elements.RandomDropoffs[0]));
                fixedMovements.Add(new MovementGroundedZone(GameBoard.Elements.GroundedZones[0]));
                fixedMovements.Add(new MovementRedDropoff(GameBoard.Elements.RandomDropoffs[0]));
            }
            else
            {
                fixedMovements.Add(new MovementGroundedZone(GameBoard.Elements.GroundedZones[1]));
                fixedMovements.Add(new MovementBuoy(GameBoard.Elements.FindBuoy(new RealPoint(2700, 400))));
                fixedMovements.Add(new MovementLightHouse(GameBoard.Elements.LightHouses[1]));
                fixedMovements.Add(new MovementGreenDropoff(GameBoard.Elements.RandomDropoffs[1]));
                fixedMovements.Add(new MovementGroundedZone(GameBoard.Elements.GroundedZones[3]));
                fixedMovements.Add(new MovementRedDropoff(GameBoard.Elements.RandomDropoffs[1]));
            }
        }

        protected override void SequenceCore()
        {
            Movement bestMovement;

            int iMovement = 0;
            bool ok = true;

            // Execution de la strat fixe tant que rien n'échoue
            while (ok && iMovement < fixedMovements.Count)
            {
                int score = fixedMovements[iMovement].Score;
                ok = fixedMovements[iMovement].Execute();
                if (ok) GameBoard.Score += score;
                iMovement++;
            }

            // Passage en mode recherche de la meilleure action
            while (IsRunning)
            {
                List<Movement> sorted = Movements.Where(m => m.IsCorrectColor() && m.CanExecute).OrderBy(m => m.GlobalCost).ToList();

                if (sorted.Count > 0)
                {
                    bestMovement = sorted[0];

                    if (bestMovement.GlobalCost != double.MaxValue && bestMovement.Value != 0)
                    {
                        int score = bestMovement.Score;

                        if (bestMovement.Execute())
                            GameBoard.Score += score;
                        else
                            bestMovement.Deactivate(new TimeSpan(0, 0, 1));
                    }
                    else
                    {
                        Robots.MainRobot.Historique.Log("Aucune action à effectuer");
                        Thread.Sleep(500);
                    }
                }
                else
                {
                    Robots.MainRobot.Historique.Log("Aucune action à effectuer");
                    Thread.Sleep(500);
                }
            }
        }
    }
}
<file_sep>/GoBot/GoBot/PathFinding/AStar.cs
// Copyright 2003 <NAME> - <<EMAIL>>
//
// This source file(s) may be redistributed by any means PROVIDING they
// are not sold for profit without the authors expressed written consent,
// and providing that this notice and the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
//-----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace AStarFolder
{
    /// <summary>
    /// A heuristic is a function that associates a value with a node to gauge it considering the node to reach.
    /// </summary>
    public delegate double Heuristic(Node NodeToEvaluate, Node TargetNode);

    /// <summary>
    /// Class to search the best path between two nodes on a graph.
    /// </summary>
    public class AStar
    {
        Graph _graph;
        Tracks _open;
        Dictionary<Node, Track> _closedTracks;
        Dictionary<Node, Track> _openTracks;
        Track _LeafToGoBackUp;
        int _iterations = -1;
        
        /// <summary>
        /// Heuristic based on the euclidian distance : Sqrt(Dx²+Dy²)
        /// </summary>
        public static Heuristic EuclidianHeuristic
        { get { return new Heuristic(Node.EuclidianDistance); } }
        
        /// <summary>
        /// Gets/Sets the heuristic that AStar will use.
        /// It must be homogeneous to arc's cost.
        /// </summary>
        public Heuristic ChoosenHeuristic
        {
            get { return Track.ChoosenHeuristic; }
            set { Track.ChoosenHeuristic = value; }
        }

        /// <summary>
        /// This value must belong to [0; 1] and it determines the influence of the heuristic on the algorithm.
        /// If this influence value is set to 0, then the search will behave in accordance with the Dijkstra algorithm.
        /// If this value is set to 1, then the cost to come to the current node will not be used whereas only the heuristic will be taken into account.
        /// </summary>
        /// <exception cref="ArgumentException">Value must belong to [0;1].</exception>
        public double DijkstraHeuristicBalance
        {
            get { return Track.DijkstraHeuristicBalance; }
            set
            {
                if (value < 0 || value > 1) throw new ArgumentException("DijkstraHeuristicBalance value must belong to [0;1].");
                Track.DijkstraHeuristicBalance = value;
            }
        }

        /// <summary>
        /// AStar Constructor.
        /// </summary>
        /// <param name="graph">The graph on which AStar will perform the search.</param>
        public AStar(Graph graph)
        {
            _graph = graph;
            _open = new Tracks();
            _openTracks = new Dictionary<Node, Track>();
            _closedTracks = new Dictionary<Node, Track>();
            ChoosenHeuristic = EuclidianHeuristic;
            DijkstraHeuristicBalance = 0.5;
        }

        /// <summary>
        /// Searches for the best path to reach the specified EndNode from the specified StartNode.
        /// </summary>
        /// <exception cref="ArgumentNullException">StartNode and EndNode cannot be null.</exception>
        /// <param name="startNode">The node from which the path must start.</param>
        /// <param name="endNode">The node to which the path must end.</param>
        /// <returns>'true' if succeeded / 'false' if failed.</returns>
        public bool SearchPath(Node startNode, Node endNode)
        {
            //lock (_graph)
            //{
                Initialize(startNode, endNode);
                while (NextStep()) { }
                return PathFound;
            //}
        }

        /// <summary>
        /// Use for a 'step by step' search only. This method is alternate to SearchPath.
        /// Initializes AStar before performing search steps manually with NextStep.
        /// </summary>
        /// <exception cref="ArgumentNullException">StartNode and EndNode cannot be null.</exception>
        /// <param name="StartNode">The node from which the path must start.</param>
        /// <param name="EndNode">The node to which the path must end.</param>
        protected void Initialize(Node StartNode, Node EndNode)
        {
            if (StartNode == null || EndNode == null) throw new ArgumentNullException();
            _closedTracks.Clear();
            _open.Clear();
            _openTracks.Clear();
            Track.Target = EndNode;
            Track newTrack = new Track(StartNode);
            _open.Add(newTrack);
            _openTracks.Add(StartNode, newTrack);
            _iterations = 0;
            _LeafToGoBackUp = null;
        }
        
        /// <summary>
        /// Use for a 'step by step' search only. This method is alternate to SearchPath.
        /// The algorithm must have been initialize before.
        /// </summary>
        /// <exception cref="InvalidOperationException">You must initialize AStar before using NextStep().</exception>
        /// <returns>'true' unless the search ended.</returns>
        protected bool NextStep()
        {
            if (_open.Count == 0) return false;
            _iterations++;
           // Console.WriteLine("_iterations : " + _iterations.ToString());
           
            Track bestTrack = _open[0];

            if (GoBot.Config.CurrentConfig.AfficheDetailTraj > 0)
            {
                GoBot.Dessinateur.CurrentTrack = bestTrack;
                System.Threading.Thread.Sleep(GoBot.Config.CurrentConfig.AfficheDetailTraj);
            }

            _open.RemoveAt(0);
            _openTracks.Remove(bestTrack.EndNode);

            if (bestTrack.Succeed)
            {
                _LeafToGoBackUp = bestTrack;
                _open.Clear();
                _openTracks.Clear();
            }
            else
            {
                Propagate(bestTrack);
            }

            return (_open.Count > 0);
        }

        private void Propagate(Track TrackToPropagate)
        {
            _closedTracks[TrackToPropagate.EndNode] = TrackToPropagate;
            
            foreach (Arc arc in TrackToPropagate.EndNode.OutgoingArcs)
            {
                if (arc.Passable && arc.EndNode.Passable)
                {
                    Track successor = new Track(TrackToPropagate, arc);
                    Track trackInClose, trackInOpen;

                    _closedTracks.TryGetValue(successor.EndNode, out trackInClose);
                    _openTracks.TryGetValue(successor.EndNode, out trackInOpen);

                    if (trackInClose != null && successor.Cost >= trackInClose.Cost)
                        continue;
                    if (trackInOpen != null && successor.Cost >= trackInOpen.Cost)
                        continue;

                    if (trackInClose != null)
                    {
                        _closedTracks.Remove(successor.EndNode);
                    }
                    if (trackInOpen != null)
                    {
                        _open.Remove(trackInOpen);
                    }

                    _open.Add(successor);
                    _openTracks[successor.EndNode] = successor;
                }
            }
        }
        
        /// <summary>
        /// To know if a path has been found.
        /// </summary>
        public bool PathFound { get { return _LeafToGoBackUp != null; } }

        /// <summary>
        /// Use for a 'step by step' search only.
        /// Gets the number of the current step.
        /// -1 if the search has not been initialized.
        /// 0 if it has not been started.
        /// </summary>
        public int StepCounter { get { return _iterations; } }
        
        /// <summary>
        /// Gets the array of nodes representing the found path.
        /// </summary>
        /// <exception cref="InvalidOperationException">You cannot get a result unless the search has ended.</exception>
        public Node[] PathByNodes
        {
            get
            {
                if (!PathFound) return null;
                return GoBackUpNodes(_LeafToGoBackUp);
            }
        }

        private Node[] GoBackUpNodes(Track T)
        {
            int Nb = T.NbArcsVisited;
            Node[] Path = new Node[Nb + 1];
            for (int i = Nb; i >= 0; i--, T = T.Queue)
                Path[i] = T.EndNode;
            return Path;
        }
    }
}

<file_sep>/GoBot/Composants/ColorDisplay.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Composants
{
    public partial class ColorDisplay : UserControl
    {
        public ColorDisplay()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Détermine la couleur actuellement affichée
        /// </summary>
        /// <param name="color">Couleur affichée</param>
        public void SetColor(Color c)
        {
            this.InvokeAuto(() =>
            {
                ColorPlus color = c;
                lblR.Text = color.Red.ToString();
                lblG.Text = color.Green.ToString();
                lblB.Text = color.Blue.ToString();

                lblH.Text = ((int)(color.Hue)).ToString();
                lblS.Text = ((int)(color.Saturation * 255)).ToString();
                lblL.Text = ((int)(color.Lightness * 255)).ToString();

                picColor.Image = MakeColorZone(picColor.Size, color);

                picR.Image = MakeColorBar(picR.Size, Color.Red, color.Red);
                picG.Image = MakeColorBar(picG.Size, Color.FromArgb(44, 208, 0), color.Green);
                picB.Image = MakeColorBar(picB.Size, Color.FromArgb(10, 104, 199), color.Blue);

                picH.Image = MakeColorBarRainbow(picR.Size, color.Hue);
                picS.Image = MakeColorBar2(picG.Size, ColorPlus.FromHsl(color.Hue, 0, 0.5), ColorPlus.FromHsl(color.Hue, 1, 0.5), (int)(color.Saturation * 255));
                picL.Image = MakeColorBar2(picB.Size, ColorPlus.FromHsl(color.Hue, 1, 0), ColorPlus.FromHsl(color.Hue, 1, 1), (int)(color.Lightness * 255));
            });
        }

        private Image MakeColorZone(Size sz, Color color)
        {
            Bitmap bmp = new Bitmap(sz.Width, sz.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.SmoothingMode = SmoothingMode.AntiAlias;

            SolidBrush brush = new SolidBrush(color);

            int margin = 2;

            g.FillEllipse(brush, new Rectangle(margin, margin, sz.Width - margin * 2, sz.Height - margin * 2));
            g.DrawEllipse(Pens.Black, new Rectangle(margin, margin, sz.Width - margin * 2, sz.Height - margin * 2));

            brush.Dispose();

            return bmp;
        }

        private Image MakeColorBar(Size sz, Color color, int value)
        {
            Bitmap bmp = new Bitmap(sz.Width, sz.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.SmoothingMode = SmoothingMode.AntiAlias;

            SolidBrush brush = new SolidBrush(color);

            int margin = 2;
            int height = (int)((sz.Height - margin * 2) * value / 255.0);
            Rectangle rect = new Rectangle(margin, sz.Height - margin - height, sz.Width - margin * 2, height);

            GraphicsPath path = CreateMixRect(rect, new List<int> { Math.Min(5, height), Math.Min(5, height), 0, 0 });

            g.FillPath(brush, path);
            g.DrawPath(Pens.Black, path);

            brush.Dispose();

            return bmp;
        }

        private Image MakeColorBar2(Size sz, ColorPlus colorBottom, ColorPlus colorTop, float value)
        {
            Bitmap bmp = new Bitmap(sz.Width, sz.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.SmoothingMode = SmoothingMode.AntiAlias;

            LinearGradientBrush brush = new LinearGradientBrush(new Point(0, bmp.Height), new Point(0, 0), colorBottom, colorTop);

            int margin = 2;
            int height = (int)(sz.Height - margin * 2);
            Rectangle rect = new Rectangle(margin, sz.Height - margin - height, sz.Width - margin * 2, height);

            GraphicsPath path = CreateMixRect(rect, new List<int> { Math.Min(2, height), Math.Min(2, height), Math.Min(2, height), Math.Min(2, height) });

            g.FillPath(brush, path);
            g.DrawPath(Pens.Black, path);

            height = (int)((sz.Height - margin * 2 - 2) * (1 - value / 255.0)) + 2;
            g.DrawLine(Pens.Black, 0, height - 1, bmp.Width, height - 1);
            g.DrawLine(Pens.White, 0, height, bmp.Width, height);
            g.DrawLine(Pens.Black, 0, height + 1, bmp.Width, height + 1);

            brush.Dispose();

            return bmp;
        }

        private Image MakeColorBarRainbow(Size sz, double hue)
        {
            Bitmap bmp = new Bitmap(sz.Width, sz.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.SmoothingMode = SmoothingMode.AntiAlias;

            int margin = 2;
            int totalHeight = (int)(sz.Height - margin * 2);
            Rectangle rect = new Rectangle(margin, sz.Height - margin - totalHeight, sz.Width - margin * 2, totalHeight);

            GraphicsPath path = CreateMixRect(rect, new List<int> { Math.Min(2, totalHeight), Math.Min(2, totalHeight), Math.Min(2, totalHeight), Math.Min(2, totalHeight) });

            int height = (int)((sz.Height - margin * 2 - 2) * (1 - hue / 360)) + 2;
            LinearGradientBrush brush = new LinearGradientBrush(new Point(0, height + totalHeight/2), new Point(0, height + totalHeight + totalHeight / 2), Color.White, Color.Black);

            ColorBlend colors = new ColorBlend(7);
            colors.Colors = new Color[] { Color.Red, Color.Yellow, Color.Lime, Color.Cyan, Color.Blue, Color.Magenta, Color.Red };
            colors.Positions = new float[] { 0 / 6f, 1 / 6f, 2 / 6f, 3 / 6f, 4 / 6f, 5 / 6f, 6 / 6f };

            brush.InterpolationColors = colors;

            g.FillPath(brush, path);
            g.DrawPath(Pens.Black, path);

            height = (int)(sz.Height /2);

            g.DrawLine(Pens.Black, 0, height - 1, bmp.Width, height - 1);
            g.DrawLine(Pens.White, 0, height, bmp.Width, height);
            g.DrawLine(Pens.Black, 0, height + 1, bmp.Width, height + 1);

            brush.Dispose();

            return bmp;
        }

        private GraphicsPath CreateMixRect(Rectangle rct, List<int> rays)
        {
            GraphicsPath pth = new GraphicsPath();
            int dd = 1;
            
            if (rays[0] > 0)
                pth.AddArc(rct.X, rct.Y, rays[0] * 2, rays[0] * 2, 180, 90);

            pth.AddLine(rct.X + rays[0], rct.Y, rct.Right - dd - rays[1], rct.Y);

            if ((rays[1] > 0))
                pth.AddArc(rct.Right - dd - 2 * rays[1], rct.Y, rays[1] * 2, rays[1] * 2, 270, 90);

            pth.AddLine(rct.Right - dd, rct.Y + rays[1], rct.Right - dd, rct.Bottom - dd - rays[2]);

            if ((rays[2] > 0))
                pth.AddArc(rct.Right - dd - 2 * rays[2], rct.Bottom - dd - 2 * rays[2], rays[2] * 2, rays[2] * 2, 0, 90);

            pth.AddLine(rct.Right - dd - rays[2], rct.Bottom - dd, rct.X + rays[3], rct.Bottom - dd);

            if ((rays[3] > 0))
                pth.AddArc(rct.X, rct.Bottom - dd - 2 * rays[3], rays[3] * 2, rays[3] * 2, 90, 90);

            pth.AddLine(rct.X, rct.Bottom - dd - rays[3], rct.X, rct.Y + rays[0]);

            pth.CloseFigure();

            return pth;
            
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PagePanda.cs
﻿using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

using GoBot.Communications;
using GoBot.Devices;
using GoBot.IHM.Forms;
using GoBot.Threading;

namespace GoBot.IHM.Pages
{
    public partial class PagePanda : UserControl
    {
        private ThreadLink _linkBattery;

        public PagePanda()
        {
            InitializeComponent();
        }

        private void PagePanda_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                Connections.AllConnections.ForEach(c => c.ConnectionChecker.ConnectionStatusChange += ConnectionChecker_ConnectionStatusChange);
                AllDevices.LidarAvoid.ConnectionChecker.ConnectionStatusChange += LidarAvoid_ConnectionStatusChange;

                SetPicImage(picLidar, Devices.AllDevices.LidarAvoid.ConnectionChecker.Connected);
                SetPicImage(picIO, Connections.ConnectionIO.ConnectionChecker.Connected);
                SetPicImage(picMove, Connections.ConnectionMove.ConnectionChecker.Connected);
                SetPicImage(picCAN, Connections.ConnectionCanBridge.ConnectionChecker.Connected);
                SetPicImage(picServo1, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo1].ConnectionChecker.Connected);
                SetPicImage(picServo2, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo2].ConnectionChecker.Connected);
                SetPicImage(picServo3, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo3].ConnectionChecker.Connected);
                SetPicImage(picServo4, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo4].ConnectionChecker.Connected);
                SetPicImage(picServo5, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo5].ConnectionChecker.Connected);
                SetPicImage(picServo6, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo6].ConnectionChecker.Connected);

                _linkBattery = ThreadManager.CreateThread(link => UpdateBatteryIcon());
                _linkBattery.StartInfiniteLoop(1000);

                pagePandaMatch.CalibrationDone += PagePandaMatch_CalibrationDone;
            }
        }

        private void PagePandaMatch_CalibrationDone()
        {
            tabControlPanda.InvokeAuto(() => ChangePageDelay());
        }

        private void ChangePageDelay(int delayMs = 500)
        {
            Thread.Sleep(500); tabControlPanda.SelectedIndex += 1;
        }

        private void UpdateBatteryIcon()
        {
            _linkBattery?.RegisterName();

            picBattery.InvokeAuto(() =>
            {
                if (!Robots.Simulation && !Connections.ConnectionCanAlim.ConnectionChecker.Connected)
                {
                    picBattery.Image = Properties.Resources.BatteryUnknow96;
                    lblBattery.Text = ("--,--") + "V";
                }
                else
                {
                    double voltage = Robots.MainRobot.BatterieVoltage;

                    if (voltage > Config.CurrentConfig.BatterieRobotVert)
                        picBattery.Image = Properties.Resources.BatteryFull96;
                    else if (voltage > Config.CurrentConfig.BatterieRobotOrange)
                        picBattery.Image = Properties.Resources.BatteryMid96;
                    else if (voltage > Config.CurrentConfig.BatterieRobotRouge)
                        picBattery.Image = Properties.Resources.BatteryLow96;
                    else if (voltage > Config.CurrentConfig.BatterieRobotCritique)
                        picBattery.Image = Properties.Resources.BatteryCritical96;
                    else
                        picBattery.Image = Properties.Resources.BatteryUnknow96;

                    lblBattery.Text = voltage.ToString("00.00") + "V";
                }
            });
        }

        private void LidarAvoid_ConnectionStatusChange(Connection sender, bool connected)
        {
            SetPicImage(picLidar, connected);
        }

        private void ConnectionChecker_ConnectionStatusChange(Connection sender, bool connected)
        {
            if (sender == Connections.ConnectionIO)
                SetPicImage(picIO, connected);
            else if (sender == Connections.ConnectionMove)
                SetPicImage(picMove, connected);
            else if (sender == Connections.ConnectionCanBridge)
                SetPicImage(picCAN, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo1])
                SetPicImage(picServo1, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo2])
                SetPicImage(picServo2, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo3])
                SetPicImage(picServo3, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo4])
                SetPicImage(picServo4, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo5])
                SetPicImage(picServo5, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo6])
                SetPicImage(picServo6, connected);
        }

        private static void SetPicImage(PictureBox pic, bool ok)
        {
            pic.Image = ok ? Properties.Resources.ValidOk48 : Properties.Resources.ValidNok48;
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            FormConfirm form = new FormConfirm();

            if (form.ShowDialog() == DialogResult.Yes)
                this.ParentForm.Close();

            form.Dispose();
        }

        private void btnNextPage_Click(object sender, EventArgs e)
        {
            tabControlPanda.SelectedIndex = (tabControlPanda.SelectedIndex + 1) % tabControlPanda.TabCount;
        }

        #region Buttons style

        private void btnClose_MouseDown(object sender, MouseEventArgs e)
        {
            btnClose.Location = new Point(btnClose.Location.X + 2, btnClose.Location.Y + 2);
        }

        private void btnClose_MouseUp(object sender, MouseEventArgs e)
        {
            btnClose.Location = new Point(btnClose.Location.X - 2, btnClose.Location.Y - 2);
        }

        private void btnNextPage_MouseDown(object sender, MouseEventArgs e)
        {
            btnNextPage.Location = new Point(btnNextPage.Location.X + 2, btnNextPage.Location.Y + 2);
        }

        private void btnNextPage_MouseUp(object sender, MouseEventArgs e)
        {
            btnNextPage.Location = new Point(btnNextPage.Location.X - 2, btnNextPage.Location.Y - 2);
        }

        #endregion

        private void tabControlPanda_SelectedIndexChanged(object sender, EventArgs e)
        {
            pagePandaLidar.LidarEnable(tabControlPanda.SelectedTab == tabPandaLidar);
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/ActionMoteur.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionMoteur : IAction
    {
        private int vitesse;
        MotorID moteur;
        private Robot robot;

        public ActionMoteur(Robot r, int vi, MotorID mo)
        {
            robot = r;
            vitesse = vi;
            moteur = mo;
        }

        public override String ToString()
        {
            return robot + " tourne " + NameFinder.GetName(moteur) + " à " + NameFinder.GetName(vitesse, moteur);
        }

        void IAction.Executer()
        {
            robot.SetMotorAtPosition(moteur, vitesse);
        }

        public System.Drawing.Image Image
        {
            get 
            { 
                return GoBot.Properties.Resources.Motor16;  
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageCheckSpeed.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PageCheckSpeed : UserControl
    {
        public PageCheckSpeed()
        {
            InitializeComponent();
        }

        private void btnTest_Click(object sender, EventArgs e)
        {
            Threading.ThreadManager.CreateThread(link =>
            {
                List<int>[] vals = Robots.MainRobot.DiagnosticLine((int)numDistance.Value, SensAR.Arriere);
                SetPoints(vals[0], vals[1]);
            }).StartThread();
        }

        private void SetPoints(List<int> left, List<int> right)
        {
            List<double> posLeft = Smooth(TargetCount(left.ConvertAll(v => (double)v), gphPos.Width), 30);
            List<double> posRight = Smooth(TargetCount(right.ConvertAll(v => (double)v), gphPos.Width), 30);
            List<double> speedLeft = Derivation(posLeft, 30);
            List<double> speedRight = Derivation(posRight, 30);
            List<double> accelLeft = Derivation(speedLeft, 30);
            List<double> accelRight = Derivation(speedRight, 30);

            gphPos.DeleteCurve("Gauche pos.");
            gphPos.DeleteCurve("Droite pos.");
            gphPos.DeleteCurve("Gauche Vit.");
            gphPos.DeleteCurve("Droite Vit.");
            gphPos.DeleteCurve("Gauche Accel.");
            gphPos.DeleteCurve("Droite Accel.");

            for (int i = 0; i < posLeft.Count; i++)
            {
                gphPos.AddPoint("Gauche pos.", posLeft[i], Color.Lime, false);
                gphPos.AddPoint("Droite pos.", posRight[i], Color.OrangeRed, false);
                gphPos.AddPoint("Gauche Vit.", speedLeft[i], Color.Green, false);
                gphPos.AddPoint("Droite Vit.", speedRight[i], Color.Red, false);
                gphPos.AddPoint("Gauche Accel.", accelLeft[i], Color.DarkGreen, false);
                gphPos.AddPoint("Droite Accel.", accelRight[i], Color.DarkRed, false);
            }

            gphPos.DrawCurves();
        }

        private List<double> Derivation(List<double> values, int interval = 1)
        {
            List<double> output = new List<double>();

            for (int i = 0; i < values.Count; i++)
            {
                output.Add(values[Math.Min(i + interval, values.Count - 1)] - values[i]);
            }

            return output;
        }

        private List<double> TargetCount(List<double> values, int count)
        {
            List<double> output;

            if (values.Count > count)
            {
                output = new List<double>();
                int avg = (int)Math.Ceiling((double)values.Count / count);
                for (int i = 0; i < values.Count - avg; i++)
                {
                    output.Add(values.GetRange(i, avg).Average());
                }
            }
            else
            {
                output = new List<double>(values);

                while (values.Count < count / 2)
                {
                    output = new List<double>();

                    for (int i = 0; i < values.Count - 1; i++)
                    {
                        output.Add(values[i]);
                        output.Add((values[i] + values[i + 1]) / 2);
                    }

                    values = output;
                }
            }

            return output;
        }

        private List<double> Smooth(List<double> values, int count)
        {
            List<double> output = new List<double>();

            for (int i = 0; i < values.Count - count; i++)
                output.Add(values.GetRange(i, count).Average());

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/Strategies/Strategy.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;

using GoBot.Movements;
using AStarFolder;
using GoBot.Threading;
using System.Diagnostics;
using GoBot.BoardContext;
using GoBot.Devices;
using GoBot.Actionneurs;

namespace GoBot.Strategies
{
    public abstract class Strategy
    {
        private System.Timers.Timer endMatchTimer;
        private ThreadLink _linkMatch;

        public abstract bool AvoidElements { get; }

        /// <summary>
        /// Obtient ou définit la durée d'un match
        /// </summary>
        public TimeSpan MatchDuration { get; set; }

        /// <summary>
        /// Retourne vrai si le match est en cours d'execution
        /// </summary>
        public bool IsRunning
        {
            get
            {
                return _linkMatch != null && _linkMatch.Running;
            }
        }

        /// <summary>
        /// Retourne l'heure de début du match
        /// </summary>
        public DateTime StartingDateTime { get; protected set; }

        /// <summary>
        /// Retourne le temps écoulé depuis le début du match
        /// </summary>
        public TimeSpan TimeSinceBegin
        {
            get
            {
                return DateTime.Now - StartingDateTime;
            }
        }
        /// <summary>
        /// Retourne le temps restant avant la fin du match
        /// </summary>
        public TimeSpan TimeBeforeEnd
        {
            get
            {
                return (StartingDateTime + MatchDuration) - DateTime.Now;
            }
        }

        /// <summary>
        /// Contient la liste de tous les mouvements du match
        /// </summary>
        public List<Movement> Movements { get; protected set; }

        /// <summary>
        /// Constructeur
        /// </summary>
        public Strategy()
        {
            if (Debugger.IsAttached)
            {
                MatchDuration = new TimeSpan(0, 0, 100);
            }
            else
            {
                MatchDuration = new TimeSpan(0, 0, 100);
            }

            Movements = new List<Movement>();

            // TODOEACHYEAR Charger ICI dans Movements les mouvements possibles

            for (int i = 0; i < GameBoard.Elements.GroundedZones.Count; i++)
                Movements.Add(new MovementGroundedZone(GameBoard.Elements.GroundedZones[i]));

            for (int i = 0; i < GameBoard.Elements.RandomDropoffs.Count; i++)
            {
                Movements.Add(new MovementRandomDropoff(GameBoard.Elements.RandomDropoffs[i]));
                Movements.Add(new MovementGreenDropoff(GameBoard.Elements.RandomDropoffs[i]));
                Movements.Add(new MovementRedDropoff(GameBoard.Elements.RandomDropoffs[i]));
            }

            for (int i = 0; i < GameBoard.Elements.ColorDropoffs.Count; i++)
                Movements.Add(new MovementColorDropoff(GameBoard.Elements.ColorDropoffs[i]));

            for (int i = 0; i < GameBoard.Elements.LightHouses.Count; i++)
                Movements.Add(new MovementLightHouse(GameBoard.Elements.LightHouses[i]));

            //Movements.Add(new MovementBuoy(GameBoard.Elements.FindBuoy(new Geometry.Shapes.RealPoint(300, 400))));
            //Movements.Add(new MovementBuoy(GameBoard.Elements.FindBuoy(new Geometry.Shapes.RealPoint(2700, 400))));

            if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
                for (int i = 0; i < GameBoard.Elements.BuoysForLeft.Count; i++)
                    Movements.Add(new MovementBuoy(GameBoard.Elements.BuoysForLeft[i]));
            else
                for (int i = 0; i < GameBoard.Elements.BuoysForRight.Count; i++)
                    Movements.Add(new MovementBuoy(GameBoard.Elements.BuoysForRight[i]));

            for (int iMov = 0; iMov < Movements.Count; iMov++)
            {
                for (int iPos = 0; iPos < Movements[iMov].Positions.Count; iPos++)
                {
                    if (!Movements[iMov].Robot.Graph.Raccordable(new Node(Movements[iMov].Positions[iPos].Coordinates),
                        GameBoard.ObstaclesAll,
                        Movements[iMov].Robot.Radius))
                    {
                        Movements[iMov].Positions.RemoveAt(iPos);
                        iPos--;
                    }
                }
            }

            Movements.Add(new MovementRandomPickup(GameBoard.Elements.RandomPickup));
        }

        /// <summary>
        /// Execute le match
        /// </summary>
        public void ExecuteMatch()
        {
            Robots.MainRobot.Historique.Log("DEBUT DU MATCH", TypeLog.Strat);

            StartingDateTime = DateTime.Now;

            GameBoard.StartMatch();

            endMatchTimer = new System.Timers.Timer();
            endMatchTimer.Elapsed += new ElapsedEventHandler(endMatchTimer_Elapsed);
            endMatchTimer.Interval = MatchDuration.TotalMilliseconds;
            endMatchTimer.Start();

            _linkMatch = ThreadManager.CreateThread(link => Execute());
            _linkMatch.StartThread();
        }

        /// <summary>
        /// Interrompt le match
        /// </summary>
        public void Stop()
        {
            _linkMatch.Kill();

            SequenceEnd();
        }

        private void endMatchTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Robots.MainRobot.Historique.Log("FIN DU MATCH", TypeLog.Strat);

            endMatchTimer.Stop();
            _linkMatch.Kill();

            SequenceEnd();
        }

        private void Execute()
        {
            _linkMatch.RegisterName();

            SequenceBegin();
            SequenceCore();
        }

        /// <summary>
        /// Contient l'execution des actions au début du match
        /// </summary>
        protected abstract void SequenceBegin();

        /// <summary>
        /// Contient l'execution des actions du match
        /// </summary>
        protected abstract void SequenceCore();

        /// <summary>
        /// Contient l'execution des actions à la fin du match
        /// </summary>
        protected virtual void SequenceEnd()
        {
            // TODOEACHYEAR Couper ICI tous les actionneurs à la fin du match et lancer la Funny Action / afficher le score

            Robots.MainRobot.Stop(StopMode.Freely);

            Actionneur.ElevatorLeft.DoElevatorFree();
            Actionneur.ElevatorRight.DoElevatorFree();

            MovementFlags moveFlags = new MovementFlags();
            if (moveFlags.ExecuteHere())
                GameBoard.Score += moveFlags.Score;

            System.Threading.Thread.Sleep(1000);

            AllDevices.CanServos.DisableAll();

            // On renvoie le score au cas où histoire d'assurer le truc...

            if (!Config.CurrentConfig.IsMiniRobot)
                Robots.MainRobot.ShowMessage("Estimation :", GameBoard.Score.ToString() + " points");
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/LineWithRealPoint.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class LineWithRealPoint
    {
        public static bool Contains(Line containingLine, RealPoint containedPoint)
        {
            bool output;

            if (containingLine.IsHorizontal)
                output = Math.Abs(containedPoint.Y - containingLine.B) <= RealPoint.PRECISION;
            else if (containingLine.IsVertical)
                output = Math.Abs(containedPoint.X - -containingLine.B) <= RealPoint.PRECISION;
            else
            {
                // Vérifie si le point est sur la droite en vérifiant sa coordonnée Y pour sa coordonnée X par rapport à l'équation de la droite

                double expectedY = containingLine.A * containedPoint.X + containingLine.B;
                output = Math.Abs(expectedY - containedPoint.Y) <= RealPoint.PRECISION;
            }

            return output;
        }

        public static bool Cross(Line line, RealPoint point)
        {
            // Pour qu'une droite croise un point c'est qu'elle contient le point...

            return Contains(line, point);
        }

        public static double Distance(Line line, RealPoint point)
        {
            // Pour calculer la distance, on calcule la droite perpendiculaire passant par ce point
            // Puis on calcule l'intersection de la droite et de sa perpendiculaire
            // On obtient la projection orthogonale du point, qui est le point de la droite le plus proche du point donné
            // On retourne la distance entre ces deux points
            
            return point.Distance(line.GetProjection(point));
        }


        public static List<RealPoint> GetCrossingPoints(Line line, RealPoint point)
        {
            // Si la droite contient le point, son seul croisement c'est le point lui même...

            List<RealPoint> output = new List<RealPoint>();

            if (line.Contains(point)) 
                output.Add(new RealPoint(point));

            return output;
        }
    }
}
<file_sep>/GoBot/GeometryTester/TestRectangles.cs
﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Geometry.Shapes;

namespace GeometryTester
{
    [TestClass]
    public class TestRectangle
    {
        //[TestMethod]                // Test bidon histoire de charger les librairies et vérifier temps execution des tests suivants
        //public void DummyTest()
        //{
        //    Polygon r0 = new PolygonRectangle(new RealPoint(0, 0), 0, 0);
        //    Assert.AreEqual(r0, r0.Rotation(90));
        //}

        // Tests d'égalité entre plusieurs rectangles
        [TestMethod]
        public void TestRectangleEqual()
        {
            Polygon r0 = new PolygonRectangle(new RealPoint(0, 0), 0, 0);

            Assert.AreEqual(r0, r0);
            Assert.AreEqual(r0, r0.Rotation(90));
            Assert.AreEqual(r0, r0.Rotation(-180));
            Assert.AreEqual(r0, r0.Rotation(-90));

            Polygon r1 = new PolygonRectangle(new RealPoint(0, 0), 100, 100);
            Polygon r2 = new PolygonRectangle(new RealPoint(0, 0), 100, 100);

            Assert.AreEqual(r1, r1);
            Assert.AreEqual(r1, r2);
            Assert.AreEqual(r1, r2.Rotation(90));
            Assert.AreEqual(r1, r2.Rotation(180));
            Assert.AreEqual(r1, r2.Rotation(-90));

            r1 = new PolygonRectangle(new RealPoint(10, 10), 100, 100);
            r2 = new PolygonRectangle(new RealPoint(10, 10), 100, 100);

            Assert.AreEqual(r1, r1);
            Assert.AreEqual(r1, r2);
            Assert.AreEqual(r1, r2.Rotation(90));
            Assert.AreEqual(r1, r2.Rotation(180));
            Assert.AreEqual(r1, r2.Rotation(-90));

            r1 = new PolygonRectangle(new RealPoint(-10, -10), -100, -100);
            r2 = new PolygonRectangle(new RealPoint(-10, -10), -100, -100);

            Assert.AreEqual(r1, r1);
            Assert.AreEqual(r1, r2);
            Assert.AreEqual(r1, r2.Rotation(90));
            Assert.AreEqual(r1, r2.Rotation(180));
            Assert.AreEqual(r1, r2.Rotation(-90));
        }

        // Tests de distance entre rectangles et segments
        [TestMethod]
        public void TestRectangleSegmentDistance()
        {
            Polygon r1 = new PolygonRectangle(new RealPoint(0, 0), 10, 10);

            // Test avec segments confondus
            Segment s11 = new Segment( new RealPoint(0, 0), new RealPoint(0, 10) );
            Segment s12 = new Segment(new RealPoint(0, 10), new RealPoint(10, 10));
            Segment s13 = new Segment(new RealPoint(10, 10), new RealPoint(10, 0));
            Segment s14 = new Segment(new RealPoint(10, 0), new RealPoint(0, 0));

            Assert.AreEqual(0, r1.Distance(s11));
            Assert.AreEqual(0, r1.Distance(s12));
            Assert.AreEqual(0, r1.Distance(s13));
            Assert.AreEqual(0, r1.Distance(s14));

            // Test avec segments décales
            Segment s21 = new Segment(new RealPoint(20, 0), new RealPoint(20, 10));
            Segment s22 = new Segment(new RealPoint(-10, 0), new RealPoint(-10, 10));
            Segment s23 = new Segment(new RealPoint(0, -10), new RealPoint(10, -10));
            Segment s24 = new Segment(new RealPoint(0, 20), new RealPoint(10, 20));

            Assert.AreEqual(10, r1.Distance(s21));
            Assert.AreEqual(10, r1.Distance(s22));
            Assert.AreEqual(10, r1.Distance(s23));
            Assert.AreEqual(10, r1.Distance(s24));
        }

        // Tests de distance entre plusieurs rectangles
        [TestMethod]
        public void TestRectanglesDistance()
        {
            Polygon r1 = new PolygonRectangle(new RealPoint(0, 0), 10, 10);

            // Polygones décalés vérticalements OU horizontalement + coincidence segment
            Polygon r11 = new PolygonRectangle(new RealPoint(10, 0), 10, 10);
            Polygon r12 = new PolygonRectangle(new RealPoint(-10, 0), 10, 10);
            Polygon r13 = new PolygonRectangle(new RealPoint(0, 10), 10, 10);
            Polygon r14 = new PolygonRectangle(new RealPoint(0, -10), 10, 10);

            Assert.AreEqual(0, r1.Distance(r11));
            Assert.AreEqual(0, r1.Distance(r12));
            Assert.AreEqual(0, r1.Distance(r13));
            Assert.AreEqual(0, r1.Distance(r14));

            // Polygones décalés vérticalements ou horizontalement + coincidence coin
            Polygon r21 = new PolygonRectangle(new RealPoint(10, 0), 10, 10);
            Polygon r22 = new PolygonRectangle(new RealPoint(-10, 0), 10, 10);
            Polygon r23 = new PolygonRectangle(new RealPoint(0, 10), 10, 10);
            Polygon r24 = new PolygonRectangle(new RealPoint(0, -10), 10, 10);

            Assert.AreEqual(0, r1.Distance(r21));
            Assert.AreEqual(0, r1.Distance(r22));
            Assert.AreEqual(0, r1.Distance(r23));
            Assert.AreEqual(0, r1.Distance(r24));

            // Polygones décalés vérticalements ET horizontalement
            Polygon r31 = new PolygonRectangle(new RealPoint(20, 20), 10, 10);
            Polygon r32 = new PolygonRectangle(new RealPoint(-20, -20), 10, 10);
            Polygon r33 = new PolygonRectangle(new RealPoint(-20, -20), 10, 10);

            Assert.AreEqual( Math.Sqrt(10*10+10*10), r1.Distance(r31));
            Assert.AreEqual( Math.Sqrt(10*10+10*10), r1.Distance(r32));

        }

        // Tests de distance entre plusieurs rectangles qui se croisent
        [TestMethod]
        public void TestCrossRectanglesDistance()
        {
            Polygon r1 = new PolygonRectangle(new RealPoint(0, 0), 10, 10);
            
            Polygon r11 = new PolygonRectangle(new RealPoint(5, 5), 10, 10);        // Rectangles qui se croisent sur 2 points

            Assert.AreEqual(0, r1.Distance(r11));

            Polygon r12 = new PolygonRectangle(new RealPoint(2, 2), 6, 6);          // Rectangles imbriqués

            Assert.AreEqual(0, r1.Distance(r12));                                   // Test imbrication rectangle A dans B
            Assert.AreEqual(0, r12.Distance(r1));                                   // Test imbrication rectangle B dans A                                   
        }

        // Tests de distance entre rectangle et Cercle
        [TestMethod]
        public void TestRectangleAndCircleDistance()
        {
            Polygon r1 = new PolygonRectangle(new RealPoint(0, 0), 10, 10);

            Circle c11 = new Circle(new RealPoint(0, 0), 10);

            Assert.AreEqual(0, r1.Distance(c11));
            Assert.AreEqual(0, c11.Distance(r1));

        }

        // Tests de distance entre rectangle et Cercle imbriqués
        [TestMethod]
        public void TestCrossRectangleAndCircleDistance()
        {
            Polygon r1 = new PolygonRectangle(new RealPoint(0, 0), 10, 10);

            Circle c11 = new Circle( new RealPoint(5, 5), 2 );

            Assert.AreEqual(0, r1.Distance(c11));
            Assert.AreEqual(0, c11.Distance(r1));
        }

    }
}
<file_sep>/GoBot/GoBot/Actions/Asservissement/ActionVitessePivot.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionVitessePivot : IAction
    {
        private int _speed;
        private Robot _robot;

        public ActionVitessePivot(Robot r, int speed)
        {
            _robot = r;
            _speed = speed;
        }

        public override String ToString()
        {
            return _robot.Name + " vitesse pivot à " + _speed;
        }

        void IAction.Executer()
        {
            _robot.SpeedConfig.PivotSpeed = _speed;
        }

        public System.Drawing.Image Image
        {
            get 
            { 
                return GoBot.Properties.Resources.Speed16;  
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/Frame.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace GoBot.Communications
{
    [Serializable]
    public class Frame
    {
        /// <summary>
        /// Liste des octets de la trame
        /// </summary>
        private List<Byte> Bytes { get; set; }

        /// <summary>
        /// Liste des séparateurs entre les différents octets dans une chaine à convertir en trame
        /// </summary>
        private static char[] _separators = { '.', ' ', ':', ';', '-', '_', ',', '/', '\\' };

        /// <summary>
        /// Construit une trame à partir d'un tableau d'octets
        /// </summary>
        /// <param name="message">Octets à convertir</param>
        public Frame(IEnumerable<Byte> message)
        {
            Bytes = new List<Byte>();

            foreach (Byte b in message)
                Bytes.Add((Byte)b);
        }

        /// <summary>
        /// Construit une trame à partir d'une chaine de caractères (Format "01 23 45 67 89 AB CD EF")
        /// </summary>
        /// <param name="message">Message à convertir</param>
        public Frame(String message)
        {
            Bytes = new List<Byte>();

            String[] splits = message.Split(_separators,StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < splits.Length; i++)
            {
                try
                {
                    Bytes.Add(byte.Parse(splits[i], System.Globalization.NumberStyles.HexNumber));
                }
                catch (Exception)
                {
                    Bytes.Add(0);
                }
            }
        }

        /// <summary>
        /// Retourne l'octet à l'index i
        /// </summary>
        /// <param name="i">Index de l'octet à retourner</param>
        /// <returns>Octet à l'index i</returns>
        public Byte this[int i]
        {
            get
            {
                return At(i);
            }
        }

        /// <summary>
        /// Retourne l'octet à l'index i
        /// </summary>
        /// <param name="i">Index de l'octet à retourner</param>
        /// <returns>Octet à l'index i</returns>
        public Byte At(int i)
        {
            int num = Bytes.ElementAt(i);

            if(num >= 0 && num <= 255)
                return (Byte)num;

            return 0;
        }

        override public String ToString()
        {
            String output = "";

            foreach (byte b in Bytes)
            {
                String val = String.Format("{0:x}", b);
                if (val.Length == 1)
                    val = "0" + val;

                output = output + val + " ";
            }

            output = output.TrimEnd().ToUpper();

            return output;
        }

        /// <summary>
        /// Convertit la trame en tableau d'octets
        /// </summary>
        /// <returns>Tableau d'octets correspondant à la trame</returns>
        public byte[] ToBytes()
        {
            byte[] retour = new byte[Bytes.Count];

            for(int i = 0; i < Bytes.Count; i++)
            {
                retour[i] = Bytes[i];
            }

            return retour;
        }

        /// <summary>
        /// Longueur de la trame
        /// </summary>
        public int Length
        {
            get
            {
                return Bytes.Count;
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/Line.cs
﻿using Geometry.Shapes.ShapesInteractions;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace Geometry.Shapes
{
    /// <summary>
    /// Droite avec une équation de la forme cy = ax + b
    /// Pour une droite standard, c = 1
    /// Pour une droite verticale, c = 0 et a = 1
    /// </summary>
    public class Line : IShape, IShapeModifiable<Line>
    {
        #region Attributs

        protected double _a, _b, _c;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Constructeur par défaut y = 0
        /// </summary>
        public Line()
        {
            _a = 0;
            _b = 0;
            _c = 1;
        }

        /// <summary>
        /// Constructeur type cy = ax + b
        /// </summary>
        /// <param name="a">A</param>
        /// <param name="b">B</param>
        /// <param name="c">C</param>
        public Line(double a, double b, double c = 1)
        {
            this._a = a;
            this._b = b;
            this._c = c;
        }

        /// <summary>
        /// Construit la droite passant par deux points
        /// </summary>
        /// <param name="p1">Premier point</param>
        /// <param name="p2">Deuxième point</param>
        public Line(RealPoint p1, RealPoint p2)
        {
            SetLine(p1, p2);
        }

        /// <summary>
        /// Construit la Droite à partir d'une autre Droite
        /// </summary>
        /// <param name="line">Droite à copier</param>
        public Line(Line line)
        {
            _a = line.A;
            _b = line.B;
            _c = line.C;
        }

        /// <summary>
        /// Contruit la Droite à partir d'une liste de points en interpolation.
        /// Régression linéaire par la méthode des moindres carrés.
        /// </summary>
        /// <param name="points">Liste des points qui génèrent la droite</param>
        public Line(IEnumerable<RealPoint> points)
        {
            double xAvg, yAvg, sum1, sum2;

            sum1 = 0;
            sum2 = 0;
            xAvg = points.Average(p => p.X);
            yAvg = points.Average(p => p.Y);

            foreach (RealPoint p in points)
            {
                sum1 += (p.X - xAvg) * (p.Y - yAvg);
                sum2 += (p.X - xAvg) * (p.X - xAvg);
            }

            if (sum2 == 0)
            {
                // Droite verticale
                _a = 0;
                _b = -points.ElementAt(0).X;
                _c = 0;
            }
            else
            {
                _a = sum1 / sum2;
                _b = yAvg - _a * xAvg;
                _c = 1;
            }
        }

        /// <summary>
        /// Calcule l'équation de la droite passant par deux points
        /// </summary>
        /// <param name="p1">Premier point</param>
        /// <param name="p2">Deuxième point</param>
        protected void SetLine(RealPoint p1, RealPoint p2)
        {
            if (p2.X - p1.X == 0)
            {
                _a = 1;
                _b = -p1.X;
                _c = 0;
            }
            else
            {
                _a = (p2.Y - p1.Y) / (p2.X - p1.X);
                _b = -(_a * p1.X - p1.Y);
                _c = 1;
            }
        }

        #endregion

        #region Propriétés

        /// <summary>
        /// Accesseur sur le paramètre A de la droite sous la forme cy = ax + b
        /// </summary>
        public double A
        {
            get
            {
                return _a;
            }
        }

        /// <summary>
        /// Accesseur sur le paramètre B de la droite sous la forme cy = ax + b
        /// </summary>
        public double B
        {
            get
            {
                return _b;
            }
        }

        /// <summary>
        /// Accesseur sur le paramètre C de la droite sous la forme cy = ax + b
        /// </summary>
        public double C
        {
            get
            {
                return _c;
            }
        }

        /// <summary>
        /// Retourne vrai si la droite est parrallèle à l'axe des abscisses.
        /// Dans ce cas l'équation donne Y = B
        /// </summary>
        public bool IsHorizontal
        {
            get
            {
                return _a == 0;
            }
        }

        /// <summary>
        /// Retourne vrai si la droite est parallèle à l'axe des ordonnées.
        /// Dans ce cas l'équation donne X = -B
        /// </summary>
        public bool IsVertical
        {
            get
            {
                return _c == 0;
            }
        }

        /// <summary>
        /// Obtient la surface de la droite
        /// </summary>
        public virtual double Surface
        {
            get
            {
                return 0;
            }
        }

        /// <summary>
        /// Obtient le barycentre de la droite
        /// </summary>
        public virtual RealPoint Barycenter
        {
            get
            {
                return null; // Ca n'existe pas le barycentre d'une droite
            }
        }

        #endregion

        #region Opérateurs & Surcharges

        public static bool operator ==(Line a, Line b)
        {
            if ((object)a == null || (object)b == null)
                return (object)a == null && (object)b == null;
            else
                return Math.Abs(a.A - b.A) < RealPoint.PRECISION
                    && Math.Abs(a.B - b.B) < RealPoint.PRECISION
                    && Math.Abs(a.C - a.C) < RealPoint.PRECISION;
        }

        public static bool operator !=(Line a, Line b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            Line p = obj as Line;
            if ((Object)p == null)
            {
                return false;
            }

            return (Line)obj == this;
        }

        public override int GetHashCode()
        {
            return (int)_a ^ (int)_b ^ (int)_c;
        }

        /// <summary>
        /// Retourne une chaine contenant l'équation de la Droite
        /// Droite verticale : x = valeur
        /// Droite horizontale : y = valeur
        /// Droite "normale" : y = valeur X + valeur
        /// </summary>
        /// <returns>Chaine représentant la Droite</returns>
        public override string ToString()
        {
            String output;

            String cString = C != 1 ? C.ToString("0.00") : "";
            String aString = A != 1 ? A.ToString("0.00") : "";

            if (this.IsVertical)
                output = "X = " + ((-B).ToString("0.00"));
            else if (this.IsHorizontal)
                output = cString + "Y = " + B.ToString("0.00");
            else if (B == 0)
                output = cString + "Y = " + aString + "X";
            else
                output = cString + "Y = " + aString + "X " + (B > 0 ? "+ " : "- ") + Math.Abs(B).ToString("0.00");

            return output;
        }

        #endregion

        #region Distance

        /// <summary>
        /// Retourne la distance minimale entre la Droite et la forme donnée
        /// </summary>
        /// <param name="shape">IForme testée</param>
        /// <returns>Distance minimale</returns>
        public virtual double Distance(IShape shape)
        {
            double output = 0;

            if (shape is RealPoint) output = LineWithRealPoint.Distance(this, shape as RealPoint);
            else if (shape is Segment) output = LineWithSegment.Distance(this, shape as Segment);
            else if (shape is Polygon) output = LineWithPolygon.Distance(this, shape as Polygon);
            else if (shape is Circle) output = LineWithCircle.Distance(this, shape as Circle);
            else if (shape is Line) output = LineWithLine.Distance(this, shape as Line);

            return output;
        }


        #endregion

        #region Contient

        /// <summary>
        /// Teste si la droite courante contient la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Vrai si la droite contient la forme donnée</returns>
        public virtual bool Contains(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = LineWithRealPoint.Contains(this, shape as RealPoint);
            else if (shape is Segment) output = LineWithSegment.Contains(this, shape as Segment);
            else if (shape is Polygon) output = LineWithPolygon.Contains(this, shape as Polygon);
            else if (shape is Circle) output = LineWithCircle.Contains(this, shape as Circle);
            else if (shape is Line) output = LineWithLine.Contains(this, shape as Line);

            return output;
        }

        #endregion

        #region Croisement

        /// <summary>
        /// Retourne la liste des points de croisement avec la forme donnée
        /// </summary>
        /// <param name="shape">Forme à tester</param>
        /// <returns>Liste des points de croisement</returns>
        public virtual List<RealPoint> GetCrossingPoints(IShape shape)
        {
            List<RealPoint> output = new List<RealPoint>();

            if (shape is RealPoint) output = LineWithRealPoint.GetCrossingPoints(this, shape as RealPoint);
            else if (shape is Segment) output = LineWithSegment.GetCrossingPoints(this, shape as Segment);
            else if (shape is Polygon) output = LineWithPolygon.GetCrossingPoints(this, shape as Polygon);
            else if (shape is Circle) output = LineWithCircle.GetCrossingPoints(this, shape as Circle);
            else if (shape is Line) output = LineWithLine.GetCrossingPoints(this, shape as Line);

            return output;
        }

        /// <summary>
        /// Teste si la droite courante croise la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Vrai si droite croise la forme testée</returns>
        public virtual bool Cross(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = LineWithRealPoint.Cross(this, shape as RealPoint);
            else if (shape is Segment) output = LineWithSegment.Cross(this, shape as Segment);
            else if (shape is Polygon) output = LineWithPolygon.Cross(this, shape as Polygon);
            else if (shape is Circle) output = LineWithCircle.Cross(this, shape as Circle);
            else if (shape is Line) output = LineWithLine.Cross(this, shape as Line);

            return output;
        }

        #endregion

        #region Transformations

        /// <summary>
        /// Retourne une ligne qui est translatée des distances données
        /// </summary>
        /// <param name="dx">Distance en X</param>
        /// <param name="dy">Distance en Y</param>
        /// <returns>Ligne translatée des distances données</returns>
        public Line Translation(double dx, double dy)
        {
            return new Line(_a, _b + dx + dy, _c);
        }

        /// <summary>
        /// Retourne une ligne qui est tournée de l'angle donné
        /// </summary>
        /// <param name="angle">Angle de rotation</param>
        /// <param name="rotationCenter">Centre de rotation, si null (0, 0) est utilisé</param>
        /// <returns>Droite tournée de l'angle donné</returns>
        public Line Rotation(AngleDelta angle, RealPoint rotationCenter = null)
        {
            RealPoint p1, p2;

            if (rotationCenter == null) rotationCenter = Barycenter;

            if (_c == 1)
            {
                p1 = new RealPoint(0, _a * 0 + _b);
                p2 = new RealPoint(1, _a * 1 + _b);

                p1 = p1.Rotation(angle, rotationCenter);
                p2 = p2.Rotation(angle, rotationCenter);
            }
            else
            {
                p1 = new RealPoint(_b, 0);
                p2 = new RealPoint(_b, 1);
            }

            return new Line(p1, p2);
        }

        #endregion

        /// <summary>
        /// Retourne l'équation de la droite perpendiculaire à celle-ci et passant par un point donné
        /// </summary>
        /// <param name="point">Point contenu par la perpendiculaire recherchée</param>
        /// <returns>Equation de la droite perpendiculaire à celle-ci et passant par le point donné</returns>
        public Line GetPerpendicular(RealPoint point)
        {
            // Si je suis une droite verticale, je retourne l'horizontale qui passe par le point
            if (C == 0)
                return new Line(0, point.Y);

            // Si je suis une droite horizontale, je retourne la verticale qui passe par le point
            else if (A == 0)
                return new Line(1, -point.X, 0);

            // Si je suis une droite banale, je calcule
            else
            {
                double newA = -1 / _a;
                double newB = -newA * point.X + point.Y;

                return new Line(newA, newB);
            }
        }

        /// <summary>
        /// Retourne la projection hortogonale du point sur la droite.
        /// </summary>
        /// <param name="point">Point à projet sur la droite</param>
        /// <returns>Projection du point sur la droite</returns>
        public RealPoint GetProjection(RealPoint point)
        {
            return GetCrossingPoints(GetPerpendicular(point))[0]; ;
        }

        public bool IsParallel(Line other)
        {
            // Les deux horizontales, les deux verticales, ou la même pente

            return this.IsHorizontal && other.IsHorizontal ||
                    this.IsVertical && other.IsVertical ||
                    (!this.IsHorizontal && !this.IsVertical && !other.IsHorizontal && !other.IsVertical && this.A == other.A);
        }


        #region Peinture

        /// <summary>
        /// Dessine la ligne sur un Graphic
        /// </summary>
        /// <param name="g">Graphic sur lequel dessiner</param>
        /// <param name="outline">Pen utilisé pour dessiner le contour de la ligne</param>
        /// <param name="fill">Brush utilisé pour remplissage de la ligne</param>
        /// <param name="scale">Echelle de conversion</param>
        public virtual void Paint(Graphics g, Pen outline, Brush fill, WorldScale scale)
        {
            // Un peu douteux mais bon
            RealPoint p1, p2;

            if (this.IsVertical)
            {
                p1 = GetCrossingPoints(new Line(new RealPoint(-100000, -100000), new RealPoint(+100000, -100000))).FirstOrDefault();
                p2 = GetCrossingPoints(new Line(new RealPoint(-100000, +100000), new RealPoint(+100000, +100000))).FirstOrDefault();
            }
            else
            {
                p1 = GetCrossingPoints(new Line(new RealPoint(-100000, -100000), new RealPoint(-100000, +100000))).FirstOrDefault();
                p2 = GetCrossingPoints(new Line(new RealPoint(+100000, -100000), new RealPoint(+100000, +100000))).FirstOrDefault();
            }

            if (p1 != null && p2 != null)
                new Segment(p1, p2).Paint(g, outline, fill, scale);
        }

        #endregion
    }
}
<file_sep>/GoBot/GoBot/Movements/Movement.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

using Geometry;
using Geometry.Shapes;
using GoBot.GameElements;
using GoBot.PathFinding;
using GoBot.BoardContext;

namespace GoBot.Movements
{
    public abstract class Movement
    {
        protected DateTime dateMinimum { get; set; }
        protected DateTime startTime { get; set; }
        protected int minimumOpponentDistance { get; set; }

        /// <summary>
        /// Facteur pour la normalisation du cout de l'action affiché à 1 pour la moins chère de toutes
        /// </summary>
        public double DisplayCostFactor { get; set; }

        /// <summary>
        /// Obtient si le mouvement est réalisable (par exemple stock pas plein)
        /// </summary>
        public abstract bool CanExecute { get; }

        /// <summary>
        /// Obtient le score rapporté par l'execution de l'action
        /// </summary>
        public abstract int Score { get; }

        /// <summary>
        /// Obtient la valeur de l'action, c'est à dire l'interet qu'il existe à la réaliser
        /// </summary>
        public abstract double Value { get; }

        /// <summary>
        /// Obtient la liste des positions à laquelle le mouvement est réalisable
        /// </summary>
        public List<Position> Positions { get; protected set; }

        /// <summary>
        /// Obtient l'élément en relation avec le mouvement (optionnel)
        /// </summary>
        public abstract GameElement Element { get; }

        /// <summary>
        /// Obtient le robot qui doit executer ce mouvement
        /// </summary>
        public abstract Robot Robot { get; }

        /// <summary>
        /// Obtient la couleur d'appartenance de l'action (ou blanc)
        /// </summary>
        public abstract Color Color { get; }

        public Movement()
        {
            Positions = new List<Position>();
            DisplayCostFactor = 1;

            dateMinimum = DateTime.Now;
            minimumOpponentDistance = 400;
        }

        /// <summary>
        /// Execute le mouvement en enchainant l'action de début de mouvement, le pathfinding vers la position d'approche puis le mouvement lui même
        /// </summary>
        /// <returns>Retourne vrai si le mouvement a pu s'executer intégralement sans problème</returns>
        public bool Execute()
        {
            Robots.MainRobot.Historique.Log("Début " + this.ToString());

            startTime = DateTime.Now;

            Position position = BestPosition;
            Trajectory traj = null;
            bool ok = true;

            if (position != null)
            {
                bool alreadyOnPosition = (position.Coordinates.Distance(Robot.Position.Coordinates) < 10 && (position.Angle - Robot.Position.Angle) < 2);

                if (!alreadyOnPosition)
                     traj = PathFinder.ChercheTrajectoire(Robot.Graph, GameBoard.ObstaclesAll, GameBoard.ObstaclesOpponents, new Position(Robot.Position), position, Robot.Radius, Robot.Width / 2);

                if (alreadyOnPosition || traj != null)
                {
                    MovementBegin();

                    if (alreadyOnPosition)
                        ok = true;
                    else
                    {
                        ok = Robot.RunTrajectory(traj);
                    }

                    if (ok)
                    {
                        ok = MovementCore();
                        if (ok)
                            Robots.MainRobot.Historique.Log("Fin réussie " + this.ToString() + " en " + (DateTime.Now - startTime).TotalSeconds.ToString("#.#") + "s");
                        else
                            Robots.MainRobot.Historique.Log("Fin ratée " + this.ToString() + " en " + (DateTime.Now - startTime).TotalSeconds.ToString("#.#") + "s");
                    }
                    else
                    {
                        Robots.MainRobot.Historique.Log("Annulation " + this.ToString() + ", trajectoire échouée");
                        ok = false;
                    }

                    MovementEnd();
                }
                else
                {
                    Robots.MainRobot.Historique.Log("Annulation " + this.ToString() + ", trajectoire introuvable");
                    ok = false;
                }
            }
            else
            {
                Robots.MainRobot.Historique.Log("Annulation " + this.ToString() + ", aucune position trouvée");
                ok = false;
            }

            Robots.MainRobot.UpdateGraph(GameBoard.ObstaclesAll);
            return ok;
        }

        public bool ExecuteHere()
        {
            bool ok;
            MovementBegin();
            ok = MovementCore();
            MovementEnd();

            return ok;
        }

        /// <summary>
        /// Représente les actions à effectuer avant de se rendre à la position d'approche du mouvement
        /// </summary>
        protected abstract void MovementBegin();

        /// <summary>
        /// Représente les actions à effectuer une fois arrivé à la position d'approche du mouvement
        /// </summary>
        protected abstract bool MovementCore();

        /// <summary>
        /// Représente les actions à effectuer à la fin du mouvement, qu'il soit réussi ou non
        /// </summary>
        protected abstract void MovementEnd();

        /// <summary>
        /// Retourne vrai si la couleur de l'action correspond à la couleur du robot qui peut la réaliser
        /// </summary>
        /// <returns></returns>
        public bool IsCorrectColor()
        {
            return (Color == null) || (Color == GameBoard.MyColor) || (Color == Color.White);
        }

        /// <summary>
        /// Obtient la meilleure position d'approche pour aborder le mouvement.
        /// Cette position est la plus proche de nous tout en étant à laa distance minimale réglementaire de tout adversaire
        /// </summary>
        public Position BestPosition
        {
            get
            {
                if (Positions.Count < 1)
                    return null;
                if (Positions.Count == 1)
                    return Positions[0];

                double closestDist = double.MaxValue;

                Position closestPoint = Positions[0];

                List<Circle> opponents = new List<IShape>(GameBoard.ObstaclesOpponents).OfType<Circle>().ToList();

                foreach (Position position in Positions)
                {
                    double distancePosition = Robot.Position.Coordinates.Distance(position.Coordinates);
                    double distanceAdv = double.MaxValue;

                    if (opponents.Count > 0)
                    {
                        distanceAdv = opponents.Min(op => position.Coordinates.Distance(op.Center));

                        if (distanceAdv < minimumOpponentDistance)
                            distancePosition = double.PositiveInfinity;
                        else
                            distancePosition -= Math.Sqrt(distanceAdv);
                    }

                    if (distancePosition < closestDist)
                    {
                        closestDist = distancePosition;
                        closestPoint = position;
                    }
                }

                return closestPoint;
            }
        }

        /// <summary>
        /// Coût global de l'action prenant en compte la valeur de l'action mais également le temps de trajectoire pour s'y rendre ou la proximité des adversaires
        /// </summary>
        public double GlobalCost
        {
            get
            {
                if (!IsAvailable || !CanExecute)
                    return double.MaxValue;

                if (Value <= 0 && Positions.Count < 1)
                    return double.MaxValue;

                Position position = BestPosition;

                if (position == null)
                    return double.MaxValue;

                double distance = Math.Max(50, Robot.Position.Coordinates.Distance(position.Coordinates)); // En dessous de 5cm de distance, tout se vaut
                double cout = (Math.Sqrt(distance)) / Value;
                bool adversairePlusProche = false;

                List<IShape> opponents = new List<IShape>(GameBoard.ObstaclesOpponents);
                foreach (Circle c in opponents)
                {
                    double distanceAdv = position.Coordinates.Distance(c.Center);
                    if (distanceAdv < minimumOpponentDistance)
                        cout = double.PositiveInfinity;
                    else
                        cout /= ((distanceAdv / 10) * (distanceAdv / 10));

                    if (distanceAdv < distance)
                        adversairePlusProche = true;
                }

                if (adversairePlusProche)
                    cout *= 2;

                return cout;
            }
        }

        /// <summary>
        /// Peint le mouvement en indiquant les différentes positions d'approche, la meilleure, et l'élément concerné
        /// </summary>
        /// <param name="g">Graphique sur lequel peindre</param>
        /// <param name="scale">Echelle de conversion</param>
        public void Paint(Graphics g, WorldScale scale)
        {
            Font font = new Font("Calibri", 8);

            Pen penRedDot = new Pen(Color.Red);
            penRedDot.DashStyle = DashStyle.Dot;

            Pen penBlackDot = new Pen(Color.Black);
            penBlackDot.DashStyle = DashStyle.Dot;

            Pen penTransparent = new Pen(Color.FromArgb(40, Color.Black));

            Brush brushTransparent = new SolidBrush(Color.FromArgb(40, Color.Black));

            Point point;

            if (Element != null)
            {
                Point pointElement = scale.RealToScreenPosition(Element.Position);

                if (GlobalCost != double.MaxValue && !double.IsInfinity(GlobalCost))
                {
                    Point pointProche = scale.RealToScreenPosition(BestPosition.Coordinates);

                    foreach (Position p in Positions)
                    {
                        point = scale.RealToScreenPosition(p.Coordinates);
                        if (point != pointProche)
                        {
                            g.FillEllipse(Brushes.Red, point.X - 2, point.Y - 2, 4, 4);
                            g.DrawLine(penRedDot, point, pointElement);
                        }
                    }

                    g.FillEllipse(Brushes.White, pointProche.X - 2, pointProche.Y - 2, 4, 4);
                    g.DrawLine(Pens.White, pointProche, pointElement);
                    g.DrawString((GlobalCost / DisplayCostFactor).ToString("0.00"), font, Brushes.White, pointProche);
                }
                else
                {
                    if (!IsCorrectColor())
                    {
                        foreach (Position p in Positions)
                        {
                            point = scale.RealToScreenPosition(p.Coordinates);
                            g.FillEllipse(brushTransparent, point.X - 2, point.Y - 2, 4, 4);
                            g.DrawLine(penTransparent, point, pointElement);
                        }
                    }
                    else
                    {
                        foreach (Position p in Positions)
                        {
                            point = scale.RealToScreenPosition(p.Coordinates);
                            g.FillEllipse(Brushes.Black, point.X - 2, point.Y - 2, 4, 4);
                            g.DrawLine(penBlackDot, point, pointElement);
                        }
                    }
                }
            }

            brushTransparent.Dispose();
            penTransparent.Dispose();
            penBlackDot.Dispose();
            penRedDot.Dispose();
            font.Dispose();
        }

        /// <summary>
        /// Désactive le mouvement pendant une durée determinée.
        /// Le mouvement aura une valeur nulle jusqu'à sa réactivation.
        /// </summary>
        /// <param name="duration">Durée de désactivation du mouvement</param>
        public void Deactivate(TimeSpan duration)
        {
            dateMinimum = DateTime.Now + duration;
        }

        /// <summary>
        /// Réactive le mouvement avant la fin de l'attente de la durée de désactivation
        /// </summary>
        public void Reactivate()
        {
            dateMinimum = DateTime.Now;
        }

        /// <summary>
        /// Retourne vrai si l'action n'est pas désactivée
        /// </summary>
        public Boolean IsAvailable
        {
            get
            {
                // Si il faut attendre avant de faire cette action
                return DateTime.Now >= dateMinimum;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelServoCan.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Collections.Generic;

using GoBot.Threading;
using GoBot.Devices.CAN;
using GoBot.Devices;
using System.Linq;
using System.Reflection;
using GoBot.Actionneurs;

namespace GoBot.IHM
{
    public partial class PanelServoCan : UserControl
    {
        private ThreadLink _linkPolling, _linkDrawing;
        private CanServo _servo;
        private PositionableServo _currentPositionable;

        public Dictionary<String, PropertyInfo> _positionsProp;

        public PanelServoCan()
        {
            InitializeComponent();

            numID.Maximum = Enum.GetValues(typeof(ServomoteurID)).Cast<int>().Max();
        }

        public void SetServo(ServomoteurID servo)
        {
            if (_servo is null || _servo.ID != servo)
            {
                numID.Value = (int)(servo);
                _servo = AllDevices.CanServos[(ServomoteurID)numID.Value];
                picConnection.Visible = false;
                lblName.Text = "";
                ReadValues();

                _currentPositionable = FindPositionnableFromServo(servo);
                SetPositions(_currentPositionable);
            }
        }

        private PositionableServo FindPositionnableFromServo(ServomoteurID servo)
        {
            PropertyInfo[] properties = Config.CurrentConfig.GetType().GetProperties();

            List<PositionableServo> positionnables = properties.Where(o => typeof(PositionableServo).IsAssignableFrom(o.PropertyType)).Select(o => o.GetValue(Config.CurrentConfig, null)).Cast<PositionableServo>().ToList();

            return positionnables.Where(o => o.ID == servo).FirstOrDefault();
        }

        private void SetPositions(Positionable pos)
        {
            cboPositions.Items.Clear();
            btnSavePosition.Enabled = false;

            if (pos != null)
            {
                PropertyInfo[] properties = pos.GetType().GetProperties();

                List<String> noms = new List<string>();
                _positionsProp = new Dictionary<string, PropertyInfo>();

                foreach (PropertyInfo property in properties)
                {
                    if (property.Name != "ID")
                    {
                        noms.Add(Config.PropertyNameToScreen(property) + " : " + property.GetValue(pos, null));
                        _positionsProp.Add(noms[noms.Count - 1], property);
                    }
                }

                cboPositions.Enabled = true;
                cboPositions.Items.AddRange(noms.ToArray());
            }
            else
            {
                cboPositions.Enabled = false;
            }
        }

        private void DrawTimeArrow()
        {
            Bitmap bmp = new Bitmap(picArrow.Width, picArrow.Height);
            Graphics g = Graphics.FromImage(bmp);
            Pen p = new Pen(Color.DimGray, 1);
            g.SmoothingMode = SmoothingMode.AntiAlias;

            p.StartCap = LineCap.Custom;
            p.EndCap = LineCap.Custom;
            p.CustomStartCap = p.CustomEndCap = new AdjustableArrowCap(3, 3);
            g.DrawLine(p, new Point(0, picArrow.Height / 2), new Point(picArrow.Width, picArrow.Height / 2));
            p.Dispose();

            picArrow.Image = bmp;
            lblTrajectoryTime.Visible = true;
        }

        private void trackBarPosition_TickValueChanged(object sender, double value)
        {
            numPosition.Value = (int)value;
        }

        private void boxTorque_ValueChanged(object sender, bool value)
        {
            if (value)
            {
                _linkPolling = ThreadManager.CreateThread(link => GetServoInfos());
                _linkPolling.StartInfiniteLoop(new TimeSpan(0, 0, 0, 0, 20));

                _linkDrawing = ThreadManager.CreateThread(link => DrawTorqueCurve());
                _linkDrawing.StartInfiniteLoop(new TimeSpan(0, 0, 0, 0, 100));
            }
            else
            {
                _linkPolling.Cancel();
                _linkDrawing.Cancel();

                _linkPolling.WaitEnd();
                _linkDrawing.WaitEnd();

                _linkPolling = null;
                _linkDrawing = null;
            }
        }

        private void GetServoInfos()
        {
            _linkPolling.RegisterName(" : " + _servo.ID.ToString());
            gphMonitoringTorque.AddPoint("Couple", _servo.ReadTorqueCurrent(), Color.Firebrick, true);
            gphMonitoringTorque.AddPoint("Alerte", _servo.LastTorqueMax, Color.Red, false);
            gphMonitoringPos.AddPoint("Position", _servo.ReadPosition(), Color.RoyalBlue);
        }

        private void DrawTorqueCurve()
        {
            _linkDrawing.RegisterName(" : " + _servo.ID.ToString());
            this.InvokeAuto(() =>
            {
                gphMonitoringPos.DrawCurves();
                gphMonitoringTorque.DrawCurves();
            });
        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            _servo.SetTrajectory((int)numPosition.Value, (int)numSpeedMax.Value, (int)numAccel.Value);
        }

        private void numPosition_ValueChanged(object sender, EventArgs e)
        {
            _servo.SetPosition((int)numPosition.Value);
        }

        private void numPositionMin_ValueChanged(object sender, EventArgs e)
        {
            int val = (int)numPositionMin.Value;

            if (_servo.LastPositionMin != val)
            {
                _servo.SetPositionMin(val);
                gphMonitoringPos.MinLimit = val;
            }

            trkPosition.Min = val;
        }

        private void numPositionMax_ValueChanged(object sender, EventArgs e)
        {
            int val = (int)numPositionMax.Value;

            if (_servo.LastPositionMax != val)
            {
                _servo.SetPositionMax(val);
                gphMonitoringPos.MaxLimit = val;
            }

            trkPosition.Max = val;
        }

        private void numAccel_ValueChanged(object sender, EventArgs e)
        {
            if (_servo.LastAcceleration != (int)numAccel.Value)
                _servo.SetAcceleration((int)numAccel.Value);
        }

        private void numSpeedMax_ValueChanged(object sender, EventArgs e)
        {
            if (_servo.LastSpeedMax != (int)numSpeedMax.Value)
                _servo.SetSpeedMax((int)numSpeedMax.Value);
        }

        private void numTorqueMax_ValueChanged(object sender, EventArgs e)
        {
            if (_servo.LastTorqueMax != (int)numTorqueMax.Value)
            {
                _servo.SetTorqueMax((int)numTorqueMax.Value);
                gphMonitoringTorque.MaxLimit = _servo.LastTorqueMax * 1.5;
            }
        }

        private void trkTrajectoryTarget_ValueChanged(object sender, double value)
        {
            lblTrajectoryTarget.Text = trkTrajectoryTarget.Value.ToString();
            DrawTrajectoryGraphs();
        }

        private void trkTrajectorySpeed_ValueChanged(object sender, double value)
        {
            lblTrajectorySpeed.Text = trkTrajectorySpeed.Value.ToString();
            DrawTrajectoryGraphs();
        }

        private void trkTrajectoryAccel_ValueChanged(object sender, double value)
        {
            lblTrajectoryAccel.Text = trkTrajectoryAccel.Value.ToString();
            DrawTrajectoryGraphs();
        }

        private void numID_ValueChanged(object sender, EventArgs e)
        {
            SetServo((ServomoteurID)numID.Value);
        }

        private void DrawTrajectoryGraphs()
        {
            SpeedConfig config = new SpeedConfig((int)trkTrajectorySpeed.Value, (int)trkTrajectoryAccel.Value, (int)trkTrajectoryAccel.Value, 0, 0, 0);
            SpeedSample sample = new SpeedSampler(config).SampleLine(_servo.LastPosition, (int)trkTrajectoryTarget.Value, gphTrajectoryPosition.Width);

            gphTrajectorySpeed.DeleteCurve("Vitesse");
            gphTrajectoryPosition.DeleteCurve("Position");

            if (sample.Valid)
            {
                sample.Speeds.ForEach(s => gphTrajectorySpeed.AddPoint("Vitesse", s, Color.Purple));
                gphTrajectorySpeed.DrawCurves();

                sample.Positions.ForEach(s => gphTrajectoryPosition.AddPoint("Position", s, Color.ForestGreen));
                gphTrajectoryPosition.DrawCurves();

                lblTrajectoryTime.Text = sample.Duration.TotalSeconds.ToString("0.0") + "s";
            }

            if (picArrow.Image == null) DrawTimeArrow();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            _servo.DisableOutput();
        }

        private void btnAutoMax_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Lancer une recherche automatique de maximum ?", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                btnAutoMax.Enabled = false;
                ThreadManager.CreateThread(link =>
                {
                    link.Name = "Recherche servo max";
                    _servo.SearchMax();
                    this.InvokeAuto(() => ReadValues());
                    btnAutoMin.InvokeAuto(() => btnAutoMax.Enabled = true);
                }).StartThread();
            }
        }

        private void btnAutoMin_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Lancer une recherche automatique de minimum ?", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                btnAutoMin.Enabled = false;
                ThreadManager.CreateThread(link =>
                {
                    link.Name = "Recherche servo min";
                    _servo.SearchMin();
                    this.InvokeAuto(() => ReadValues());
                    btnAutoMin.InvokeAuto(() => btnAutoMin.Enabled = true);
                }).StartThread();
            }
        }

        private void PanelServoCan_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                gphMonitoringTorque.ScaleMode = Composants.GraphPanel.ScaleType.FixedIfEnough;
                gphMonitoringPos.ScaleMode = Composants.GraphPanel.ScaleType.FixedIfEnough;
            }
        }

        private void btnSauvegarderPosition_Click(object sender, EventArgs e)
        {
            int index = cboPositions.SelectedIndex;

            _positionsProp[(String)cboPositions.SelectedItem].SetValue(_currentPositionable, _servo.ReadPosition(), null);

            SetPositions(_currentPositionable);
            cboPositions.SelectedIndex = index;

            Config.Save();
        }

        private void cboPositions_SelectedIndexChanged(object sender, EventArgs e)
        {
            btnSavePosition.Enabled = true;
        }

        private void ReadValues()
        {
            try
            {
                numPositionMin.Value = _servo.ReadPositionMin();
                numPositionMax.Value = _servo.ReadPositionMax();
                numPosition.Value = _servo.ReadPosition();
                numAccel.Value = _servo.ReadAcceleration();
                numSpeedMax.Value = _servo.ReadSpeedMax();
                numTorqueMax.Value = _servo.ReadTorqueMax();

                trkPosition.Min = _servo.LastPositionMin;
                trkPosition.Max = _servo.LastPositionMax;
                trkPosition.SetValue(_servo.LastPosition, false);

                //trkTrajectoryTarget.Min = _servo.LastPositionMin;
                //trkTrajectoryTarget.Max = _servo.LastPositionMax;
                //trkTrajectoryTarget.SetValue(_servo.LastPosition);
                //trkTrajectorySpeed.SetValue(_servo.LastSpeedMax);
                //trkTrajectoryAccel.SetValue(_servo.LastAcceleration);

                gphMonitoringTorque.MinLimit = 0;
                gphMonitoringTorque.MaxLimit = _servo.LastTorqueMax * 1.5;

                gphMonitoringPos.MinLimit = _servo.LastPositionMin;
                gphMonitoringPos.MaxLimit = _servo.LastPositionMax;

                picConnection.Visible = true;
                picConnection.Image = GoBot.Properties.Resources.ConnectionOk;
                lblName.Text = NameFinder.GetName(_servo.ID);

                grpControl.Enabled = true;
                grpMonitoring.Enabled = true;
                grpTrajectory.Enabled = true;
                grpSettings.Enabled = true;
                grpPositions.Enabled = true;

            }
            catch
            {
                picConnection.Visible = true;
                picConnection.Image = GoBot.Properties.Resources.ConnectionNok;
                lblName.Text = "Pas de connexion";

                grpControl.Enabled = false;
                grpMonitoring.Enabled = false;
                grpTrajectory.Enabled = false;
                grpSettings.Enabled = false;
                grpPositions.Enabled = false;
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/Circle.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using Geometry.Shapes.ShapesInteractions;

namespace Geometry.Shapes
{
    public class Circle : IShape, IShapeModifiable<Circle>
    {
        #region Attributs

        private RealPoint _center;
        private double _radius;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Construit un cercle depuis son centre et son rayon
        /// </summary>
        /// <param name="center">Point central du cercle</param>
        /// <param name="radius">Rayon du cercle</param>
        public Circle(RealPoint center, double radius)
        {
            if (radius < 0) throw new ArgumentException("Radius must be >= 0");

            _center = center;
            _radius = radius;
        }

        /// <summary>
        /// Construit un cercle depuis un autre cercle
        /// </summary>
        /// <param name="circle">cercle à copier</param>
        public Circle(Circle circle)
        {
            _center = circle._center;
            _radius = circle._radius;
        }

        #endregion

        #region Propriétés

        /// <summary>
        /// Obtient le centre du cercle
        /// </summary>
        public RealPoint Center { get { return _center; } }

        /// <summary>
        /// Obtient le rayon du cercle
        /// </summary>
        public double Radius { get { return _radius; } }

        /// <summary>
        /// Obtient la surface du cercle
        /// </summary>
        public double Surface { get { return _radius * _radius * Math.PI; } }

        /// <summary>
        /// Obtient le barycentre du cercle
        /// </summary>
        public RealPoint Barycenter { get { return _center; } }

        #endregion

        #region Opérateurs & Surcharges

        public static bool operator ==(Circle a, Circle b)
        {
            if ((object)a == null || (object)b == null)
                return (object)a == null && (object)b == null;
            else
                return Math.Abs(a.Radius - b.Radius) < RealPoint.PRECISION
                        && a.Center == b.Center;
        }

        public static bool operator !=(Circle a, Circle b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            Circle p = obj as Circle;
            if ((Object)p == null)
            {
                return false;
            }

            return (Circle)obj == this;
        }

        public override int GetHashCode()
        {
            return (int)(_center.GetHashCode()) ^ (int)_radius;
        }

        public override string ToString()
        {
            return "C = " + _center.ToString() + "; R = " + _radius.ToString("0.00");
        }

        #endregion

        #region Distance

        /// <summary>
        /// Retourne la distance minimale entre le cercle courant et la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Distance minimale</returns>
        public double Distance(IShape shape)
        {
            double output = 0;

            if (shape is RealPoint) output = CircleWithRealPoint.Distance(this, shape as RealPoint);
            else if (shape is Segment) output = CircleWithSegment.Distance(this, shape as Segment);
            else if (shape is Polygon) output = CircleWithPolygon.Distance(this, shape as Polygon);
            else if (shape is Circle) output = CircleWithCircle.Distance(this, shape as Circle);
            else if (shape is Line) output = CircleWithLine.Distance(this, shape as Line);

            return output;
        }

        #endregion

        #region Contient

        /// <summary>
        /// Teste si le cercle courant contient la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Vrai si le cercle contient la forme testée</returns>
        public bool Contains(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = CircleWithRealPoint.Contains(this, shape as RealPoint);
            else if (shape is Segment) output = CircleWithSegment.Contains(this, shape as Segment);
            else if (shape is Polygon) output = CircleWithPolygon.Contains(this, shape as Polygon);
            else if (shape is Circle) output = CircleWithCircle.Contains(this, shape as Circle);
            else if (shape is Line) output = CircleWithLine.Contains(this, shape as Line);

            return output;
        }

        #endregion

        #region Croise

        /// <summary>
        /// Retourne la liste des points de croisement avec la forme donnée
        /// </summary>
        /// <param name="shape">Forme à tester</param>
        /// <returns>Liste des points de croisement</returns>
        public List<RealPoint> GetCrossingPoints(IShape shape)
        {
            List<RealPoint> output = new List<RealPoint>();

            if (shape is RealPoint) output = CircleWithRealPoint.GetCrossingPoints(this, shape as RealPoint);
            else if (shape is Segment) output = CircleWithSegment.GetCrossingPoints(this, shape as Segment);
            else if (shape is Polygon) output = CircleWithPolygon.GetCrossingPoints(this, shape as Polygon);
            else if (shape is Circle) output = CircleWithCircle.GetCrossingPoints(this, shape as Circle);
            else if (shape is Line) output = CircleWithLine.GetCrossingPoints(this, shape as Line);

            return output;
        }

        /// <summary>
        /// Teste si le cercle courant croise la forme donnée
        /// </summary>
        /// <param name="shape">Forme testéé</param>
        /// <returns>Vrai si le cercle courant croise la forme donnée</returns>
        public bool Cross(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = CircleWithRealPoint.Cross(this, shape as RealPoint);
            else if (shape is Segment) output = CircleWithSegment.Cross(this, shape as Segment);
            else if (shape is Polygon) output = CircleWithPolygon.Cross(this, shape as Polygon);
            else if (shape is Circle) output = CircleWithCircle.Cross(this, shape as Circle);
            else if (shape is Line) output = CircleWithLine.Cross(this, shape as Line);

            return output;
        }

        #endregion

        #region Transformations

        /// <summary>
        /// Retourne un cercle qui est translaté des distances données
        /// </summary>
        /// <param name="dx">Distance en X</param>
        /// <param name="dy">Distance en Y</param>
        /// <returns>Cercle translaté des distances données</returns>
        public Circle Translation(double dx, double dy)
        {
            return new Circle(_center.Translation(dx, dy), _radius);
        }

        /// <summary>
        /// Retourne un cercle qui est tourné de l'angle donné
        /// </summary>
        /// <param name="angle">Angle de rotation</param>
        /// <param name="rotationCenter">Centre de rotation, si null le barycentre est utilisé</param>
        /// <returns>Cercle tourné de l'angle donné</returns>
        public Circle Rotation(AngleDelta angle, RealPoint rotationCenter = null)
        {
            if (rotationCenter == null) rotationCenter = Barycenter;

            return new Circle(_center.Rotation(angle, rotationCenter), _radius);
        }

        #endregion

        #region Peinture

        /// <summary>
        /// Dessine le cercle sur un Graphic
        /// </summary>
        /// <param name="g">Graphic sur lequel dessiner</param>
        /// <param name="outline">Pen utilisé pour dessiner le contour du cercle</param>
        /// <param name="fill">Brush utilisé pour remplissage du cercle</param>
        /// <param name="scale">Echelle de conversion</param>
        public void Paint(Graphics g, Pen outline, Brush fill, WorldScale scale)
        {
            Point screenPosition = scale.RealToScreenPosition(Center);
            int screenRadius = scale.RealToScreenDistance(Radius);

            if (fill != null)
                g.FillEllipse(fill, new Rectangle(screenPosition.X - screenRadius, screenPosition.Y - screenRadius, screenRadius * 2, screenRadius * 2));

            if (outline != null)
                g.DrawEllipse(outline, new Rectangle(screenPosition.X - screenRadius, screenPosition.Y - screenRadius, screenRadius * 2, screenRadius * 2));

        }

        #endregion
    }
}
<file_sep>/GoBot/GoBot/GameElements/GroundedZone.cs
﻿using Geometry.Shapes;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace GoBot.GameElements
{
    public class GroundedZone : GameElementZone
    {
        private List<Buoy> _buoys;

        public GroundedZone(RealPoint position, Color color, List<Buoy> buoys) : base(position, color, 150)
        {
            _buoys = buoys;
        }

        public List<Buoy> Buoys => _buoys;
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/SegmentWithLine.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class SegmentWithLine
    {
        public static bool Contains(Segment containingSegment, Line containedLine)
        {
            // Un segment fini ne peut pas contenir une droite infinie.
            return false;
        }

        public static bool Cross(Segment segment, Line line)
        {
            return LineWithSegment.Cross(line, segment);
        }

        public static double Distance(Segment segment, Line line)
        {
            return LineWithSegment.Distance(line, segment);
        }

        public static List<RealPoint> GetCrossingPoints(Segment segment, Line line)
        {
            return LineWithSegment.GetCrossingPoints(line, segment);
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/UDP/UdpFrameFactory.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Devices;
using System.Collections.Generic;
using System.Drawing;
using System;

namespace GoBot.Communications.UDP
{
    static class UdpFrameFactory
    {
        static public UdpFrameFunction ExtractFunction(Frame frame)
        {
            return (UdpFrameFunction)frame[1];
        }

        static public Board ExtractBoard(Frame frame)
        {
            return (Board)frame[0];
        }

        static public Board ExtractSender(Frame frame, bool isInput)
        {
            return isInput ? (Board)frame[0] : Board.PC;
        }

        static public Board ExtractReceiver(Frame frame, bool isInput)
        {
            return isInput ? Board.PC : (Board)frame[0];
        }

        private static byte ByteDivide(int valeur, bool poidsFort)
        {
            byte b;
            if (poidsFort)
                b = (byte)(valeur >> 8);
            else
                b = (byte)(valeur & 0x00FF);
            return b;
        }

        static public Frame Debug(Board board, int numDebug)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)board;
            switch(board)
            {
                case Board.RecIO:
                    tab[1] = (byte)UdpFrameFunction.Debug;
                    break;
                case Board.RecMove:
                    tab[1] = (byte)UdpFrameFunction.Debug;
                    break;
            }
            tab[2] = (byte)numDebug;

            return new Frame(tab);
        }

        static public Frame DemandeTensionBatterie(Board board)
        {
            byte[] tab = new byte[2];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.DemandeTension;

            return new Frame(tab);
        }

        static public Frame DemandeCapteurCouleur(Board board, SensorColorID capteur)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.DemandeCapteurCouleur;
            tab[2] = (byte)capteur;

            return new Frame(tab);
        }

        static public Frame DemandeMesureLidar(LidarID lidar)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)Board.RecMove;
            tab[1] = (byte)UdpFrameFunction.DemandeLidar;
            tab[2] = (byte)lidar;

            return new Frame(tab);
        }

        static public Frame ActionneurOnOff(Board board, ActuatorOnOffID actionneur, bool onOff)
        {
            byte[] tab = new byte[4];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.PilotageOnOff;
            tab[2] = (byte)actionneur;
            tab[3] = (byte)(onOff ? 1 : 0);

            return new Frame(tab);
        }

        static public Frame DemandeCapteurOnOff(Board board, SensorOnOffID capteur)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.DemandeCapteurOnOff;
            tab[2] = (byte)capteur;

            return new Frame(tab);
        }

        static public Frame MoteurPosition(Board board, MotorID moteur, int position)
        {
            byte[] tab = new byte[5];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.MoteurPosition;
            tab[2] = (byte)moteur;
            tab[3] = (byte)ByteDivide(position, true);
            tab[4] = (byte)ByteDivide(position, false);

            return new Frame(tab);
        }

        static public Frame MoteurStop(Board board, MotorID moteur, StopMode mode)
        {
            byte[] tab = new byte[4];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.MoteurStop;
            tab[2] = (byte)moteur;
            tab[3] = (byte)mode;

            return new Frame(tab);
        }

        static public Frame MoteurVitesse(Board board, MotorID moteur, SensGD sens, int vitesse)
        {
            byte[] tab = new byte[6];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.MoteurVitesse;
            tab[2] = (byte)moteur;
            tab[3] = (byte)sens;
            tab[4] = (byte)ByteDivide(vitesse, true);
            tab[5] = (byte)ByteDivide(vitesse, false);

            return new Frame(tab);
        }

        static public Frame MoteurAcceleration(Board board, MotorID moteur, int acceleration)
        {
            byte[] tab = new byte[5];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.MoteurAccel;
            tab[2] = (byte)moteur;
            tab[3] = (byte)ByteDivide(acceleration, true);
            tab[4] = (byte)ByteDivide(acceleration, false);

            return new Frame(tab);
        }

        static public Frame MoteurResetPosition(Board board, MotorID moteur)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.MoteurResetPosition;
            tab[2] = (byte)moteur;

            return new Frame(tab);
        }

        static public Frame MoteurOrigin(Board board, MotorID moteur)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.MoteurOrigin;
            tab[2] = (byte)moteur;

            return new Frame(tab);
        }

        static public Frame Deplacer(SensAR sens, int distance, Robot robot)
        {
            byte[] tab = new byte[5];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.Deplace;
            tab[2] = (byte)sens;
            tab[3] = ByteDivide(distance, true);
            tab[4] = ByteDivide(distance, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame OffsetPos(Position pos, Robot robot)
        {
            byte[] tab = new byte[8];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserEnvoiPositionAbsolue;
            tab[2] = ByteDivide((int)pos.Coordinates.X, true);
            tab[3] = ByteDivide((int)pos.Coordinates.X, false);
            tab[4] = ByteDivide((int)pos.Coordinates.Y, true);
            tab[5] = ByteDivide((int)pos.Coordinates.Y, false);
            tab[6] = ByteDivide((int)(pos.Angle.InPositiveDegrees * 100), true);
            tab[7] = ByteDivide((int)(pos.Angle.InPositiveDegrees * 100), false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame Pivot(SensGD sens, AngleDelta angle, Robot robot)
        {
            byte[] tab = new byte[7];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.Pivot;
            tab[2] = (byte)sens;
            tab[3] = ByteDivide((int)(angle * 100.0), true);
            tab[4] = ByteDivide((int)(angle * 100.0), false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame Stop(StopMode mode, Robot robot)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.Stop;
            tab[2] = (byte)mode;

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame DemandePositionContinue(int intervalle, Robot robot)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserIntervalleRetourPosition;
            tab[2] = (byte)(intervalle / 10.0);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame CoeffAsserv(int p, int i, int d, Robot robot)
        {
            byte[] tab = new byte[8];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserPID;
            tab[2] = (byte)ByteDivide(p, true);
            tab[3] = (byte)ByteDivide(p, false);
            tab[4] = (byte)ByteDivide(i, true);
            tab[5] = (byte)ByteDivide(i, false);
            tab[6] = (byte)ByteDivide(d, true);
            tab[7] = (byte)ByteDivide(d, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame CoeffAsservCap(int p, int i, int d, Robot robot)
        {
            byte[] tab = new byte[8];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserPIDCap;
            tab[2] = (byte)ByteDivide(p / 100, true);
            tab[3] = (byte)ByteDivide(p / 100, false);
            tab[4] = (byte)ByteDivide(i, true);
            tab[5] = (byte)ByteDivide(i, false);
            tab[6] = (byte)ByteDivide(d / 100, true);
            tab[7] = (byte)ByteDivide(d / 100, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame CoeffAsservVitesse(int p, int i, int d, Robot robot)
        {
            byte[] tab = new byte[8];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserPID;
            tab[2] = (byte)ByteDivide(p, true);
            tab[3] = (byte)ByteDivide(p, false);
            tab[4] = (byte)ByteDivide(i, true);
            tab[5] = (byte)ByteDivide(i, false);
            tab[6] = (byte)ByteDivide(d, true);
            tab[7] = (byte)ByteDivide(d, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame Virage(SensAR sensAr, SensGD sensGd, int rayon, AngleDelta angle, Robot robot)
        {
            byte[] tab = new byte[8];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.Virage;
            tab[2] = (byte)sensAr;
            tab[3] = (byte)sensGd;
            tab[4] = (byte)ByteDivide(rayon, true);
            tab[5] = (byte)ByteDivide(rayon, false);
            tab[6] = (byte)ByteDivide((int)(angle * 100), true);
            tab[7] = (byte)ByteDivide((int)(angle * 100), false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame TrajectoirePolaire(SensAR sensAr, List<RealPoint> points, Robot robot)
        {
            byte[] tab = new byte[5 + points.Count * 2 * 2];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.TrajectoirePolaire;
            tab[2] = (byte)sensAr;
            tab[3] = (byte)ByteDivide(points.Count, true);
            tab[4] = (byte)ByteDivide(points.Count, false);
            for (int i = 0; i < points.Count; i++)
            {
                tab[5 + i * 4 + 0] = ByteDivide((int)(points[i].X * 10), true);
                tab[5 + i * 4 + 1] = ByteDivide((int)(points[i].X * 10), false);
                tab[5 + i * 4 + 2] = ByteDivide((int)(points[i].Y * 10), true);
                tab[5 + i * 4 + 3] = ByteDivide((int)(points[i].Y * 10), false);
            }

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame DemandePosition(Robot robot)
        {
            byte[] tab = new byte[2];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserDemandePositionXYTeta;

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame VitesseLigne(int vitesse, Robot robot)
        {
            byte[] tab = new byte[4];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserVitesseDeplacement;
            tab[2] = (byte)ByteDivide(vitesse, true);
            tab[3] = (byte)ByteDivide(vitesse, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame AccelLigne(int accelDebut, int accelFin, Robot robot)
        {
            byte[] tab = new byte[6];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserAccelerationDeplacement;
            tab[2] = (byte)ByteDivide(accelDebut, true);
            tab[3] = (byte)ByteDivide(accelDebut, false);
            tab[4] = (byte)ByteDivide(accelFin, true);
            tab[5] = (byte)ByteDivide(accelFin, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame VitessePivot(int vitesse, Robot robot)
        {
            byte[] tab = new byte[4];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserVitessePivot;
            tab[2] = (byte)ByteDivide(vitesse, true);
            tab[3] = (byte)ByteDivide(vitesse, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame AccelPivot(int accel, Robot robot)
        {
            byte[] tab = new byte[4];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserAccelerationPivot;
            tab[2] = (byte)ByteDivide(accel, true);
            tab[3] = (byte)ByteDivide(accel, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame Recallage(SensAR sens, Robot robot)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.Recallage;
            tab[2] = (byte)sens;

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame TestConnexion(Board board)
        {
            byte[] tab = new byte[2];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.TestConnexion;
            return new Frame(tab);
        }

        static public Frame EnvoiConsigneBrute(int consigne, SensAR sens, Robot robot)
        {
            byte[] tab = new byte[5];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserEnvoiConsigneBrutePosition;
            tab[2] = (byte)sens;
            tab[3] = (byte)ByteDivide(consigne, true);
            tab[4] = (byte)ByteDivide(consigne, false);

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame DemandePositionsCodeurs(Robot robot)
        {
            byte[] tab = new byte[2];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.AsserDemandePositionCodeurs;

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame DemandeValeursAnalogiques(Board board)
        {
            byte[] tab = new byte[2];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.DemandeValeursAnalogiques;

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame DemandeValeursNumeriques(Board board)
        {
            byte[] tab = new byte[2];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.DemandeValeursNumeriques;

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame DemandeCpuPwm(Robot robot)
        {
            byte[] tab = new byte[2];
            tab[0] = (byte)robot.AsservBoard;
            tab[1] = (byte)UdpFrameFunction.DemandeChargeCPU_PWM;

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame EnvoyerUart1(Board board, Frame trame)
        {
            byte[] tab = new byte[3 + trame.Length];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.EnvoiUart1;
            tab[2] = (byte)trame.Length;
            for (int i = 0; i < trame.Length; i++)
                tab[3 + i] = trame[i];

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame EnvoyerCAN(Board board, Frame trame)
        {
            byte[] tab = new byte[3 + trame.Length];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.EnvoiCAN;
            tab[2] = (byte)trame.Length;
            for (int i = 0; i < trame.Length; i++)
                tab[3 + i] = trame[i];

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame EnvoyerUart2(Board board, Frame trame)
        {
            byte[] tab = new byte[3 + trame.Length];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.EnvoiUart2;
            tab[2] = (byte)trame.Length;
            for (int i = 0; i < trame.Length; i++)
                tab[3 + i] = trame[i];

            Frame retour = new Frame(tab);
            return retour;
        }

        static public Frame CodeurPosition(Board board, CodeurID codeur)
        {
            byte[] tab = new byte[3];
            tab[0] = (byte)board;
            tab[1] = (byte)UdpFrameFunction.DemandePositionCodeur;
            tab[2] = (byte)codeur;

            Frame retour = new Frame(tab);
            return retour;
        }
        static public Frame DemandeCouleurEquipe()
        {
            byte[] tab = new byte[2];
            tab[0] = (byte)Board.RecMove;
            tab[1] = (byte)UdpFrameFunction.DemandeCouleurEquipe;

            return new Frame(tab);
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/LineWithCircle.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    public static class LineWithCircle
    {
        public static bool Contains(Line containingLine, Circle containedCircle)
        {
            // Une droite contient un cercle si son rayon est nul et qu'il est sur la droite

            return containedCircle.Radius == 0 && containingLine.Contains(containedCircle.Center);
        }

        public static bool Cross(Line line, Circle circle)
        {
            return CircleWithLine.Cross(circle, line);
        }

        public static double Distance(Line line, Circle circle)
        {
            return CircleWithLine.Distance(circle, line);
        }

        public static List<RealPoint> GetCrossingPoints(Line line, Circle circle)
        {
            return CircleWithLine.GetCrossingPoints(circle, line);
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/PolygonWithRealPoint.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class PolygonWithRealPoint
    {
        public static bool Contains(Polygon containingPolygon, RealPoint containedPoint)
        {
            if(containingPolygon is PolygonRectangle)
            {
                // Plus rapide que la méthode des polygones
                return containedPoint.X >= containingPolygon.Points[0].X && containedPoint.X <= containingPolygon.Points[2].X && containedPoint.Y >= containingPolygon.Points[0].Y && containedPoint.Y <= containingPolygon.Points[2].Y;
            }

            // Pour savoir si le Polygone contient un point on trace un segment entre ce point et un point très éloigné
            // On compte combien de cotés du polygone croisent cette droite
            // Si ce nombre est impaire alors le point est contenu dans le polygone

            int crossCount = 0;
            Segment testSeg = new Segment(containedPoint, new RealPoint(containingPolygon.Sides.Min(o => o.StartPoint.X) - 10000, containedPoint.Y));

            foreach (Segment s in containingPolygon.Sides)
            {
                if (s.Contains(containedPoint))
                    return true;

                if (s.Cross(testSeg))
                {
                    List<RealPoint> cross = testSeg.GetCrossingPoints(s);
                    if (cross.Count > 0 && cross[0] != s.EndPoint) // Pour ne pas compter 2 fois un croisement sur un sommet, il sera déjà compté sur le Begin d'un autre
                        crossCount++;
                }
            }

            crossCount -= containingPolygon.Sides.Count(o => Math.Abs(o.StartPoint.Y - containedPoint.Y) < RealPoint.PRECISION);

            return (crossCount % 2 == 1);
        }

        public static bool Cross(Polygon polygon, RealPoint point)
        {
            return RealPointWithPolygon.Cross(point, polygon);
        }

        public static double Distance(Polygon polygon, RealPoint point)
        {
            return RealPointWithPolygon.Distance(point, polygon);
        }

        public static List<RealPoint> GetCrossingPoints(Polygon polygon, RealPoint point)
        {
            return RealPointWithPolygon.GetCrossingPoints(point, polygon);
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/Actionneur.cs
﻿using GoBot.BoardContext;
using GoBot.Devices;
using GoBot.GameElements;
using System;
using System.Collections.Generic;
using System.Drawing;

namespace GoBot.Actionneurs
{
    static class Actionneur
    {
        private static FingerLeft _fingerLeft;
        private static FingerRight _fingerRight;
        private static ElevatorRight _elevatorRight;
        private static ElevatorLeft _elevatorLeft;

        private static Lifter _lifter;

        private static LiftLeft _liftLeft;
        private static LiftRight _liftRight;
        private static LiftBack _liftBack;
        private static Arms _arms;
        private static Flags _flags;

        public static void Init()
        {
            _flags = new Flags();
            _fingerLeft = new FingerLeft();
            _fingerRight = new FingerRight();
            _elevatorRight = new ElevatorRight();
            _elevatorLeft = new ElevatorLeft();
            _lifter = new Lifter();

            _liftLeft = new LiftLeft();
            _liftRight = new LiftRight();
            _liftBack = new LiftBack();
            _arms = new Arms();

            //_elevatorLeft.FillWith(Buoy.Red);
            //_elevatorRight.FillWith(Buoy.Green);

            //_lifter.Load = new List<Color>(){ Buoy.Red, Buoy.Green, Buoy.Red, Buoy.Green, Buoy.Red};
        }

        public static FingerLeft FingerLeft { get => _fingerLeft; }
        public static FingerRight FingerRight { get => _fingerRight; }
        public static ElevatorRight ElevatorRight { get => _elevatorRight; }
        public static ElevatorLeft ElevatorLeft { get => _elevatorLeft; }
        public static Lifter Lifter { get => _lifter; }
        public static LiftLeft LiftLeft { get => _liftLeft; }
        public static LiftRight LiftRight { get => _liftRight; }
        public static LiftBack LiftBack { get => _liftBack; }
        public static Arms Arms { get => _arms; }
        public static Flags Flags { get => _flags; }

        public static Elevator FindElevator(Color color)
        {
            if (color == Buoy.Red)
                return _elevatorLeft;
            else
                return _elevatorRight;
        }

    }
}
<file_sep>/GoBot/GoBot/SpeedConfig.cs
﻿using Geometry;
using GoBot.PathFinding;
using System;
using System.IO;

namespace GoBot
{
    [Serializable]
    public class SpeedConfig
    {
        #region Core

        private int _lineAcceleration;
        private int _lineDeceleration;
        private int _lineSpeed;
        private int _pivotAcceleration;
        private int _pivotDeceleration;
        private int _pivotSpeed;

        #endregion

        #region Constructors

        public SpeedConfig(int lineSpeed, int lineAccel, int lineDecel, int pivotSpeed, int pivotAccel, int pivotDecel)
        {
            SetParams(lineSpeed, lineAccel, lineDecel, pivotSpeed, pivotAccel, pivotDecel);
        }

        public SpeedConfig()
        {
            SetParams(500, 500, 500, 500, 500, 500);
        }

        #endregion

        #region Properties

        public int LineAcceleration
        {
            get { return _lineAcceleration; }
            set
            {
                if (_lineAcceleration != value)
                {
                    _lineAcceleration = value;
                    OnParamChange(true, false, false, false, false, false);
                }
            }
        }

        public int LineDeceleration
        {
            get { return _lineDeceleration; }
            set
            {
                if (_lineDeceleration != value)
                {
                    _lineDeceleration = value;
                    OnParamChange(false, true, false, false, false, false);
                }
            }
        }

        public int LineSpeed
        {
            get { return _lineSpeed; }
            set
            {
                if (_lineSpeed != value)
                {
                    _lineSpeed = value;
                    OnParamChange(false, false, true, false, false, false);
                }
            }
        }

        public int PivotAcceleration
        {
            get { return _pivotAcceleration; }
            set
            {
                if (_pivotAcceleration != value)
                {
                    _pivotAcceleration = value;
                    OnParamChange(false, false, false, true, false, false);
                }
            }
        }

        public int PivotDeceleration
        {
            get { return _pivotDeceleration; }
            set
            {
                if (_pivotDeceleration != value)
                {
                    _pivotDeceleration = value;
                    OnParamChange(false, false, false, false, true, false);
                }
            }
        }

        public int PivotSpeed
        {
            get { return _pivotSpeed; }
            set
            {
                if (_pivotSpeed != value)
                {
                    _pivotSpeed = value;
                    OnParamChange(false, false, false, false, false, true);
                }
            }
        }

        #endregion

        #region Events

        public delegate void ParamChangeDelegate(bool lineAccelChange, bool lineDecelChange, bool lineSpeedChange, bool pivotAccelChange, bool pivotDecelChange, bool pivotSpeedChange);
        public event ParamChangeDelegate ParamChange;

        protected void OnParamChange(bool lineAccelChange, bool lineDecelChange, bool lineSpeedChange, bool pivotAccelChange, bool pivotDecelChange, bool pivotSpeedChange)
        {
            ParamChange?.Invoke(lineAccelChange, lineDecelChange, lineSpeedChange, pivotAccelChange, pivotDecelChange, pivotSpeedChange);
        }

        #endregion

        #region Public

        public TimeSpan LineDuration(int distance)
        {
            TimeSpan accel, maxSpeed, braking;
            return DistanceDuration((int)distance, LineAcceleration, LineSpeed, LineDeceleration, out accel, out maxSpeed, out braking);
        }

        public TimeSpan LineDuration(int distance, out TimeSpan accelDuration, out TimeSpan maxSpeedDuration, out TimeSpan brakingDuration)
        {
            return DistanceDuration((int)distance, LineAcceleration, LineSpeed, LineDeceleration, out accelDuration, out maxSpeedDuration, out brakingDuration);
        }

        public TimeSpan PivotDuration(AngleDelta angle, double axialDistance)
        {
            double dist = (Math.PI * axialDistance) / 360 * Math.Abs(angle.InDegrees);
            TimeSpan accel, maxSpeed, braking;
            return DistanceDuration((int)dist, PivotAcceleration, PivotSpeed, PivotDeceleration, out accel, out maxSpeed, out braking);
        }

        public TimeSpan PivotDuration(AngleDelta angle, double axialDistance, out TimeSpan accelDuration, out TimeSpan maxSpeedDuration, out TimeSpan brakingDuration)
        {
            double dist = (Math.PI * axialDistance) / 360 * Math.Abs(angle.InDegrees);
            return DistanceDuration((int)dist, PivotAcceleration, PivotSpeed, PivotDeceleration, out accelDuration, out maxSpeedDuration, out brakingDuration);
        }

        public void SetParams(int lineSpeed, int lineAccel, int lineDecel, int pivotSpeed, int pivotAccel, int pivotDecel)
        {
            _lineSpeed = lineSpeed;
            _lineAcceleration = lineAccel;
            _lineDeceleration = lineDecel;
            _pivotSpeed = pivotSpeed;
            _pivotAcceleration = pivotAccel;
            _pivotDeceleration = pivotDecel;

            OnParamChange(true, true, true, true, true, true);
        }

        public void SetParams(SpeedConfig config)
        {
            _lineSpeed = config.LineSpeed;
            _lineAcceleration = config.LineAcceleration;
            _lineDeceleration = config.LineDeceleration;
            _pivotSpeed = config.PivotSpeed;
            _pivotAcceleration = config.PivotAcceleration;
            _pivotDeceleration = config.PivotDeceleration;

            OnParamChange(true, true, true, true, true, true);
        }

        #endregion

        #region Private

        private TimeSpan DistanceDuration(int distance, int acceleration, int maxSpeed, int deceleration, out TimeSpan accelDuration, out TimeSpan maxSpeedDuration, out TimeSpan brakingDuration)
        {
            if (distance == 0 || acceleration == 0 || deceleration == 0 || maxSpeed == 0)
            {
                accelDuration = new TimeSpan();
                maxSpeedDuration = new TimeSpan();
                brakingDuration = new TimeSpan();
                return new TimeSpan();
            }
            else
            {

                double durationAccel, durationMaxSpeed, durationBraking;
                double distanceAccel, distanceMaxSpeed, distanceBraking;

                distanceAccel = (maxSpeed * maxSpeed) / (double)(2 * acceleration);
                distanceBraking = (maxSpeed * maxSpeed) / (double)(2 * deceleration);

                if (distanceAccel + distanceBraking < distance)
                {
                    distanceMaxSpeed = distance - distanceAccel - distanceBraking;

                    durationAccel = maxSpeed / (double)acceleration;
                    durationBraking = maxSpeed / (double)deceleration;
                    durationMaxSpeed = distanceMaxSpeed / (double)maxSpeed;
                }
                else
                {
                    distanceMaxSpeed = 0;
                    durationMaxSpeed = 0;

                    double rapport = deceleration / (double)acceleration;
                    distanceBraking = (int)(distance / (rapport + 1));
                    distanceAccel = distance - distanceBraking;

                    durationAccel = Math.Sqrt((2 * distanceAccel) / (double)acceleration);
                    durationBraking = Math.Sqrt((2 * distanceBraking) / (double)(deceleration));
                }

                accelDuration = new TimeSpan(0, 0, 0, 0, (int) (1000 * durationAccel));
                maxSpeedDuration = new TimeSpan(0, 0, 0, 0, (int)(1000 * durationMaxSpeed));
                brakingDuration = new TimeSpan(0, 0, 0, 0, (int)(1000 * durationBraking));

            }

            return accelDuration + maxSpeedDuration + brakingDuration;
        }

        #endregion

    }
}
<file_sep>/GoBot/Composants/ByteBinaryGraph.cs
﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Composants
{
    public partial class ByteBinaryGraph : UserControl
    {
        private List<Label> Labels { get; set; }
        private List<BinaryGraph> Graphs { get; set; }

        public ByteBinaryGraph()
        {
            InitializeComponent();

            Labels = new List<Label>() { label1, label2, label3, label4, label5, label6, label7, label8 };
            Graphs = new List<BinaryGraph>() { binaryGraph1, binaryGraph2, binaryGraph3, binaryGraph4, binaryGraph5, binaryGraph6, binaryGraph7, binaryGraph8 };
        }

        /// <summary>
        /// Permet de définir la liste des nom des graphes
        /// </summary>
        /// <param name="names">Noms des graphes</param>
        public void SetNames(List<String> names)
        {
            for (int i = 0; i < names.Count && i < Labels.Count; i++)
            {
                Labels[i].Text = names[i];
            }
        }

        /// <summary>
        /// Permet d'ajouter une valeur aux 8 graphes sous forme d'un octet fonctionnant comme un masque
        /// </summary>
        /// <param name="value">Masque des valeurs à ajouter aux graphes</param>
        public void SetValue(Byte value)
        {
            for(int i = 0; i < 8; i++)
            {
                if (Labels[i].Text != "-")
                    Graphs[i].AddPoint((value & 0x1) > 0);

                value = (Byte)(value >> 1);
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/Lifts.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace GoBot.Actionneurs
{
    public abstract class Lift
    {
        protected SmallElevator _elevator;
        protected ActuatorOnOffID _makeVacuum, _openVacuum;
        protected SensorOnOffID _pressure;

        public void DoAirLock()
        {
            Robots.MainRobot.SetActuatorOnOffValue(_makeVacuum, true);
            Robots.MainRobot.SetActuatorOnOffValue(_openVacuum, false);
        }

        public void DoAirUnlock()
        {
            Robots.MainRobot.SetActuatorOnOffValue(_makeVacuum, false);
            Robots.MainRobot.SetActuatorOnOffValue(_openVacuum, true);
        }

        public bool HasSomething()
        {
            return Robots.MainRobot.ReadSensorOnOff(_pressure);
        }

        public void DoPositionBottom()
        {
            _elevator.SendPosition(_elevator.PositionBottom);
        }

        public void DoPositionMiddle()
        {
            _elevator.SendPosition(_elevator.PositionMiddle);
        }

        public void DoPositionTop()
        {
            _elevator.SendPosition(_elevator.PositionTop);
        }

        public void DoPositionGrab()
        {
            _elevator.SendPosition(_elevator.PositionGrab);
        }

        public void DoTest()
        {
            for (int i = 0; i < 3; i++)
            {
                DoPositionBottom();
                Thread.Sleep(500);
                DoAirLock();

                Thread.Sleep(500);
                if (HasSomething())
                {
                    DoPositionTop();
                }
                else
                {
                    DoPositionMiddle();
                }

                Thread.Sleep(1000);
                DoAirUnlock();
            }
        }
    }

    public class LiftLeft : Lift
    {
        public LiftLeft()
        {
            _elevator = Config.CurrentConfig.SmallElevatorLeft;
            _makeVacuum = ActuatorOnOffID.MakeVacuumLeft;
            _openVacuum = ActuatorOnOffID.OpenVacuumLeft;
            _pressure = SensorOnOffID.PressureSensorLeft;
        }
    }

    public class LiftRight : Lift
    {
        public LiftRight()
        {
            _elevator = Config.CurrentConfig.SmallElevatorRight;
            _makeVacuum = ActuatorOnOffID.MakeVacuumRight;
            _openVacuum = ActuatorOnOffID.OpenVacuumRight;
            _pressure = SensorOnOffID.PressureSensorRight;
        }
    }

    public class LiftBack : Lift
    {
        protected Selector _selector;
        protected Retractor _retractor;

        public LiftBack()
        {
            _elevator = Config.CurrentConfig.SmallElevatorBack;
            _makeVacuum = ActuatorOnOffID.MakeVacuumBack;
            _openVacuum = ActuatorOnOffID.OpenVacuumBack;
            _pressure = SensorOnOffID.PressureSensorBack;

            _selector = Config.CurrentConfig.Selector;
        }

        public void DoPositionSelectorRight()
        {
            _selector.SendPosition(_selector.PositionRight);
        }

        public void DoPositionSelectorMiddle()
        {
            _selector.SendPosition(_selector.PositionMiddle);
        }

        public void DoPositionSelectorLeft()
        {
            _selector.SendPosition(_selector.PositionLeft);
        }

        public void DoEngage()
        {
            _retractor.SendPosition(_retractor.PositionEngage);
        }

        public void DoDisengage()
        {
            _retractor.SendPosition(_retractor.PositionDisengage);
        }

        public void DoTestBack()
        {
            DoEngage();
            Thread.Sleep(500);
            DoDisengage();
            Thread.Sleep(500);
            DoPositionSelectorRight();
            Thread.Sleep(500);
            DoPositionSelectorMiddle();
            Thread.Sleep(500);
            DoPositionSelectorLeft();
        }
    }
}
<file_sep>/GoBot/GoBot/PathFinding/Tracks.cs
using System;
using System.Collections;
using System.Collections.Generic;


namespace AStarFolder
{
    public class Tracks : IEnumerable<Track>
    {
        private List<Track> _list;
        private IComparer<Track> _comparer;
        
        public Tracks()
        {
            _comparer = new ComparisonCost();
            _list = new List<Track>();
        }
        public Track this[int Index]
        {
            get
            {
                return _list[Index];
            }
        }
        
        public int Add(Track newTrack)
        {
            int index = -1;

            int Index = FindBestPlaceFor(newTrack);
            int NewIndex = Index >= 0 ? Index : -Index - 1;
            if (NewIndex >= Count) _list.Add(newTrack);
            else _list.Insert(NewIndex, newTrack);
            index = NewIndex;

            return index;
        }
        
        protected int FindBestPlaceFor(Track t)
        {
            int place = _list.BinarySearch(t, _comparer);

            while (place > 0 && _list[place - 1].Equals(t)) place--; // We want to point at the FIRST occurence

            return place;
        }
        
        public void Clear()
        {
            _list.Clear();
        }
        
        public void Remove(Track t)
        {
            _list.Remove(t);
        }
        
        public void RemoveAt(int Index) { _list.RemoveAt(Index); }
        
        public int Count { get { return _list.Count; } }
        
        public override string ToString()
        {
            return "Count = " + _list.Count;
        }
        
        public override bool Equals(object o)
        {
            Tracks other = (Tracks)o;

            if (other.Count != Count)
                return false;

            for (int i = 0; i < Count; i++)
                if (!other[i].Equals(this[i]))
                    return false;

            return true;
        }
        
        public override int GetHashCode()
        {
            return _list.GetHashCode();
        }

        public IEnumerator<Track> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        private class ComparisonCost : IComparer<Track>
        {
            public int Compare(Track O1, Track O2)
            {
                IComparable C = O1 as IComparable;
                return O1.Evaluation.CompareTo(O2.Evaluation);
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelSensorsColor.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;

using GoBot.Threading;

namespace GoBot.IHM
{
    public partial class PanelSensorsColor : UserControl
    {
        ThreadLink _linkColorLeft, _linkColorRight;

        public PanelSensorsColor()
        {
            InitializeComponent();
        }

        private void PanelSensorsColor_Load(object sender, EventArgs e)
        {
            picColorLeft.SetColor(Color.DarkRed);
            picColorRight.SetColor(Color.DarkRed);
        }

        private void btnColorLeft_ValueChanged(object sender, bool value)
        {
            if (value)
            {
                Robots.MainRobot.SetActuatorOnOffValue(ActuatorOnOffID.PowerSensorColorBuoyLeft, true);
                Robots.MainRobot.SensorColorChanged += GrosRobot_SensorColorChanged;
                ThreadManager.CreateThread(link => PollingColorLeft(link)).StartInfiniteLoop(50);
            }
            else
            {
                Robots.MainRobot.SetActuatorOnOffValue(ActuatorOnOffID.PowerSensorColorBuoyLeft, false);
                _linkColorLeft.Cancel();
                _linkColorLeft.WaitEnd();
                _linkColorLeft = null;
            }
        }

        private void btnColorRight_ValueChanged(object sender, bool value)
        {
            if (value)
            {
                Robots.MainRobot.SetActuatorOnOffValue(ActuatorOnOffID.PowerSensorColorBuoyRight, true);
                Robots.MainRobot.SensorColorChanged += GrosRobot_SensorColorChanged;
                ThreadManager.CreateThread(link => PollingColorRight(link)).StartInfiniteLoop(50);
            }
            else
            {
                Robots.MainRobot.SetActuatorOnOffValue(ActuatorOnOffID.PowerSensorColorBuoyRight, false);
                _linkColorRight.Cancel();
                _linkColorRight.WaitEnd();
                _linkColorRight = null;
            }
        }

        void PollingColorLeft(ThreadLink link)
        {
            _linkColorLeft = link;
            _linkColorLeft.RegisterName();
            Robots.MainRobot.ReadSensorColor(SensorColorID.BuoyLeft, false);
        }

        void PollingColorRight(ThreadLink link)
        {
            _linkColorRight = link;
            _linkColorRight.RegisterName();
            Robots.MainRobot.ReadSensorColor(SensorColorID.BuoyRight, false);
        }

        void GrosRobot_SensorColorChanged(SensorColorID sensor, Color color)
        {
            this.InvokeAuto(() =>
            {
                switch (sensor)
                {
                    case SensorColorID.BuoyRight:
                        picColorRight.SetColor(color);
                        break;
                    case SensorColorID.BuoyLeft:
                        picColorLeft.SetColor(color);
                        break;
                }
            });
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/Polygon.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using Geometry.Shapes.ShapesInteractions;

namespace Geometry.Shapes
{
    public class Polygon : IShape, IShapeModifiable<Polygon>
    {
        #region Attributs

        /// <summary>
        /// Liste des côtés du Polygone sous forme de segments
        /// La figure est forcément fermée et le dernier point est donc forcément relié au premier
        /// </summary>
        protected List<Segment> _sides;

        protected VolatileResult<RealPoint> _barycenter;
        protected VolatileResult<double> _surface;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Contruit un polygone selon une liste de cotés
        /// Les côtés doivent être donnés dans l'ordre
        /// Si 2 côtés ne se touchent pas ils sont automatiquement reliés par un Segment intermédiaire
        /// Si 2 côtés se croisent une exception ArgumentException est levée
        /// Si le polygone n'est pas fermé le premier et le dernier point sont reliés
        /// </summary>
        /// <param name="sides">Liste des cotés</param>
        public Polygon(IEnumerable<Segment> sides) : this(sides, true)
        {
            // Construit le polygon en forcant la vérification des croisements
        }

        protected Polygon(IEnumerable<Segment> sides, bool checkCrossing) : this()
        {
            BuildPolygon(sides, checkCrossing);
        }

        /// <summary>
        /// Constructeur par défaut utile uniquement pour les héritiés
        /// </summary>
        protected Polygon()
        {
            _barycenter = new VolatileResult<RealPoint>(ComputeBarycenter);
            _surface = new VolatileResult<double>(ComputeSurface);
            _sides = new List<Segment>();
        }

        /// <summary>
        /// Construit un Polygone depuis un autre Polygone
        /// </summary>
        public Polygon(Polygon polygon) : this()
        {
            polygon.Sides.ForEach(s => _sides.Add(new Segment(s)));
        }

        /// <summary>
        /// Construit un polygone selon une liste de points
        /// Si le polygone n'est pas fermé le premier et le dernier point sont reliés
        /// </summary>
        /// <param name="points">Liste des points du polygone dans l'ordre où ils sont reliés</param>
        public Polygon(IEnumerable<RealPoint> points) : this(points, true)
        {
            // Construit le polygon en forcant la vérification des croisements
        }

        /// <summary>
        /// Construit un polygone selon une liste de points
        /// Si le polygone n'est pas fermé le premier et le dernier point sont reliés
        /// </summary>
        /// <param name="points">Liste des points du polygone dans l'ordre où ils sont reliés</param>
        /// <param name="checkCrossing">Vrai pour vérifier le croisement entre les côtés</param>
        protected Polygon(IEnumerable<RealPoint> points, bool checkCrossing) : this()
        {
            List<Segment> segs = new List<Segment>();

            if (points.Count() == 0)
                return;

            for (int i = 1; i < points.Count(); i++)
                segs.Add(new Segment(points.ElementAt(i - 1), points.ElementAt(i)));

            segs.Add(new Segment(points.ElementAt(points.Count() - 1), points.ElementAt(0)));

            BuildPolygon(segs, checkCrossing);
        }

        /// <summary>
        /// Construit le polygone à partir d'une liste de segment définissant son contour
        /// </summary>
        /// <param name="segs">Segments du contour</param>
        /// <param name="crossChecking">Vrai pour vérifier les croisements des côtés</param>
        protected void BuildPolygon(IEnumerable<Segment> segs, bool crossChecking)
        {
            if (segs.Count() == 0)
                return;

            _sides.Clear();

            for (int i = 0; i < segs.Count() - 1; i++)
            {
                Segment seg1 = segs.ElementAt(i);
                Segment seg2 = segs.ElementAt(i + 1);

                _sides.Add(seg1);

                if (seg1.EndPoint != seg2.StartPoint)
                    _sides.Add(new Segment(seg1.EndPoint, seg2.StartPoint));

            }

            _sides.Add(segs.ElementAt(segs.Count() - 1));

            if (crossChecking)
            {
                for (int i = 0; i < _sides.Count; i++)
                    for (int j = i + 1; j < _sides.Count; j++)
                    {
                        List<RealPoint> cross = _sides[i].GetCrossingPoints(_sides[j]);
                        if (cross.Count > 0 && cross[0] != _sides[i].StartPoint && cross[0] != _sides[i].EndPoint)
                            throw new ArgumentException("Impossible to create a polygon with crossing sides.");
                    }
            }
        }

        #endregion

        #region Propriétés

        /// <summary>
        /// Obtient la liste des cotés du polygone
        /// </summary>
        public List<Segment> Sides
        {
            get
            {
                return _sides;
            }
        }

        /// <summary>
        /// Obtient la liste des sommets du polygone
        /// </summary>
        public List<RealPoint> Points
        {
            get
            {
                return _sides.Select(s => new RealPoint(s.StartPoint)).ToList();
            }
        }

        /// <summary>
        /// Obtient la surface du polygone
        /// </summary>
        public double Surface
        {
            get
            {
                return _surface.Value;
            }
        }

        protected virtual double ComputeSurface()
        {
            double surface = 0;

            foreach (PolygonTriangle t in this.ToTriangles())
                surface += t.Surface;

            return surface;
        }

        /// <summary>
        /// Obtient le barycentre du polygone
        /// </summary>
        public RealPoint Barycenter
        {
            get
            {
                return _barycenter.Value;
            }
        }

        protected virtual RealPoint ComputeBarycenter()
        {
            double surface = Surface;
            RealPoint output = null;

            if (this.Surface == 0)
            {
                output = new RealPoint(_sides[0].StartPoint);
            }
            else
            {
                output = new RealPoint();

                foreach (PolygonTriangle t in this.ToTriangles())
                {
                    RealPoint barycentreTriangle = t.Barycenter;
                    double otherSurface = t.Surface;

                    if (t.Surface > 0)
                    {
                        output.X += barycentreTriangle.X * otherSurface / surface;
                        output.Y += barycentreTriangle.Y * otherSurface / surface;
                    }
                }
            }

            return output;
        }

        #endregion

        #region Opérateurs & Surcharges

        public static bool operator ==(Polygon a, Polygon b)
        {
            bool ok;

            if ((object)a == null || (object)b == null)
                ok = (object)a == null && (object)b == null;
            else if (a.Sides.Count == b.Sides.Count)
            {
                ok = a.Points.TrueForAll(p => b.Points.Contains(p));
            }
            else
                ok = false;

            return ok;
        }

        public static bool operator !=(Polygon a, Polygon b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            Polygon p = obj as Polygon;
            if ((Object)p == null)
            {
                return false;
            }

            return (Polygon)obj == this;
        }

        public override int GetHashCode()
        {
            if (_sides.Count == 0)
                return 0;

            int hash = _sides[0].GetHashCode();
            for (int i = 1; i < _sides.Count; i++)
                hash ^= _sides[i].GetHashCode();

            return hash;
        }

        public override string ToString()
        {
            if (_sides.Count == 0)
                return "-";

            String chaine = _sides[0].ToString() + Environment.NewLine;
            for (int i = 1; i < _sides.Count; i++)
                chaine += _sides[i].ToString() + Environment.NewLine;

            return chaine;
        }

        #endregion

        #region Distance

        /// <summary>
        /// Retourne la distance minimum entre le polygone courant et la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Distance minimum entre le polygone et la forme donnée</returns>
        public double Distance(IShape shape)
        {
            double output = 0;

            if (shape is RealPoint) output = PolygonWithRealPoint.Distance(this, shape as RealPoint);
            else if (shape is Segment) output = PolygonWithSegment.Distance(this, shape as Segment);
            else if (shape is Polygon) output = PolygonWithPolygon.Distance(this, shape as Polygon);
            else if (shape is Circle) output = PolygonWithCircle.Distance(this, shape as Circle);
            else if (shape is Line) output = PolygonWithLine.Distance(this, shape as Line);

            return output;
        }

        #endregion

        #region Contient

        /// <summary>
        /// Teste si le polygone courant contient la forme donnée
        /// </summary>
        /// <param name="shape">Forme testé</param>
        /// <returns>Vrai si le polygone contient la forme testée</returns>
        public bool Contains(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = PolygonWithRealPoint.Contains(this, shape as RealPoint);
            else if (shape is Segment) output = PolygonWithSegment.Contains(this, shape as Segment);
            else if (shape is Polygon) output = PolygonWithPolygon.Contains(this, shape as Polygon);
            else if (shape is Circle) output = PolygonWithCircle.Contains(this, shape as Circle);
            else if (shape is Line) output = PolygonWithLine.Contains(this, shape as Line);

            return output;
        }

        #endregion

        #region Croisements

        /// <summary>
        /// Retourne la liste des points de croisement avec la forme donnée
        /// </summary>
        /// <param name="forme">Forme à tester</param>
        /// <returns>Liste des points de croisement</returns>
        public List<RealPoint> GetCrossingPoints(IShape shape)
        {
            List<RealPoint> output = new List<RealPoint>();

            if (shape is RealPoint) output = PolygonWithRealPoint.GetCrossingPoints(this, shape as RealPoint);
            else if (shape is Segment) output = PolygonWithSegment.GetCrossingPoints(this, shape as Segment);
            else if (shape is Polygon) output = PolygonWithPolygon.GetCrossingPoints(this, shape as Polygon);
            else if (shape is Circle) output = PolygonWithCircle.GetCrossingPoints(this, shape as Circle);
            else if (shape is Line) output = PolygonWithLine.GetCrossingPoints(this, shape as Line);

            return output;
        }

        /// <summary>
        /// Teste si le polygone courant croise la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Vrai si le polygone croise la forme donnée</returns>
        public bool Cross(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = PolygonWithRealPoint.Cross(this, shape as RealPoint);
            else if (shape is Segment) output = PolygonWithSegment.Cross(this, shape as Segment);
            else if (shape is Polygon) output = PolygonWithPolygon.Cross(this, shape as Polygon);
            else if (shape is Circle) output = PolygonWithCircle.Cross(this, shape as Circle);
            else if (shape is Line) output = PolygonWithLine.Cross(this, shape as Line);

            return output;
        }

        #endregion

        #region Intersection

        /// <summary>
        /// Sépare un polygone en coupant les segments qui croisent un autre polygone
        /// </summary>
        /// <param name="origin">Le polygone a découper</param>
        /// <param name="cutter">Le polygone qui découpe</param>
        /// <returns>Le polygone d'origine dont les cotés sont coupés</returns>
        private List<Segment> Cut(Polygon origin, Polygon cutter)
        {
            List<Segment> segs = new List<Segment>();

            foreach (Segment seg in origin.Sides)
            {
                List<RealPoint> points = cutter.GetCrossingPoints(seg).OrderBy(p => p.Distance(seg.StartPoint)).ToList();

                if (points.Count != 0)
                {
                    points.Insert(0, seg.StartPoint);
                    points.Add(seg.EndPoint);

                    // Découpage du coté selon les points de croisement
                    for (int i = 0; i < points.Count - 1; i++)
                    {
                        if (points[i] != points[i + 1])
                            segs.Add(new Segment(points[i], points[i + 1]));
                    }
                }
                else
                {
                    // Si aucun croisement, ajout du coté tel quel
                    segs.Add(new Segment(seg.StartPoint, seg.EndPoint));
                }
            }
            return segs;
        }

        /// <summary>
        /// Retourne les polygones représentant l'intersection entre le polygone courant et le polygone donné
        /// </summary>
        /// <param name="other">Polygone avec lequel calculer l'intersection</param>
        /// <returns>Liste des polygones d'intersection</returns>
        public List<Polygon> Intersection(Polygon other)
        {
            List<Segment> segsMe = Cut(this, other);
            List<Segment> segsOther = Cut(other, this);

            // On supprime les segments qui ne sont pas dans les 2 polygones
            for (int i = segsMe.Count - 1; i >= 0; i--)
            {
                if (!other.Contains(segsMe[i]))
                    segsMe.RemoveAt(i);
            }

            for (int i = segsOther.Count - 1; i >= 0; i--)
            {
                if (!this.Contains(segsOther[i]))
                    segsOther.RemoveAt(i);
            }

            List<Segment> segs = new List<Segment>();
            segs.AddRange(segsMe);
            segs.AddRange(segsOther);

            return BuildPolygons(segs);
        }

        /// <summary>
        /// Crée une liste de polygone à partir d'une liste de segment.
        /// On cherche à rejoindre les début et fin de segment pour former les polygones.
        /// </summary>
        /// <param name="inputSegs">Segments à partir desquels contruire les polygones</param>
        /// <returns>Liste de polygones construits</returns>
        private List<Polygon> BuildPolygons(List<Segment> inputSegs)
        {
            List<Segment> currentSegs = new List<Segment>();
            List<Polygon> polygons = new List<Polygon>();

            while (inputSegs.Count != 0)
            {
                currentSegs.Add(inputSegs[0]);
                inputSegs.RemoveAt(0);

                bool polygonOpen = true;

                while (polygonOpen)
                {
                    for (int i = inputSegs.Count - 1; i >= 0; i--)
                    {
                        Segment seg = inputSegs[i];
                        inputSegs.RemoveAt(i);

                        if (currentSegs[currentSegs.Count - 1].EndPoint == seg.StartPoint)
                        {
                            // Le segment est la suite du polygone ...
                            if (currentSegs[0].StartPoint == seg.EndPoint)
                            {
                                // ... et le ferme : on a terminé un polygone
                                currentSegs.Add(seg);
                                polygons.Add(new Polygon(currentSegs, false));
                                currentSegs.Clear();
                                polygonOpen = false;
                                break;
                            }
                            else
                            {
                                // ... on l'ajoute
                                currentSegs.Add(seg);
                            }
                        }
                        else if (currentSegs[currentSegs.Count - 1].EndPoint == seg.EndPoint)
                        {
                            // Le segment à l'envers est la suite du polygone ...
                            if (currentSegs[0].StartPoint == seg.StartPoint)
                            {
                                // ... et le ferme : on le retourne et on a terminé un polygone
                                currentSegs.Add(new Segment(seg.EndPoint, seg.StartPoint));
                                polygons.Add(new Polygon(currentSegs, false));
                                currentSegs.Clear();
                                polygonOpen = false;
                                break;
                            }
                            else
                            {
                                // ... on le retourne et on l'ajoute
                                currentSegs.Add(new Segment(seg.EndPoint, seg.StartPoint));
                            }
                        }
                    }

                }
            }

            return polygons;

        }

        /// <summary>
        /// Calcule la liste des polygones résultant de l'intersection de tous les polygones donnés
        /// </summary>
        /// <param name="polygons">Polygones à intersecter</param>
        /// <returns>Intersections des polygones</returns>
        public static List<Polygon> Intersections(List<Polygon> polygons)
        {
            List<Polygon> currentIntersects = new List<Polygon>();
            List<Polygon> intersects = new List<Polygon>();

            if (polygons.Count >= 2)
            {
                intersects = polygons[0].Intersection(polygons[1]);

                for (int i = 2; i < polygons.Count; i++)
                {
                    currentIntersects.Clear();

                    foreach (Polygon p in intersects)
                        currentIntersects.AddRange(p.Intersection(polygons[i]));

                    intersects.Clear();
                    intersects.AddRange(currentIntersects);
                }
            }

            return intersects;
        }

        #endregion

        #region Transformations

        /// <summary>
        /// Retourne un polygone qui est translaté des distances données
        /// </summary>
        /// <param name="dx">Distance en X</param>
        /// <param name="dy">Distance en Y</param>
        /// <returns>Polygone translaté des distances données</returns>
        public Polygon Translation(double dx, double dy)
        {
            Polygon output = new Polygon(Points.Select(p => p.Translation(dx, dy)), false);

            if (_barycenter.Computed)
                output._barycenter.Value = _barycenter.Value.Translation(dx, dy);

            return output;
        }

        /// <summary>
        /// Retourne un polygone qui est tourné de l'angle donné
        /// </summary>
        /// <param name="angle">Angle de rotation</param>
        /// <param name="rotationCenter">Centre de rotation, si null le barycentre est utilisé</param>
        /// <returns>Polygone tourné de l'angle donné</returns>
        public Polygon Rotation(AngleDelta angle, RealPoint rotationCenter = null)
        {
            if (rotationCenter == null)
                rotationCenter = Barycenter;

            Polygon output = new Polygon(Points.ConvertAll(p => p.Rotation(angle, rotationCenter)), false);

            if (_barycenter.Computed && _barycenter.Value == rotationCenter)
                output._barycenter.Value = new RealPoint(_barycenter.Value);

            return output;
        }

        /// <summary>
        /// Transforme le polygone en liste de triangle qui représentent la même surface.
        /// </summary>
        /// <returns>Liste de triangles équivalente au polygone</returns>
        public List<PolygonTriangle> ToTriangles()
        {
            List<PolygonTriangle> triangles = new List<PolygonTriangle>();
            List<RealPoint> points = new List<RealPoint>(Points);
            RealPoint p1, p2, p3;

            do
            {
                p1 = points[0];
                p2 = points[1];
                p3 = points[2];

                PolygonTriangle triangle = new PolygonTriangle(p1, p2, p3);
                if (this.Contains(triangle.Barycenter))
                {
                    triangles.Add(triangle);
                    points.Add(p1);
                    points.RemoveAt(1);
                    points.RemoveAt(0);
                }
                else
                {
                    points.Add(p1);
                    points.RemoveAt(0);
                }
            } while (points.Count >= 3);

            return triangles;
        }

        #endregion

        #region Peinture

        /// <summary>
        /// Dessine le polygone sur un Graphic
        /// </summary>
        /// <param name="g">Graphic sur lequel dessiner</param>
        /// <param name="outlineColor">Couleur du contour du polygone</param>
        /// <param name="outlineWidth">Epaisseur du contour</param>
        /// <param name="fillColor">Couleur de remplissage du polygone</param>
        /// <param name="scale">Echelle de conversion</param>
        public void Paint(Graphics g, Pen outline, Brush fill, WorldScale scale)
        {
            if (Sides.Count == 0)
                return;

            Point[] listePoints = new Point[Sides.Count + 1];

            listePoints[0] = scale.RealToScreenPosition(Sides[0].StartPoint);

            for (int i = 0; i < Sides.Count; i++)
            {
                Segment s = Sides[i];
                listePoints[i] = scale.RealToScreenPosition(s.EndPoint);
            }

            listePoints[listePoints.Length - 1] = listePoints[0];

            if (fill != null)
                g.FillPolygon(fill, listePoints, System.Drawing.Drawing2D.FillMode.Winding);

            if (outline != null)
                g.DrawPolygon(outline, listePoints);
        }

        #endregion
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageNumeric.cs
﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

using GoBot.Threading;

namespace GoBot.IHM.Pages
{
    public partial class PanelBoardNumeric : UserControl
    {
        private ThreadLink _link;
        private Board _board;

        public PanelBoardNumeric()
        {
            InitializeComponent();

            byteBinaryGraphA1.SetNames(new List<String>() { "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7" });
            byteBinaryGraphA2.SetNames(new List<String>() { "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15" });

            byteBinaryGraphB1.SetNames(new List<String>() { "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7" });
            byteBinaryGraphB2.SetNames(new List<String>() { "B8", "B9", "B10", "B11", "B12", "B13", "B14", "B15" });

            byteBinaryGraphC1.SetNames(new List<String>() { "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7" });
            byteBinaryGraphC2.SetNames(new List<String>() { "C8", "C9", "C10", "C11", "C12", "C13", "C14", "C15" });
        }
        
        private void AskValues()
        {
            Robots.MainRobot.ReadNumericPins(_board, true);

            lock (Robots.MainRobot.NumericPinsValue)
            {
                if (switchButtonPortA.Value)
                {
                    byteBinaryGraphA1.SetValue(Robots.MainRobot.NumericPinsValue[_board][1]);
                    byteBinaryGraphA2.SetValue(Robots.MainRobot.NumericPinsValue[_board][0]);
                }
                if (switchButtonPortB.Value)
                {
                    byteBinaryGraphB1.SetValue(Robots.MainRobot.NumericPinsValue[_board][3]);
                    byteBinaryGraphB2.SetValue(Robots.MainRobot.NumericPinsValue[_board][2]);
                }
                if (switchButtonPortC.Value)
                {
                    byteBinaryGraphC1.SetValue(Robots.MainRobot.NumericPinsValue[_board][5]);
                    byteBinaryGraphC2.SetValue(Robots.MainRobot.NumericPinsValue[_board][4]);
                }
            }
        }

        public void SetBoard(Board board)
        {
            _board = board;

            if (board == Board.RecIO)
            {
                byteBinaryGraphA1.SetNames(new List<String>() { "Moteur 4 I", "Moteur 2 I", "OSC", "A3 (Free)", "Ethernet reset", "-", "-", "Capt. couleur S3" });
                byteBinaryGraphA2.SetNames(new List<String>() { "Capt. couleur led", "Ethernet CS", "Capt. couleur S2", "-", "-", "-", "-", "-" });

                byteBinaryGraphB1.SetNames(new List<String>() { "Moteur 1 I", "Moteur 3 I", "Tension bat.", "Vacuostat 2", "Capt. couleur Out", "PGD - JTAG", "PGJ - JTAG", "Ethernet INT" });
                byteBinaryGraphB2.SetNames(new List<String>() { "Codeur 1B", "Codeur 1A", "Moteur 2 H", "Moteur 2 L", "Moteur 3 H", "Moteur 3 L", "Moteur 4 H", "Moteur 4 L" });

                byteBinaryGraphC1.SetNames(new List<String>() { "Vacuostat 1", "-", "-", "Ethernet SCK", "Ethernet MOSI", "Ethernet MISO", "Moteur 1 H", "Moteur 1 L" });
                byteBinaryGraphC2.SetNames(new List<String>() { "Codeur 2 A", "Codeur 2 B", "-", "-", "-", "-", "-", "-" });
            }
            else if (board == Board.RecMove)
            {
                byteBinaryGraphA1.SetNames(new List<String>() { "Moteur 4 I", "Moteur 2 I", "OSC", "Shunt (OSC2)", "Ethernet reset", "-", "-", "Capt. couleur S3" });
                byteBinaryGraphA2.SetNames(new List<String>() { "Capt. couleur led", "Ethernet CS", "Capt. couleur S2", "-", "-", "-", "-", "-" });

                byteBinaryGraphB1.SetNames(new List<String>() { "Moteur 1 I", "Moteur 3 I", "Jack", "Lidar TX", "Capt. couleur Out", "PGD - JTAG", "PGJ - JTAG", "Ethernet INT" });
                byteBinaryGraphB2.SetNames(new List<String>() { "Codeur 1B", "Codeur 1A", "Moteur 2 H", "Moteur 2 L", "Moteur 3 H", "Moteur 3 L", "Moteur 4 H", "Moteur 4 L" });

                byteBinaryGraphC1.SetNames(new List<String>() { "Lidar RX", "Vacuostat 1", "Vacuostat 2", "Ethernet SCK", "Ethernet MOSI", "Ethernet MISO", "Moteur 1 H", "Moteur 1 L" });
                byteBinaryGraphC2.SetNames(new List<String>() { "Codeur 2 A", "Codeur 2 B", "-", "-", "-", "-", "-", "-" });
            }
        }

        private void switchButtonPort_ValueChanged(object sender, bool value)
        {
            if ((switchButtonPortA.Value || switchButtonPortB.Value || switchButtonPortC.Value) && _link  == null)
            {
                _link = ThreadManager.CreateThread(link => AskValues());
                _link.Name = "Ports numériques " + _board.ToString();
                _link.StartInfiniteLoop(50);
            }

            if (!switchButtonPortA.Value & !switchButtonPortB.Value & !switchButtonPortC.Value & _link != null)
            {
                _link.Cancel();
                _link.WaitEnd();
                _link = null;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Communications/Connections.cs
﻿using GoBot.Communications.CAN;
using GoBot.Communications.UDP;
using GoBot.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;

namespace GoBot.Communications
{
    static class Connections
    {
        private static ThreadLink _linkTestConnections;

        private static Dictionary<CanBoard, CanSubConnection> _connectionsCan;


        public static UDPConnection ConnectionMove { get; private set; }
        public static UDPConnection ConnectionIO { get; private set; }
        public static UDPConnection ConnectionCanBridge { get; private set; }
        public static CanSubConnection ConnectionCanAlim { get; private set; }
        public static Dictionary<CanBoard, CanSubConnection> ConnectionsCan { get { return _connectionsCan; } }

        public static CanConnection ConnectionCan { get; set; }

        /// <summary>
        /// Liste de toutes les connexions suivies par Connections
        /// </summary>
        public static List<Connection> AllConnections { get; set; }

        /// <summary>
        /// Association de la connexion avec la carte UDP
        /// </summary>
        public static Dictionary<Board, Connection> UDPBoardConnection { get; private set; }

        /// <summary>
        /// Activation ou désactivation de la connexion
        /// </summary>
        public static Dictionary<Board, bool> EnableConnection { get; private set; }

        /// <summary>
        /// Intervalle entre les vérifications de connexion
        /// </summary>
        private static int IntervalLoopTests = 1000;


        /// <summary>
        /// Initialise toutes les connexions
        /// </summary>
        public static void Init()
        {
            _connectionsCan = new Dictionary<CanBoard, CanSubConnection>();

            EnableConnection = new Dictionary<Board, bool>();
            UDPBoardConnection = new Dictionary<Board, Connection>();
            AllConnections = new List<Connection>();

            ConnectionIO = AddUDPConnection(Board.RecIO, IPAddress.Parse("10.1.0.14"), 12324, 12314);
            ConnectionIO.Name = Board.RecIO.ToString();

            ConnectionMove = AddUDPConnection(Board.RecMove, IPAddress.Parse("10.1.0.11"), 12321, 12311);
            ConnectionMove.Name = Board.RecMove.ToString();

            ConnectionCanBridge = AddUDPConnection(Board.RecCan, IPAddress.Parse("10.1.0.15"), 12325, 12315);
            ConnectionCanBridge.Name = Board.RecCan.ToString();
            
            ConnectionCan = new CanConnection(Board.RecCan);
            
            _connectionsCan.Add(CanBoard.CanServo1, new CanSubConnection(ConnectionCan, CanBoard.CanServo1));
            _connectionsCan.Add(CanBoard.CanServo2, new CanSubConnection(ConnectionCan, CanBoard.CanServo2));
            _connectionsCan.Add(CanBoard.CanServo3, new CanSubConnection(ConnectionCan, CanBoard.CanServo3));
            _connectionsCan.Add(CanBoard.CanServo4, new CanSubConnection(ConnectionCan, CanBoard.CanServo4));
            _connectionsCan.Add(CanBoard.CanServo5, new CanSubConnection(ConnectionCan, CanBoard.CanServo5));
            _connectionsCan.Add(CanBoard.CanServo6, new CanSubConnection(ConnectionCan, CanBoard.CanServo6));

            ConnectionCanAlim = new CanSubConnection(ConnectionCan, CanBoard.CanAlim);
            _connectionsCan.Add(CanBoard.CanAlim, ConnectionCanAlim);

            _connectionsCan.Values.ToList().ForEach(o => AllConnections.Add(o));

            // En remplacement des tests de connexion des ConnexionCheck, pour les syncroniser
            _linkTestConnections = ThreadManager.CreateThread(link => TestConnections());
            _linkTestConnections.Name = "Tests de connexion";
            _linkTestConnections.StartInfiniteLoop();
        }

        public static void StartConnections()
        {
            ConnectionIO.StartReception();
            ConnectionMove.StartReception();
            ConnectionCan.StartReception();
        }

        /// <summary>
        /// Ajoute une connexion UDP aux connexions suivies
        /// </summary>
        /// <param name="board">Carte associée à la connexion</param>
        /// <param name="ip">Adresse IP de la connexion</param>
        /// <param name="inPort">Port d'entrée pour le PC</param>
        /// <param name="outPort">Port de sortie pour le PC</param>
        /// <returns>La connexion créée</returns>
        private static UDPConnection AddUDPConnection(Board board, IPAddress ip, int inPort, int outPort)
        {
            UDPConnection output = new UDPConnection();
            output.Connect(ip, inPort, outPort);
            UDPBoardConnection.Add(board, output);
            EnableConnection.Add(board, true);
            AllConnections.Add(output);
            output.ConnectionChecker.SendConnectionTest += ConnexionCheck_SendConnectionTestUDP;

            return output;
        }

        /// <summary>
        /// Envoie un teste de connexion UDP à une connexion UDP
        /// </summary>
        /// <param name="sender">Connexion à laquelle envoyer le test</param>
        private static void ConnexionCheck_SendConnectionTestUDP(Connection sender)
        {
            sender.SendMessage(UdpFrameFactory.TestConnexion(GetUDPBoardByConnection(sender)));
        }

        /// <summary>
        /// Boucle de tests de connexions pour maintenir les vérification à intervalle régulier
        /// </summary>
        private static void TestConnections()
        {
            int interval = IntervalLoopTests / AllConnections.Count();

            foreach (Connection conn in AllConnections.OrderBy(c => Connections.GetUDPBoardByConnection(c).ToString()))
            {
                if (!_linkTestConnections.Cancelled)
                {
                    conn.ConnectionChecker.CheckConnection();
                    Thread.Sleep(interval);
                }
            }
        }

        /// <summary>
        /// Retourne la carte concernée par une connexion
        /// </summary>
        /// <param name="conn">Connexion dont on veut la carte</param>
        /// <returns>La carte concernée par la connexion donnée</returns>
        public static Board GetUDPBoardByConnection(Connection conn)
        {
            Board output = Board.RecMove;

            foreach (Board c in Enum.GetValues(typeof(Board)))
            {
                if (UDPBoardConnection.ContainsKey(c) && UDPBoardConnection[c] == conn)
                    output = c;
            }

            return output;
        }

        /// <summary>
        /// Retourne la carte concernée par une connexion
        /// </summary>
        /// <param name="conn">Connexion dont on veut la carte</param>
        /// <returns>La carte concernée par la connexion donnée</returns>
        public static CanBoard GetCANBoardByConnection(Connection conn)
        {
            CanBoard output = CanBoard.CanServo1;

            foreach (CanBoard c in Enum.GetValues(typeof(CanBoard)))
            {
                if (_connectionsCan.ContainsKey(c) && _connectionsCan[c] == conn)
                    output = c;
            }

            return output;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/SegmentWithSegment.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class SegmentWithSegment
    {

        public static bool Contains(Segment containingSegment, Segment containedSegment)
        {
            // Il suffit de vérifier que le segment contient les deux extrémités
            return SegmentWithRealPoint.Contains(containingSegment, containedSegment.StartPoint) && SegmentWithRealPoint.Contains(containingSegment, containedSegment.EndPoint);
        }

        public static bool Cross(Segment segment1, Segment segment2)
        {
            return GetCrossingPoints(segment1, segment2).Count > 0;
        }

        public static double Distance(Segment segment1, Segment segment2)
        {
            // Si les segments se croisent la distance est de 0
            if (Cross(segment1, segment2))
                return 0;

            // Sinon c'est la distance minimale entre (chaque extremité d'un segment) et (l'autre segment)
            double minDistance = double.MaxValue;

            // Le minimal est peut être entre les extremités
            minDistance = Math.Min(minDistance, segment1.StartPoint.Distance(segment2.StartPoint));
            minDistance = Math.Min(minDistance, segment1.StartPoint.Distance(segment2.EndPoint));
            minDistance = Math.Min(minDistance, segment1.EndPoint.Distance(segment2.StartPoint));
            minDistance = Math.Min(minDistance, segment1.EndPoint.Distance(segment2.EndPoint));

            // Le minimal est peut etre entre une extremité et son projeté hortogonal sur l'autre segment
            Line perpendicular = segment1.GetPerpendicular(segment2.StartPoint);
            List<RealPoint> cross = segment1.GetCrossingPoints(perpendicular);
            if (cross.Count > 0) minDistance = Math.Min(minDistance, cross[0].Distance(segment2.StartPoint));

            perpendicular = segment1.GetPerpendicular(segment2.EndPoint);
            cross = segment1.GetCrossingPoints(perpendicular);
            if (cross.Count > 0) minDistance = Math.Min(minDistance, cross[0].Distance(segment2.EndPoint));

            perpendicular = segment2.GetPerpendicular(segment1.StartPoint);
            cross = segment2.GetCrossingPoints(perpendicular);
            if (cross.Count > 0) minDistance = Math.Min(minDistance, cross[0].Distance(segment1.StartPoint));

            perpendicular = segment2.GetPerpendicular(segment1.EndPoint);
            cross = segment2.GetCrossingPoints(perpendicular);
            if (cross.Count > 0) minDistance = Math.Min(minDistance, cross[0].Distance(segment1.EndPoint));

            return minDistance;
        }

        public static List<RealPoint> GetCrossingPoints(Segment segment1, Segment segment2)
        {
            List<RealPoint> output = new List<RealPoint>();

            double x1, x2, x3, x4, y1, y2, y3, y4;

            x1 = segment1.StartPoint.X;
            x2 = segment1.EndPoint.X;
            x3 = segment2.StartPoint.X;
            x4 = segment2.EndPoint.X;

            y1 = segment1.StartPoint.Y;
            y2 = segment1.EndPoint.Y;
            y3 = segment2.StartPoint.Y;
            y4 = segment2.EndPoint.Y;

            double den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

            if (den != 0)
            {
                double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den;
                double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den;

                if (t > 0 && t < 1 && u > 0 && u < 1)
                {
                    output.Add(new RealPoint(x1 + t * (x2 - x1), y1 + t * (y2 - y1)));
                }
            }

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/Asservissement/ActionAccelerationLigne.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionAccelerationLigne : IAction
    {
        private int _accel;
        private int _decel;
        private Robot _robot;

        public ActionAccelerationLigne(Robot r, int accel, int decel)
        {
            _robot = r;
            _accel = accel;
            _decel = decel;
        }

        public override String ToString()
        {
            return _robot.Name + " accélération ligne à " + _accel + " / " + _decel;
        }

        void IAction.Executer()
        {
            _robot.SpeedConfig.LineAcceleration = _accel;
            _robot.SpeedConfig.LineDeceleration = _decel;
        }

        public System.Drawing.Image Image
        {
            get 
            { 
                return GoBot.Properties.Resources.Speed16;  
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Dessinateur.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Threading;
using Geometry.Shapes;
using AStarFolder;
using Geometry;
using GoBot.Beacons;
using GoBot.GameElements;
using GoBot.PathFinding;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using GoBot.Threading;
using GoBot.Devices;
using GoBot.BoardContext;
using GoBot.Actionneurs;

namespace GoBot
{
    static class Dessinateur
    {

        private static ThreadLink _linkDisplay;

        #region Conversion coordonnées réelles / écran

        /// <summary>
        /// Nombre de pixels par mm du terrain
        /// </summary>
        private const double RAPPORT_SCREEN_REAL = (3000.0 / 805);

        /// <summary>
        /// Position en pixel sur l'image de l'abscisse 0 de la table
        /// </summary>
        private const int OFFSET_IMAGE_X = 35;

        /// <summary>
        /// Position en pixel sur l'image de l'ordonnée 0 de la table
        /// </summary>
        private const int OFFSET_IMAGE_Y = 35;

        #endregion

        //Déclaration du délégué pour l’évènement détection de positions
        public delegate void TableDessineeDelegate(Image img);
        //Déclaration de l’évènement utilisant le délégué
        public static event TableDessineeDelegate TableDessinee;

        private static RealPoint positionCurseur;
        public static RealPoint PositionCurseurTable { get; set; }
        public static RealPoint positionDepart;

        public static List<Point> trajectoirePolaireScreen;
        private static List<Point> pointsPolaireScreen;
        public static bool MouseClicked { get; set; }

        public static bool AfficheGraphArretes { get; set; } = false;
        public static bool AfficheGraph { get; set; } = false;
        public static bool AfficheObstacles { get; set; } = false;
        public static bool AfficheTable { get; set; } = true;
        public static bool AfficheTableRelief { get; set; } = false;
        public static bool AfficheHistoriqueCoordonnees { get; set; } = false;
        public static bool AfficheCoutsMouvements { get; set; } = false;
        public static bool AfficheElementsJeu { get; set; } = true;

        public static MouseMode modeCourant;

        private static Pen penRougePointille = new Pen(Color.Red),
                            penNoirPointille = new Pen(Color.Black),
                            penBleuClairPointille = new Pen(Color.LightBlue),

                            penBlancTransparent = new Pen(Color.FromArgb(40, Color.Black)),
                            penBlancFleche = new Pen(Color.White, 3),
                            penCouleurGauche = new Pen(GameBoard.ColorLeftBlue),
                            penCouleurGaucheFleche = new Pen(GameBoard.ColorLeftBlue, 3),
                            penCouleurGaucheEpais = new Pen(Color.FromArgb(35, GameBoard.ColorLeftBlue), 3),
                            penCouleurDroite = new Pen(GameBoard.ColorRightYellow),
                            penCouleurDroiteFleche = new Pen(GameBoard.ColorRightYellow, 3),
                            penCouleurDroiteEpais = new Pen(Color.FromArgb(35, GameBoard.ColorRightYellow), 3),

                            penRougeEpais = new Pen(Color.Red, 3),
                            penBleuEpais = new Pen(GameBoard.ColorRightYellow, 3),
                            penVertEpais = new Pen(Color.Green, 3);

        private static SolidBrush brushNoir = new SolidBrush(Color.Black),
                                    brushNoirTresTransparent = new SolidBrush(Color.FromArgb(35, Color.Black)),
                                    brushCouleurGauche = new SolidBrush(GameBoard.ColorLeftBlue),
                                    brushCouleurGaucheTransparent = new SolidBrush(Color.FromArgb(110, GameBoard.ColorLeftBlue)),
                                    brushCouleurGaucheTresTransparent = new SolidBrush(Color.FromArgb(35, GameBoard.ColorLeftBlue)),
                                    brushCouleurDroite = new SolidBrush(GameBoard.ColorRightYellow),
                                    brushCouleurDroiteTransparent = new SolidBrush(Color.FromArgb(110, GameBoard.ColorRightYellow)),
                                    brushCouleurDroiteTresTransparent = new SolidBrush(Color.FromArgb(35, GameBoard.ColorRightYellow));

        public static WorldScale Scale { get; } = new WorldScale(RAPPORT_SCREEN_REAL, OFFSET_IMAGE_X, OFFSET_IMAGE_Y);

        public static RealPoint PositionCurseur
        {
            get
            {
                return positionCurseur;
            }
            set
            {
                positionCurseur = value;
                PositionCurseurTable = Scale.ScreenToRealPosition(value);
            }
        }
        public static List<RealPoint> TrajectoirePolaire
        {
            set
            {
                trajectoirePolaireScreen = new List<Point>();
                for (int i = 0; i < value.Count; i++)
                    trajectoirePolaireScreen.Add(Scale.RealToScreenPosition(value[i]));
            }
        }

        public static List<RealPoint> PointsPolaire
        {
            set
            {
                pointsPolaireScreen = new List<Point>();
                for (int i = 0; i < value.Count; i++)
                    pointsPolaireScreen.Add(Scale.RealToScreenPosition(value[i]));
            }
        }

        public static void Init()
        {
            penRougePointille.DashStyle = DashStyle.Dot;
            penBleuClairPointille.DashStyle = DashStyle.Dot;
            penNoirPointille.DashStyle = DashStyle.Dot;

            penCouleurGaucheFleche.StartCap = LineCap.Round;
            penCouleurGaucheFleche.EndCap = LineCap.ArrowAnchor;
            penBlancFleche.StartCap = LineCap.Round;
            penBlancFleche.EndCap = LineCap.ArrowAnchor;
        }

        public static void Start()
        {
            PositionCurseur = new RealPoint();

            _linkDisplay = ThreadManager.CreateThread(link => DisplayLoop());
            _linkDisplay.Name = "Affichage de la table";
            _linkDisplay.StartThread();
        }

        public static void Stop()
        {
            _linkDisplay?.Cancel();
            _linkDisplay?.WaitEnd();
            _linkDisplay = null;
        }

        public enum MouseMode
        {
            None,
            PositionCentre,
            PositionFace,
            TeleportCentre,
            TeleportFace,
            TrajectoirePolaire
        }

        public static void DisplayLoop()
        {
            Stopwatch sw = Stopwatch.StartNew();

            while (!_linkDisplay.Cancelled)
            {
                _linkDisplay.LoopsCount++;

                // Limitation à 20FPS
                long sleep = 50 - sw.ElapsedMilliseconds - 1;
                if (sleep > 0)
                    Thread.Sleep((int)sleep);

                sw.Restart();

                try
                {
                    Bitmap bmp;

                    if (AfficheTable)
                        bmp = new Bitmap(Properties.Resources.TablePlan);
                    else
                        bmp = new Bitmap(Properties.Resources.TablePlan.Width, Properties.Resources.TablePlan.Height);

                    {
                        Graphics g = Graphics.FromImage(bmp);
                        g.SmoothingMode = SmoothingMode.AntiAlias;
                        g.SetClip(Scale.RealToScreenRect(new RectangleF(0, 0, 3000, 2000)));
                        g.DrawEllipse(Pens.DimGray, Scale.RealToScreenRect(new RectangleF(1000, -500, 1000, 1000)));
                        g.ResetClip();

                        if (AfficheGraph || AfficheGraphArretes)
                            DessineGraph(Robots.MainRobot, g, AfficheGraph, AfficheGraphArretes);

                        if (AfficheHistoriqueCoordonnees && Robots.MainRobot.PositionsHistorical != null)
                            DessineHistoriqueTrajectoire(Robots.MainRobot, g);

                        if (AfficheObstacles)
                            DessineObstacles(g, Robots.MainRobot);

                        if (true)
                            DrawOpponents(g, GameBoard.ObstaclesOpponents);

                        if (AfficheElementsJeu)
                            DessineElementsJeu(g, GameBoard.Elements);

                        if (Robots.MainRobot != null)
                            DessineRobot(Robots.MainRobot, g);

                        DessinePathFinding(g);

                        DessinePositionEnnemis(g);

                        DessineDetections(g);

                        Robots.MainRobot.PositionTarget?.Paint(g, Color.Red, 5, Color.Red, Scale);

                        if (AfficheCoutsMouvements)
                        {
                            if (GameBoard.Strategy != null && GameBoard.Strategy.Movements != null)
                            {
                                GameBoard.Strategy.Movements.ForEach(mouv => mouv.DisplayCostFactor = GameBoard.Strategy.Movements.Min(m => m.GlobalCost));
                                GameBoard.Strategy.Movements.ForEach(mouv => mouv.Paint(g, Scale));
                            }
                        }

                        if ((modeCourant == MouseMode.PositionCentre || modeCourant == MouseMode.TeleportCentre) && positionDepart != null)
                        {
                            Point positionFin = positionCurseur;

                            Bitmap bmpGrosRobot = new Bitmap(Scale.RealToScreenDistance(Robots.MainRobot.Diameter * 3), Scale.RealToScreenDistance(Robots.MainRobot.Diameter * 3));
                            Graphics gGros = Graphics.FromImage(bmpGrosRobot);
                            gGros.Clear(Color.Transparent);

                            Direction traj = Maths.GetDirection(positionDepart, PositionCurseurTable);

                            Rectangle r = new Rectangle(bmpGrosRobot.Width / 2 - Scale.RealToScreenDistance(Robots.MainRobot.Width / 2),
                                                        bmpGrosRobot.Height / 2 - Scale.RealToScreenDistance(Robots.MainRobot.LenghtFront),
                                                        Scale.RealToScreenDistance(Robots.MainRobot.Width),
                                                        Scale.RealToScreenDistance(Robots.MainRobot.LenghtTotal));

                            gGros.FillRectangle(brushNoirTresTransparent, r);
                            gGros.DrawRectangle(GameBoard.MyColor == GameBoard.ColorLeftBlue ? penCouleurGauche : penCouleurDroite, r);
                            gGros.DrawLine(GameBoard.MyColor == GameBoard.ColorLeftBlue ? penCouleurGauche : penCouleurDroite, bmpGrosRobot.Width / 2, bmpGrosRobot.Height / 2, bmpGrosRobot.Width / 2, bmpGrosRobot.Height / 2 - Scale.RealToScreenDistance(Robots.MainRobot.LenghtFront));

                            Point pointOrigine = Scale.RealToScreenPosition(positionDepart);
                            g.DrawImage(RotateImage(bmpGrosRobot, 360 - traj.angle.InDegrees + 90), pointOrigine.X - bmpGrosRobot.Width / 2, pointOrigine.Y - bmpGrosRobot.Height / 2);

                            g.DrawLine(penBlancFleche, (Point)Scale.RealToScreenPosition(positionDepart), positionFin);
                        }

                        else if ((modeCourant == MouseMode.PositionFace || modeCourant == MouseMode.TeleportFace) && positionDepart != null)
                        {
                            Point positionFin = positionCurseur;

                            Bitmap bmpGrosRobot = new Bitmap(Scale.RealToScreenDistance(Robots.MainRobot.Diameter * 3), Scale.RealToScreenDistance(Robots.MainRobot.Diameter * 3));
                            Graphics gGros = Graphics.FromImage(bmpGrosRobot);
                            gGros.Clear(Color.Transparent);

                            Direction traj = Maths.GetDirection(positionDepart, PositionCurseurTable);

                            Point pointOrigine = Scale.RealToScreenPosition(positionDepart);
                            Position departRecule = new Position(-traj.angle, pointOrigine);
                            departRecule.Move(Scale.RealToScreenDistance(-Robots.MainRobot.LenghtFront));

                            Rectangle r = new Rectangle(bmpGrosRobot.Width / 2 - Scale.RealToScreenDistance(Robots.MainRobot.Width / 2),
                                                        bmpGrosRobot.Height / 2 - Scale.RealToScreenDistance(Robots.MainRobot.LenghtFront),
                                                        Scale.RealToScreenDistance(Robots.MainRobot.Width),
                                                        Scale.RealToScreenDistance(Robots.MainRobot.LenghtTotal));

                            gGros.FillRectangle(brushNoirTresTransparent, r);
                            gGros.DrawRectangle(GameBoard.MyColor == GameBoard.ColorLeftBlue ? penCouleurGauche : penCouleurDroite, r);
                            gGros.DrawLine(GameBoard.MyColor == GameBoard.ColorLeftBlue ? penCouleurGauche : penCouleurDroite, bmpGrosRobot.Width / 2, bmpGrosRobot.Height / 2, bmpGrosRobot.Width / 2, bmpGrosRobot.Height / 2 - Scale.RealToScreenDistance(Robots.MainRobot.LenghtFront));

                            g.DrawImage(RotateImage(bmpGrosRobot, 360 - traj.angle.InDegrees + 90), (int)(departRecule.Coordinates.X) - bmpGrosRobot.Width / 2, (int)(departRecule.Coordinates.Y) - bmpGrosRobot.Height / 2);

                            g.DrawLine(penBlancFleche, (Point)Scale.RealToScreenPosition(positionDepart), positionFin);
                        }

                        if (Robots.MainRobot.TrajectoryRunning != null)
                        {
                            Trajectory traj = new Trajectory();
                            traj.AddPoint(Robots.MainRobot.Position.Coordinates);

                            for (int iPoint = 1; iPoint < Robots.MainRobot.TrajectoryRunning.Points.Count; iPoint++)
                                traj.AddPoint(Robots.MainRobot.TrajectoryRunning.Points[iPoint]);

                            traj.Paint(g, Scale);
                        }

                        // Trajectoire polaire

                        //if (modeCourant == Mode.TrajectoirePolaire)
                        {
                            if (trajectoirePolaireScreen != null)
                                foreach (Point p in trajectoirePolaireScreen)
                                    g.FillEllipse(Brushes.Red, p.X - 1, p.Y - 1, 2, 2);
                            if (pointsPolaireScreen != null)
                                foreach (Point p in pointsPolaireScreen)
                                {
                                    g.FillEllipse(Brushes.White, p.X - 3, p.Y - 3, 6, 6);
                                    g.DrawEllipse(Pens.Black, p.X - 3, p.Y - 3, 6, 6);
                                }
                        }

                        TableDessinee?.Invoke(bmp);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Erreur pendant le dessin de la table " + ex.Message);
                }
            }
        }

        private static void DessineElementsJeu(Graphics g, IEnumerable<GameElement> elements)
        {
            foreach (GameElement element in elements)
                element.Paint(g, Scale);
        }

        private static void DessinePositionEnnemis(Graphics g)
        {
            for (int i = 0; i < SuiviBalise.PositionsEnnemies.Count; i++)
            {
                RealPoint p = SuiviBalise.PositionsEnnemies[i];
                Point positionEcran = Scale.RealToScreenPosition(p);

                if (p == null)
                    continue;

                int vitesse = (int)Math.Sqrt(SuiviBalise.VecteursPositionsEnnemies[i].X * SuiviBalise.VecteursPositionsEnnemies[i].X + SuiviBalise.VecteursPositionsEnnemies[i].Y * SuiviBalise.VecteursPositionsEnnemies[i].Y);

                //if (vitesse < 50)
                //    g.DrawImage(Properties.Resources.Stop, positionEcran.X - Properties.Resources.Stop.Width / 2, positionEcran.Y - Properties.Resources.Stop.Height / 2, Properties.Resources.Stop.Width, Properties.Resources.Stop.Height);
                g.FillEllipse(GameBoard.MyColor == GameBoard.ColorRightYellow ? brushCouleurGaucheTransparent : brushCouleurDroiteTransparent, positionEcran.X - Scale.RealToScreenDistance(GameBoard.OpponentRadius), positionEcran.Y - Scale.RealToScreenDistance(GameBoard.OpponentRadius), Scale.RealToScreenDistance(GameBoard.OpponentRadius * 2), Scale.RealToScreenDistance(GameBoard.OpponentRadius * 2));
                g.DrawEllipse(GameBoard.MyColor == GameBoard.ColorRightYellow ? penCouleurGauche : penCouleurDroite, positionEcran.X - Scale.RealToScreenDistance(GameBoard.OpponentRadius), positionEcran.Y - Scale.RealToScreenDistance(GameBoard.OpponentRadius), Scale.RealToScreenDistance(GameBoard.OpponentRadius * 2), Scale.RealToScreenDistance(GameBoard.OpponentRadius * 2));
                g.DrawLine(GameBoard.MyColor == GameBoard.ColorRightYellow ? penCouleurGaucheEpais : penCouleurDroiteEpais, new Point(positionEcran.X - 7, positionEcran.Y - 7), new Point(positionEcran.X + 7, positionEcran.Y + 7));
                g.DrawLine(GameBoard.MyColor == GameBoard.ColorRightYellow ? penCouleurGaucheEpais : penCouleurDroiteEpais, new Point(positionEcran.X - 7, positionEcran.Y + 7), new Point(positionEcran.X + 7, positionEcran.Y - 7));
                g.DrawLine(GameBoard.MyColor == GameBoard.ColorRightYellow ? penCouleurGaucheFleche : penCouleurDroiteFleche, positionEcran.X, positionEcran.Y, positionEcran.X + Scale.RealToScreenDistance(SuiviBalise.VecteursPositionsEnnemies[i].X / 3), positionEcran.Y + Scale.RealToScreenDistance(SuiviBalise.VecteursPositionsEnnemies[i].Y / 3));
                g.DrawString(i + " - " + vitesse.ToString() + "mm/s", new Font("Calibri", 9, FontStyle.Bold), Brushes.White, positionEcran.X, positionEcran.Y);
            }
        }

        private static void DessineDetections(Graphics g)
        {
            List<IShape> detections = GameBoard.Detections;

            if (detections != null)
            {
                foreach (IShape d in detections)
                {
                    d?.Paint(g, Color.DodgerBlue, 1, Color.LightSkyBlue, Scale);
                }
            }
        }

        private static void DessineRobot(Robot robot, Graphics g)
        {
            List<Color> load;
            Point positionRobot = Scale.RealToScreenPosition(robot.Position.Coordinates);

            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.TranslateTransform(positionRobot.X, positionRobot.Y);
            g.RotateTransform((float)(robot.Position.Angle.InDegrees));
            g.TranslateTransform(-positionRobot.X, -positionRobot.Y);

            Rectangle robotRect = new Rectangle(positionRobot.X - Scale.RealToScreenDistance(robot.LenghtBack), positionRobot.Y - Scale.RealToScreenDistance(robot.Width / 2), Scale.RealToScreenDistance(robot.LenghtBack + robot.LenghtFront), Scale.RealToScreenDistance(robot.Width));

            if (Actionneur.Lifter.Loaded)
            {
                load = Actionneur.Lifter.Load;
                int offset = Actionneur.Lifter.Opened ? -55 : 0;

                for (int j = 0; j < load.Count; j++)
                    PaintRobotBuoy(g, robot, new RealPoint(offset - 160, (j - 2) * 75), load[j]);
            }

            Rectangle grabberRect = robotRect;
            grabberRect.Height = (int)((double)robotRect.Height / Properties.Resources.Robot.Height * Properties.Resources.GrabberLeft.Height);
            grabberRect.Y -= (grabberRect.Height - robotRect.Height) / 2;

            if (Actionneur.ElevatorLeft.GrabberOpened)
                g.DrawImage(Properties.Resources.GrabberLeft, grabberRect);
            if (Actionneur.ElevatorRight.GrabberOpened)
                g.DrawImage(Properties.Resources.GrabberRight, grabberRect);

            if (Config.CurrentConfig.IsMiniRobot)
                g.DrawImage(Properties.Resources.RobotMiniClose, robotRect);
            else
                g.DrawImage(Properties.Resources.Robot, robotRect);

            //g.FillRectangle(Brushes.White, robotRect);
            //using (SolidBrush brush = new SolidBrush(Color.FromArgb(50, GameBoard.MyColor)))
            //    g.FillRectangle(brush, robotRect);
            //g.DrawRectangle(GameBoard.MyColor == GameBoard.ColorRightYellow ? penCouleurDroite : penCouleurGauche, robotRect);
            //g.DrawLine(GameBoard.MyColor == GameBoard.ColorRightYellow ? penCouleurDroite : penCouleurGauche, robotRect.Center(), new Point(robotRect.Right, (int)robotRect.Center().Y));

            // TODOEACHYEAR Dessiner ici les actionneurs pour qu'ils prennent l'inclinaison du robot

            load = Actionneur.ElevatorLeft.LoadFirst;
            for (int i = load.Count - 1; i >= 0; i--)
                PaintRobotBuoy(g, robot, new RealPoint(+30 + i * 22, -110), load[i]);

            load = Actionneur.ElevatorLeft.LoadSecond;
            for (int i = load.Count - 1; i >= 0; i--)
                PaintRobotBuoy(g, robot, new RealPoint(-85 + i * 22, -110), load[i]);

            load = Actionneur.ElevatorRight.LoadFirst;
            for (int i = load.Count - 1; i >= 0; i--)
                PaintRobotBuoy(g, robot, new RealPoint(+30 + i * 22, 110), load[i]);

            load = Actionneur.ElevatorRight.LoadSecond;
            for (int i = load.Count - 1; i >= 0; i--)
                PaintRobotBuoy(g, robot, new RealPoint(-85 + i * 22, 110), load[i]);

            PaintRobotBuoy(g, robot, new RealPoint(-86.5, -193.5), Actionneur.FingerLeft.Load);
            PaintRobotBuoy(g, robot, new RealPoint(-86.5, 193.5), Actionneur.FingerRight.Load);

            if (Actionneur.Flags.LeftOpened)
            {
                RectangleF flagRect = new RectangleF((float)robot.Position.Coordinates.X - 20, (float)robot.Position.Coordinates.Y - 140, 53, 70);

                Bitmap img = new Bitmap(Properties.Resources.FlagT2);
                img.RotateFlip(RotateFlipType.Rotate90FlipNone);
                g.DrawImage(img, Scale.RealToScreenRect(flagRect));
                img.Dispose();
            }
            if (Actionneur.Flags.RightOpened)
            {
                RectangleF flagRect = new RectangleF((float)robot.Position.Coordinates.X - 20, (float)robot.Position.Coordinates.Y + 140 - 70, 53, 70);
                g.DrawImage(Properties.Resources.FlagO2, Scale.RealToScreenRect(flagRect));

                Bitmap img = new Bitmap(Properties.Resources.FlagO2);
                img.RotateFlip(RotateFlipType.Rotate90FlipNone);
                g.DrawImage(img, Scale.RealToScreenRect(flagRect));
                img.Dispose();
            }
            if (Actionneur.Lifter.Opened)
            {
                Rectangle lifterRect = robotRect;
                lifterRect.Width = (int)((double)robotRect.Width / Properties.Resources.Robot.Width * Properties.Resources.Lifter.Width);
                lifterRect.X -= (lifterRect.Width - robotRect.Width) / 2;
                g.DrawImage(Properties.Resources.Lifter, lifterRect);
            }

            g.ResetTransform();
        }

        private static void PaintRobotBuoy(Graphics g, Robot robot, RealPoint center, Color c)
        {
            if (c != Color.Transparent) new Circle(new RealPoint(robot.Position.Coordinates.X + center.X, robot.Position.Coordinates.Y + center.Y), 36).Paint(g, Color.Black, 1, c, Scale);
        }

        private static void DessineObstacles(Graphics g, Robot robot)
        {
            g.SetClip(Scale.RealToScreenRect(new RectangleF(-GameBoard.BorderSize, -GameBoard.BorderSize, GameBoard.Width + GameBoard.BorderSize * 2, GameBoard.Height + GameBoard.BorderSize * 2)));

            foreach (IShape forme in GameBoard.ObstaclesAll)
                forme.Paint(g, Color.Red, 5, Color.Transparent, Scale);

            new Circle(robot.Position.Coordinates, robot.Radius).Paint(g, Color.LimeGreen, 2, Color.Transparent, Scale);

            DessineZoneMorte(g);

            g.ResetClip();
        }

        private static void DrawOpponents(Graphics g, IEnumerable<IShape> opponents)
        {
            foreach (IShape shape in opponents)
            {
                if (shape is Circle)
                {
                    Circle pos = (Circle)shape;
                    Rectangle r = Scale.RealToScreenRect(new RectangleF((float)(pos.Center.X - pos.Radius), (float)(pos.Center.Y - pos.Radius), (float)pos.Radius*2, (float)pos.Radius*2));
                    g.DrawImage(Properties.Resources.Skull, r);
                }
            }
        }

        private static void DessineZoneMorte(Graphics g)
        {
            int farAway = 10000;

            if (AllDevices.LidarAvoid != null && AllDevices.LidarAvoid.DeadAngle > 0)
            {
                AnglePosition debutAngleMort = AllDevices.LidarAvoid.Position.Angle + 180 + new AngleDelta(AllDevices.LidarAvoid.DeadAngle / 2);
                AnglePosition finAngleMort = AllDevices.LidarAvoid.Position.Angle + 180 + new AngleDelta(-AllDevices.LidarAvoid.DeadAngle / 2);

                List<Point> points = new List<Point>();
                points.Add(Scale.RealToScreenPosition(AllDevices.LidarAvoid.Position.Coordinates));
                points.Add(Scale.RealToScreenPosition(new Point((int)(AllDevices.LidarAvoid.Position.Coordinates.X + debutAngleMort.Cos * farAway), (int)(AllDevices.LidarAvoid.Position.Coordinates.Y + debutAngleMort.Sin * farAway))));
                points.Add(Scale.RealToScreenPosition(new Point((int)(AllDevices.LidarAvoid.Position.Coordinates.X + finAngleMort.Cos * farAway), (int)(AllDevices.LidarAvoid.Position.Coordinates.Y + finAngleMort.Sin * farAway))));

                g.IntersectClip(new Rectangle(Scale.RealToScreenPosition(new Point(0, 0)), new Size(Scale.RealToScreenDistance(GameBoard.Width), Scale.RealToScreenDistance(GameBoard.Height))));

                g.DrawLine(Pens.Black, points[0], points[1]);
                g.DrawLine(Pens.Black, points[0], points[2]);
                Brush brush = new SolidBrush(Color.FromArgb(80, Color.Black));
                g.FillPolygon(brush, points.ToArray());
                brush.Dispose();

                g.ResetClip();
            }
        }

        private static void DessinePlateau(Graphics g)
        {
            g.DrawImage(Properties.Resources.TablePlan, 0, 0, Properties.Resources.TablePlan.Width, Properties.Resources.TablePlan.Height);
        }

        private static void DessineHistoriqueTrajectoire(Robot robot, Graphics g)
        {
            lock (Robots.MainRobot.PositionsHistorical)
            {
                for (int i = 1; i < Robots.MainRobot.PositionsHistorical.Count; i++)
                {
                    int couleur = (int)(i * 1200 / robot.PositionsHistorical.Count * 255 / 1200);
                    Color pointColor = Color.FromArgb(couleur, couleur, couleur);

                    RealPoint point = robot.PositionsHistorical[i].Coordinates;
                    RealPoint pointPrec = robot.PositionsHistorical[i - 1].Coordinates;

                    new Segment(point, pointPrec).Paint(g, pointColor, 1, Color.Transparent, Scale);
                    point.Paint(g, Color.Black, 3, pointColor, Scale);
                }
            }
        }

        private static void DessineGraph(Robot robot, Graphics g, bool graph, bool arretes)
        {
            // Dessin du graph
            //robot.SemGraph.WaitOne();

            lock (robot.Graph)
            {
                // Dessin des arcs
                if (arretes)
                    foreach (Arc a in robot.Graph.Arcs)
                    {
                        if (a.Passable)
                            new Segment(a.StartNode.Position, a.EndNode.Position).Paint(g, Color.RoyalBlue, 1, Color.Transparent, Scale);
                    }

                if (graph)
                    //Dessin des noeuds
                    foreach (Node n in robot.Graph.Nodes)
                    {
                        n.Position.Paint(g, n.Passable ? Color.Black : Color.RoyalBlue, 3, n.Passable ? Color.RoyalBlue : Color.Transparent, Scale);
                    }
            }

            //robot.SemGraph.Release();
        }

        public static Track CurrentTrack;

        private static void DessinePathFinding(Graphics g)
        {
            if (GoBot.Config.CurrentConfig.AfficheDetailTraj > 0)
            {
                if (CurrentTrack != null)
                {
                    Track track = CurrentTrack;

                    Node n1, n2;
                    n1 = track.EndNode;

                    while (track.Queue != null)
                    {
                        track = track.Queue;
                        n2 = track.EndNode;

                        new Segment(n1.Position, n2.Position).Paint(g, Color.White, 4, Color.Yellow, Scale);

                        if (PathFinder.CheminEnCoursNoeuds != null && PathFinder.CheminEnCoursNoeuds.Contains(n2))
                            break;

                        // g.DrawLine(Pens.Red, Scale.RealToScreenPosition(n1.Position), Scale.RealToScreenPosition(n2.Position));
                        n1 = n2;
                    }
                }

                if (PathFinder.CheminTest != null)
                {
                    PathFinder.CheminTest.Paint(g, Color.White, 4, Color.DodgerBlue, Scale);
                }
                //List<RealPoint> points = null;
                //if (PathFinder.PointsTrouves != null)
                //    points = new List<RealPoint>(PathFinder.PointsTrouves);
                //if (points != null && points.Count > 1)
                //{
                //    for (int i = 1; i < points.Count; i++)
                //    {
                //        g.DrawLine(penVertEpais, Scale.RealToScreenPosition(points[i - 1]), Scale.RealToScreenPosition(points[i - 1]));
                //    }
                //}

                //Arc cheminTest = PathFinder.CheminTest;
                //if (cheminTest != null)
                //    g.DrawLine(penRougeEpais, Scale.RealToScreenPosition(new RealPoint(cheminTest.StartNode.Position.X, cheminTest.StartNode.Position.Y)), Scale.RealToScreenPosition(new RealPoint(cheminTest.EndNode.Position.X, cheminTest.EndNode.Position.Y)));

                //if (robot.ObstacleTeste != null)
                //    DessinerForme(g, Color.Green, 10, robot.ObstacleTeste);

                IShape obstacleProbleme = PathFinder.ObstacleProbleme;
                if (obstacleProbleme != null)
                    obstacleProbleme.Paint(g, Color.Red, 10, Color.Transparent, Scale);

                if (PathFinder.CheminEnCoursNoeudsSimplifyed != null && PathFinder.CheminEnCoursNoeudsSimplifyed.Count > 0)
                {
                    Node n1, n2;
                    n1 = PathFinder.CheminEnCoursNoeudsSimplifyed[0];
                    int i = 1;

                    while (i < PathFinder.CheminEnCoursNoeudsSimplifyed?.Count)
                    {
                        n2 = PathFinder.CheminEnCoursNoeudsSimplifyed[i];
                        new Segment(n1.Position, n2.Position).Paint(g, Color.White, 4, Color.Orange, Scale);
                        i++;
                        n1 = n2;
                    }
                }
                if (PathFinder.CheminEnCoursNoeuds != null && PathFinder.CheminEnCoursNoeuds.Count > 0)
                {
                    Node n1, n2;
                    n1 = PathFinder.CheminEnCoursNoeuds[0];
                    int i = 1;

                    while (i < PathFinder.CheminEnCoursNoeuds.Count)
                    {
                        n2 = PathFinder.CheminEnCoursNoeuds[i];
                        new Segment(n1.Position, n2.Position).Paint(g, Color.White, 4, Color.Green, Scale);
                        i++;
                        n1 = n2;
                    }
                }
            }
        }

        private static Bitmap RotateImage(Bitmap b, AnglePosition angle)
        {
            //create a new empty bitmap to hold rotated image
            Bitmap returnBitmap = new Bitmap(b.Width, b.Height);

            try
            {
                //make a graphics object from the empty bitmap
                Graphics g = Graphics.FromImage(returnBitmap);
                //move rotation point to center of image
                g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
                //rotate
                g.RotateTransform((float)angle);
                //move image back
                g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
                //draw passed in image onto graphics object
                g.DrawImage(b, 0, 0, b.Width, b.Height);
            }
            catch (Exception)
            {
                returnBitmap.Dispose();
                return null;
            }

            return returnBitmap;
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageDiagnosticMove.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

using GoBot.Threading;

namespace GoBot.IHM.Pages
{
    public partial class PageDiagnosticMove : UserControl
    {
        private ThreadLink _linkPolling;
        private ThreadLink _linkDrawing;

        double _cpuAverage;

        public PageDiagnosticMove()
        {
            InitializeComponent();
        }

        private void btnLaunch_Click(object sender, EventArgs e)
        {
            if (_linkPolling == null)
            {
                btnLaunch.Text = "Stopper";
                btnLaunch.Image = Properties.Resources.Pause16;

                _linkPolling = ThreadManager.CreateThread(link => Measure());
                _linkPolling.StartInfiniteLoop(new TimeSpan(0));

                _linkDrawing = ThreadManager.CreateThread(link => DrawMeasures());
                _linkDrawing.StartInfiniteLoop(new TimeSpan(0, 0, 0, 0, 50));
            }
            else
            {
                btnLaunch.Text = "Lancer";
                btnLaunch.Image = Properties.Resources.Play16;

                _linkPolling.Cancel();
                _linkDrawing.Cancel();

                _linkPolling.WaitEnd();
                _linkDrawing.WaitEnd();

                _linkPolling = null;
                _linkDrawing = null;
            }
        }

        private void DrawMeasures()
        {
            this.InvokeAuto(() =>
            {
                lblCpuLoad.Text = (_cpuAverage * 100).ToString("00") + "%";
                gphCpu.DrawCurves();
                gphPwmRight.DrawCurves();
                gphPwmLeft.DrawCurves();
                
                Image img = new Bitmap(Properties.Resources.Vumetre);
                Graphics g = Graphics.FromImage(img);
                g.FillRectangle(new SolidBrush(Color.FromArgb(250, 250, 250)), 0, 0, 12, img.Height - (int)(_cpuAverage * img.Height));
                picVumetre.Image = img;
            });
        }

        private void Measure()
        {
            List<double>[] values = Robots.MainRobot.DiagnosticCpuPwm(30);
            _cpuAverage = values[0].Average();

            int min = Math.Min(values[0].Count, values[1].Count);
            min = Math.Min(min, values[2].Count);

            for (int i = 0; i < min; i++)
            {
                gphCpu.AddPoint("Charge CPU", values[0][i], Color.Green, true);

                gphPwmLeft.AddPoint("PWM Gauche", values[1][i], Color.Blue, true);
                gphPwmRight.AddPoint("PWM Droite", values[2][i], Color.Red, true);
            }
        }

        private void PanelDiagnosticMove_Load(object sender, EventArgs e)
        {
            if(!Execution.DesignMode)
            {
                gphCpu.MaxLimit = 1;
                gphCpu.MinLimit = 0;
                gphCpu.ScaleMode = Composants.GraphPanel.ScaleType.Fixed;
                gphCpu.NamesVisible = true;
                gphCpu.BorderVisible = true;
                gphCpu.BorderColor = Color.FromArgb(100, 100, 100);
                gphCpu.BackColor = Color.FromArgb(250, 250, 250);
                gphCpu.NamesAlignment = ContentAlignment.BottomLeft;

                gphPwmLeft.MaxLimit = 4000;
                gphPwmLeft.MinLimit = -4000;
                gphPwmLeft.ScaleMode = Composants.GraphPanel.ScaleType.Fixed;
                gphPwmLeft.NamesVisible = true;
                gphPwmLeft.BorderVisible = true;
                gphPwmLeft.BorderColor = Color.FromArgb(100, 100, 100);
                gphPwmLeft.BackColor = Color.FromArgb(250, 250, 250);
                gphPwmLeft.NamesAlignment = ContentAlignment.TopLeft;

                gphPwmRight.MaxLimit = 4000;
                gphPwmRight.MinLimit = -4000;
                gphPwmRight.ScaleMode = Composants.GraphPanel.ScaleType.Fixed;
                gphPwmRight.NamesVisible = true;
                gphPwmRight.BorderVisible = true;
                gphPwmRight.BorderColor = Color.FromArgb(100, 100, 100);
                gphPwmRight.BackColor = Color.FromArgb(250, 250, 250);
                gphPwmRight.NamesAlignment = ContentAlignment.BottomLeft;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Config.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using Microsoft.Win32;
using GoBot.Actionneurs;
using System.Reflection;
using GoBot.Communications.UDP;
using GoBot.Communications.CAN;

namespace GoBot
{
    [Serializable]
    public partial class Config
    {
        private static Config config = null;

        public static List<Positionable> Positionnables { get; set; }

        public static void ChargerPositionnables()
        {
            Positionnables = new List<Positionable>();
            PropertyInfo[] proprietes = typeof(Config).GetProperties();
            foreach (PropertyInfo p in proprietes)
            {
                if (p.PropertyType.IsSubclassOf(typeof(Positionable)))
                {
                    if (p.GetValue(Config.CurrentConfig, null) == null)
                        p.SetValue(Config.CurrentConfig, Activator.CreateInstance(p.PropertyType), null);

                    Positionnables.Add((Positionable)(p.GetValue(Config.CurrentConfig, null)));
                }
            }
        }

        public static string PropertyNameToScreen(PropertyInfo property)
        {
            String typeName = property.Name;
            String nom = "";

            foreach (char c in typeName)
            {
                char ch = c;
                if (c <= 'Z' && c>= 'A')
                    nom += " " + (char)(c + 32);
                else
                    nom += c;
            }

            nom = typeName.Substring(0, 1) + nom.Substring(2);

            return nom;
        }

        public Config()
        {
            ConfigLent = new SpeedConfig();
            ConfigRapide = new SpeedConfig();
        }

        public bool IsMiniRobot { get; set; }

        public int AfficheDetailTraj { get; set; }

        // Batteries

        public double BatterieRobotVert { get; set; } // 23
        public double BatterieRobotOrange { get; set; } // 22
        public double BatterieRobotRouge { get; set; } // 21
        public double BatterieRobotCritique { get; set; } // 3

        // Zone camera

        public int CameraXMin { get; set; }
        public int CameraXMax { get; set; }
        public int CameraYMin { get; set; }
        public int CameraYMax { get; set; }

        // Déplacement gros robot
        
        public SpeedConfig ConfigRapide { get; set; }
        public SpeedConfig ConfigLent { get; set; }

        public int GRCoeffP { get; set; } = 60;
        public int GRCoeffI { get; set; } = 0;
        public int GRCoeffD { get; set; } = 400;

        // Offset balises

        public double OffsetBaliseCapteur1 { get; set; }
        public double OffsetBaliseCapteur2 { get; set; }

        // Parametres logs UDP
        
        public SerializableDictionary<UdpFrameFunction, bool> LogsFonctionsIO { get; set; }
        public SerializableDictionary<UdpFrameFunction, bool> LogsFonctionsMove { get; set; }
        public SerializableDictionary<UdpFrameFunction, bool> LogsFonctionsGB { get; set; }
        public SerializableDictionary<UdpFrameFunction, bool> LogsFonctionsCan { get; set; }

        public SerializableDictionary<Board, bool> LogsExpediteurs { get; set; }
        public SerializableDictionary<Board, bool> LogsDestinataires { get; set; }

        // Parametres logs CAN

        public SerializableDictionary<CanFrameFunction, bool> LogsCanFunctions { get; set; }
        public SerializableDictionary<CanBoard, bool> LogsCanSenders { get; set; }
        public SerializableDictionary<CanBoard, bool> LogsCanReceivers { get; set; }

        public double GetOffsetBalise(int iCapteur)
        {
            if (iCapteur == 1)
            {
                return OffsetBaliseCapteur1;
            }
            else if (iCapteur == 2)
            {
                return OffsetBaliseCapteur2;
            }

            return 0;
        }

        public void SetOffsetBalise(int iCapteur, double offset)
        {
            if (iCapteur == 1)
            {
                OffsetBaliseCapteur1 = offset;
            }
            else if (iCapteur == 2)
            {
                OffsetBaliseCapteur2 = offset;
            }
        }

        public static Config CurrentConfig
        {
            get
            {
                if (config == null)
                    config = new Config();

                return config;
            }
            set
            {
                config = value;
            }
        }

        public static void Load()
        {
            try
            {
                XmlSerializer mySerializer = new XmlSerializer(typeof(Config));
                using (FileStream myFileStream = new FileStream(PathData + "/config.xml", FileMode.Open))
                    CurrentConfig = (Config)mySerializer.Deserialize(myFileStream);

                CurrentConfig.AfficheDetailTraj = 0;
            }
            catch (Exception)
            {
                MessageBox.Show("Aucune configuration chargée.");
            }

            ChargerPositionnables();
        }

        public static void Save()
        {
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "");

            XmlSerializer mySerializer = new XmlSerializer(typeof(Config));
            using (StreamWriter myWriter = new StreamWriter(PathData + "/config.xml"))
                mySerializer.Serialize(myWriter, CurrentConfig, ns);

            File.Copy(PathData + "/config.xml", PathData + "/Configs/config" + Execution.LaunchStartString + ".xml", true);
        }

        public static String PathData
        {
            get
            {
                return (String)Registry.CurrentUser.OpenSubKey("Software").OpenSubKey("GoBot").GetValue("Path");
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Threading/ThreadLink.cs
﻿using System;
using System.Diagnostics;
using System.Threading;

namespace GoBot.Threading
{
    /// <summary>
    /// Lien vers un thread d'execution supervisé. Permet de suivre l'état de son execution et de demander son extinction.
    /// </summary>
    public class ThreadLink
    {
        #region Delegates

        public delegate void CallBack(ThreadLink link);

        #endregion

        #region Fields

        private static int _idCounter = 0;

        private int _id;
        private String _name;
        private DateTime _startDate;
        private DateTime _endDate;

        private bool _started;
        private bool _cancelled;
        private bool _ended;

        private bool _loopPaused;
        private int _loopsCount;
        private int _loopsTarget;

        private Thread _innerThread;
        private CallBack _innerCallback;

        private Semaphore _loopLock;

        #endregion

        #region Properties

        /// <summary>
        /// Numéro d'identification.
        /// </summary>
        public int Id
        {
            get
            {
                return _id;
            }

            private set
            {
                _id = value;
            }
        }

        /// <summary>
        /// Nom de la fonction executée ou appelante.
        /// </summary>
        public string Name
        {
            get
            {
                return _name;
            }

            set
            {
                _name = value;
            }
        }

        /// <summary>
        /// Retourne vrai si le trhead a été lancé.
        /// </summary>
        public bool Started
        {
            get
            {
                return _started;
            }

            private set
            {
                _started = value;
            }
        }

        /// <summary>
        /// Retourne la date de lancement du thread.
        /// </summary>
        public DateTime StartDate
        {
            get
            {
                return _startDate;
            }

            private set
            {
                _startDate = value;
            }
        }

        /// <summary>
        /// Retourne vrai si le thread a terminé son execution.
        /// </summary>
        public bool Ended
        {
            get
            {
                return _ended;
            }

            private set
            {
                _ended = value;
            }
        }

        /// <summary>
        /// Retourne la date de fin d'execution.
        /// </summary>
        public DateTime EndDate
        {
            get
            {
                return _endDate;
            }

            private set
            {
                _endDate = value;
            }
        }

        /// <summary>
        /// Retourne la durée d'execution jusqu'à la date actuelle, ou la date de fin si le thread est déjà terminé.
        /// </summary>
        public TimeSpan Duration
        {
            get
            {
                TimeSpan output;

                if (!Started)
                    output = new TimeSpan();
                else if (!Ended)
                    output = DateTime.Now - StartDate;
                else
                    output = EndDate - StartDate;

                return output;
            }
        }

        /// <summary>
        /// Retourne vrai si le thread est lancé et non terminé.
        /// </summary>
        public bool Running
        {
            get
            {
                return Started && !Cancelled && !Ended;
            }
        }

        /// <summary>
        /// Retourne vrai si l'annulation du thread a été demandée.
        /// </summary>
        public bool Cancelled
        {
            get
            {
                return _cancelled;
            }

            private set
            {
                _cancelled = value;
            }
        }

        /// <summary>
        /// Retourne vrai si la boucle d'execution est en pause.
        /// </summary>
        public bool LoopPaused
        {
            get
            {
                return _loopPaused;
            }

            private set
            {
                _loopPaused = value;
            }
        }

        /// <summary>
        /// Obtient ou définit le nombre d'execution de la boucle de thread qui ont déjà été effectuées.
        /// </summary>
        public int LoopsCount
        {
            get
            {
                return _loopsCount;
            }

            set
            {
                _loopsCount = value;
            }
        }

        /// <summary>
        /// Obtient le nombre d'executions attendues de la boucle (0 si pas de limite)
        /// </summary>
        public int LoopsTarget
        {
            get
            {
                return _loopsTarget;
            }

            private set
            {
                _loopsTarget = value;
            }
        }

        #endregion

        #region Constructors
        
        public ThreadLink(CallBack call)
        {
            _innerCallback = call;
            _id = _idCounter++;
            _name = "Started by " + GetCaller(2);
            
            _cancelled = false;
            _started = false;
            _ended = false;

            _loopLock = null;
            _loopPaused = false;
            _loopsCount = 0;
            _loopsTarget = 0;
            
            _loopLock = null;
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Lance le thread sur une execution unique.
        /// </summary>
        public void StartThread()
        {
            _innerThread = new Thread(f => ThreadCall(_innerCallback));
            _innerThread.IsBackground = true;
            _innerThread.Start();
        }

        /// <summary>
        /// Execute un thread après l'attente spécifiée.
        /// </summary>
        /// <param name="delay">Délai avant l'execution.</param>
        public void StartDelayedThread(TimeSpan delay)
        {
            _innerThread = new Thread(f => ThreadCallDelayed(delay, _innerCallback));
            _innerThread.IsBackground = true;
            _innerThread.Start();
        }

        /// <summary>
        /// Execute un thread après l'attente spécifiée.
        /// </summary>
        /// <param name="delay">Délai avant l'execution.</param>
        public void StartDelayedThread(int delayMs)
        {
            _innerThread = new Thread(f => ThreadCallDelayed(new TimeSpan(0, 0, 0, 0, delayMs), _innerCallback));
            _innerThread.IsBackground = true;
            _innerThread.Start();
        }

        /// <summary>
        /// Lance le thread sur une execution à intervalle régulier pour un certain nombre de fois.
        /// </summary>
        /// <param name="interval">Intervalle entre les executions. (De la fin d'une execution au début de la suivante)</param>
        /// <param name="executions">Nombre total d'executions attendues.</param>
        public void StartLoop(TimeSpan interval, int executions)
        {
            _loopsTarget = executions;
            _innerThread = new Thread(f => ThreadLoop(interval, executions));
            _innerThread.IsBackground = true;
            _innerThread.Start();
        }

        /// <summary>
        /// Lance le thread sur une execution à intervalle régulier pour un certain nombre de fois.
        /// </summary>
        /// <param name="interval">Intervalle entre les executions. (De la fin d'une execution au début de la suivante)</param>
        /// <param name="executions">Nombre total d'executions attendues.</param>
        public void StartLoop(int intervalMs, int executions)
        {
            this.StartLoop(new TimeSpan(0, 0, 0, 0, intervalMs), executions);
        }

        /// <summary>
        /// Lance le thread sur une execution à intervalle régulier pour un nombre illimité de fois.
        /// </summary>
        /// <param name="interval">Intervalle entre les executions. (De la fin d'une execution au début de la suivante)</param>
        public void StartInfiniteLoop(TimeSpan interval)
        {
            _innerThread = new Thread(f => ThreadInfiniteLoop(interval));
            _innerThread.IsBackground = true;
            _innerThread.Start();
        }

        /// <summary>
        /// Lance le thread sur une execution à intervalle régulier pour un nombre illimité de fois.
        /// </summary>
        /// <param name="interval">Intervalle entre les executions. (De la fin d'une execution au début de la suivante)</param>
        public void StartInfiniteLoop(int intervalMs = 0)
        {
            this.StartInfiniteLoop(new TimeSpan(0, 0, 0, 0, intervalMs));
        }

        /// <summary>
        /// Demande l'annulation de l'execution du thread. La fonction appellée doit tester la propriété Cancelled pour interrompre volontairement son execution.
        /// </summary>
        public void Cancel()
        {
            _cancelled = true;
        }

        /// <summary>
        /// Enregistre la classe et le nom de la fonction appellante comme nom du thread.
        /// </summary>
        public void RegisterName(String complement = "")
        {
            _name = GetCaller() + complement;
        }

        /// <summary>
        /// Met en pause la boucle d'execution du thread à la fin de l'itératino actuelle.
        /// </summary>
        public void PauseLoop()
        {
            _loopPaused = true;
            _loopLock = new Semaphore(0, int.MaxValue);
        }

        /// <summary>
        /// Reprends l'execution de la boucle après sa mise en pause.
        /// </summary>
        public void ResumeLoop()
        {
            _loopPaused = false;
            _loopLock?.Release();
        }

        /// <summary>
        /// Attend la fin de l'execution du thread sans bloquer le thread principal.
        /// </summary>
        /// <param name="timeout">Nombre de millisecondes avant l'abandon de l'attente de fin.</param>
        /// <returns>Retourne vrai si le thread s'est terminé avant le timeout.</returns>
        public bool WaitEnd(int timeout = 2000)
        {
            Stopwatch sw = Stopwatch.StartNew();

            // Si jamais la boucle était en pause il faut la libérer pour qu'elle puisse se terminer.
            _loopLock?.Release();

            // Pompage des messages windows parce que si on attend sur le thread principal une invocation du thread principal c'est un deadlock...
            while (_innerThread != null && sw.ElapsedMilliseconds < timeout && !_innerThread.Join(10))
                System.Windows.Forms.Application.DoEvents();

            sw.Stop();

            return sw.ElapsedMilliseconds < timeout;
        }

        /// <summary>
        /// Tente de tuer le thread immédiatement sans précautions. Les appels à Kill doivent être évités au maximum et l'utilisation de Cancel avec gestion de l'annulation doit être privilégiée.
        /// </summary>
        public void Kill()
        {
            this.Cancel();
            _innerThread.Abort();
        }

        #endregion

        #region Private methods

        private void ThreadCall(CallBack call)
        {
            _startDate = DateTime.Now;
            _started = true;

            call.Invoke(this);

            _ended = true;
            _endDate = DateTime.Now;
        }

        private void ThreadCallDelayed(TimeSpan delay, CallBack call)
        {
            _startDate = DateTime.Now;
            _started = true;

            Thread.Sleep(delay);
            
            call.Invoke(this);

            _ended = true;
            _endDate = DateTime.Now;
        }


        private void ThreadLoop(TimeSpan interval, int executions)
        {
            Thread.Sleep(0); // Permet d'executer la fin d'appel de lancement avant de commencer

            _startDate = DateTime.Now;
            _started = true;

            while (!_cancelled && _loopsCount < executions)
            {
                _loopLock?.WaitOne();
                _loopLock = null;

                _loopsCount++;
                _innerCallback.Invoke(this);
                Thread.Sleep(interval);
            }

            _ended = true;
            _endDate = DateTime.Now;
        }

        private void ThreadInfiniteLoop(TimeSpan interval)
        {
            Thread.Sleep(0); // Permet d'executer la fin d'appel de lancement avant de commencer

            _startDate = DateTime.Now;
            _started = true;

            while (!this.Cancelled)
            {
                _loopLock?.WaitOne();
                _loopLock = null;

                _loopsCount++;
                _innerCallback.Invoke(this);

                if(interval.TotalMilliseconds > 15)
                    Thread.Sleep(interval);
                else
                {
                    Stopwatch chrono = Stopwatch.StartNew();
                    while (chrono.Elapsed < interval) ;
                }
            }

            _ended = true;
            _endDate = DateTime.Now;
        }

        private static String GetCaller(int floors = 1)
        {
            StackFrame stack = new StackTrace().GetFrame(1 + floors);

            return stack.GetMethod().DeclaringType.Name + "." + stack.GetMethod().Name;
        }

        #endregion

        #region Type modifications

        public override string ToString()
        {
            String output = "Thread " + _id.ToString() + " \"" + _name + "\"";

            if (Started)
            {
                output += ", started at " + _startDate.ToLongTimeString();
                if (_cancelled)
                    output += ", cancelled";
                else if (_ended)
                    output += ", ended at " + _endDate.ToLongTimeString();

                if (_loopsCount > 0)
                    output += ", " + _loopsCount.ToString() + " loops";

                output += " (" + Duration.ToString(@"hh\:mm\:ss") + ")";
            }
            else
                output += ", not started.";

            return output;
        }

        #endregion
    }
}
<file_sep>/GoBot/GoBot/Actions/Deplacement/ActionPivot.cs
﻿using Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionPivot : ITimeableAction
    {
        private AngleDelta angle;
        private Robot robot;
        private SensGD sens;

        public ActionPivot(Robot r, AngleDelta a, SensGD s)
        {
            robot = r;
            angle = a;
            sens = s;
        }

        System.Drawing.Image IAction.Image
        {
            get 
            {
                if (sens == SensGD.Droite)
                    return GoBot.Properties.Resources.TurnRigth16;
                else
                    return GoBot.Properties.Resources.TurnLeft16;
            }
        }

        public override String ToString()
        {
            return robot.Name + " pivote de " + angle + " " + sens.ToString().ToLower();
        }

        void IAction.Executer()
        {
            if(sens == SensGD.Droite)
                robot.PivotRight(angle);
            else
                robot.PivotLeft(angle);
        }

        public TimeSpan Duration
        {
            get
            {
                return robot.SpeedConfig.PivotDuration(angle, robot.WheelSpacing);
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Logs/Logs.cs
﻿using System;
using System.IO;
using System.Windows.Forms;

namespace GoBot.Logs
{
    public static class Logs
    {
        public static ILog LogDebug { get; private set; }
        public static ILog LogConsole { get; private set; }
        
        public static void Init()
        {
            LogDebug = new LogFile(Config.PathData + "/LogsTraces/LogDebug" + Execution.LaunchStartString + ".txt");
            LogConsole = new LogConsole();

            try
            {
                if (!Directory.Exists(Config.PathData + "/Logs/"))
                    Directory.CreateDirectory(Config.PathData + "/Logs/");
                if (!Directory.Exists(Config.PathData + "/Configs/"))
                    Directory.CreateDirectory(Config.PathData + "/Configs/");
                if (!Directory.Exists(Config.PathData + "/LogsTraces/"))
                    Directory.CreateDirectory(Config.PathData + "/LogsTraces/");

                Directory.CreateDirectory(Config.PathData + "/Logs/" + Execution.LaunchStartString);
            }
            catch (Exception)
            {
                MessageBox.Show("Problème lors de la création des dossiers de log.\nVérifiez si le dossier n'est pas protégé en écriture.", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Beacons/SuiviBalise.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Geometry.Shapes;

namespace GoBot.Beacons
{
    /// <summary>
    /// Association d'une position dans un plan à une date d'acquisition
    /// </summary>
    class PositionTemporelle
    {
        /// <summary>
        /// Date d'acquisition de la position
        /// </summary>
        public DateTime Date { get; set; }

        /// <summary>
        /// Position mesurée
        /// </summary>
        public RealPoint Position { get; set; }

        /// <summary>
        /// Constructeur
        /// </summary>
        /// <param name="date">Date d'acquisition de la position</param>
        /// <param name="position">Position mesurée</param>
        public PositionTemporelle(DateTime date, RealPoint position)
        {
            Date = date;
            Position = position;
        }
    }

    /// <summary>
    /// Petmet d'effectuer un tracking sur les différentes positions ennemies calculées par les balises.
    /// </summary>
    public static class SuiviBalise
    {
        // Nombre de balises à suivre
        public static int NombreMaxBalises { get; set; }

        public static List<RealPoint> PositionsEnnemies { get; set; }
        public static List<RealPoint> VecteursPositionsEnnemies { get; set; }
        private static List<List<PositionTemporelle>> PositionsTemporelles { get; set; }
        private static List<DateTime> DatePositionsBalises { get; set; }
        private const double deplacementMaxSeconde = 4000;
        
        public static void Init()
        {
            NombreMaxBalises = 2;
            PositionsEnnemies = new List<RealPoint>();
            PositionsTemporelles = new List<List<PositionTemporelle>>();
            VecteursPositionsEnnemies = new List<RealPoint>();
        }

        public static void MajPositions(List<RealPoint> detections, bool force = false)
        {
            //if (detections.Count < NombreMaxBalises && (detections.Count == 0 || detections.Count < PositionsEnnemies.Count))
            //    return;

            //if (force && detections.Count <= NombreMaxBalises)
            {
                VecteursPositionsEnnemies.Clear();
                PositionsEnnemies.Clear();
                PositionsEnnemies.AddRange(detections);

                DatePositionsBalises = new List<DateTime>();
                PositionsTemporelles.Clear();
                for (int i = 0; i < PositionsEnnemies.Count; i++)
                {
                    DatePositionsBalises.Add(DateTime.Now);
                    PositionsTemporelles.Add(new List<PositionTemporelle>());
                    PositionsTemporelles[i].Add(new PositionTemporelle(DateTime.Now, PositionsEnnemies[i]));
                    VecteursPositionsEnnemies.Add(new RealPoint());
                }
            }
            /*else
            {
                for (int i = 0; i < PositionsEnnemies.Count; i++)
                {
                    int plusProche = 0;
                    for(int j = 1; j < detections.Count; j++)
                    {
                        if (PositionsEnnemies[i].Distance(detections[j]) < PositionsEnnemies[i].Distance(detections[plusProche]))
                        {
                            plusProche = i;
                        }
                    }

                    if (PositionsEnnemies[i].Distance(detections[plusProche]) < deplacementMaxSeconde / 1000.0 * (DateTime.Now - DatePositionsBalises[i]).TotalMilliseconds)
                    {
                        PositionsEnnemies[i] = detections[plusProche];
                        DatePositionsBalises[i] = DateTime.Now;

                        PositionsTemporelles[i].Add(new PositionTemporelle(DateTime.Now, detections[plusProche]));

                        if (PositionsTemporelles[i].Count > 7)
                            PositionsTemporelles[i].RemoveAt(0);
                    }
                }

            }

            CalculVecteurs();*/
            if (PositionEnnemisActualisee != null)
                PositionEnnemisActualisee();
        }

        private static void CalculVecteurs()
        {
            for(int i = 0; i < PositionsEnnemies.Count; i++)
            {
                List<RealPoint> deplacements = new List<RealPoint>();

                for (int j = 1; j < PositionsTemporelles[i].Count; j++)
                {
                    double dx = PositionsTemporelles[i][j].Position.X - PositionsTemporelles[i][j - 1].Position.X;
                    double dy = PositionsTemporelles[i][j].Position.Y - PositionsTemporelles[i][j - 1].Position.Y;

                    TimeSpan t = PositionsTemporelles[i][j].Date - PositionsTemporelles[i][j - 1].Date;
                    if(t.TotalMilliseconds > 0)
                        deplacements.Add(new RealPoint(dx * 1000.0 / t.TotalMilliseconds, dy * 1000.0 / t.TotalMilliseconds));
                }

                double x = 0, y = 0;
                foreach (RealPoint p in deplacements)
                {
                    x += p.X;
                    y += p.Y;
                }

                if (deplacements.Count > 0)
                {
                    x /= deplacements.Count;
                    y /= deplacements.Count;
                }

                VecteursPositionsEnnemies[i] = new RealPoint(x, y);
            }
        }

        //Déclaration du délégué pour l’évènement de position des ennemis
        public delegate void PositionEnnemisDelegate();
        //Déclaration de l’évènement utilisant le délégué
        public static event PositionEnnemisDelegate PositionEnnemisActualisee;
    }
}
<file_sep>/GoBot/GoBot/Devices/Pepperl/Pepperl.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using GoBot.BoardContext;
using System.Threading;

namespace GoBot.Devices
{
    class Pepperl : Lidar
    {
        IPAddress _ip;
        PepperlManager _manager;

        PepperlFreq _freq;
        PepperlFilter _filter;
        int _filterWidth;

        ThreadLink _feedLink;

        List<RealPoint> _lastMeasure;
        Mutex _lockMeasure;

        public Pepperl(IPAddress ip)
        {
            _ip = ip;
            _manager = new PepperlManager(ip, 32123);
            _manager.NewMeasure += _manager_NewMeasure;
            _freq = PepperlFreq.Hz35;
            _filter = PepperlFilter.None;
            _filterWidth = 2;

            _checker.SendConnectionTest += _checker_SendConnectionTest;
        }

        private void _checker_SendConnectionTest(Communications.Connection sender)
        {
            if (!_checker.Connected && _started)
            {
                // On est censés être connectés mais en fait non, donc on essaie de relancer
                lock (this)
                {
                    // TODO : ou pas... ça déconne on dirait
                    //StopLoop();
                    //StartLoop();
                }
            }
        }
        public override AngleDelta DeadAngle => 0;

        public override bool Activated => _feedLink != null && _feedLink.Running;

        public int PointsPerScan { get { return _freq.SamplesPerScan() / (_filter == PepperlFilter.None ? 1 : _filterWidth); } }

        public AngleDelta Resolution { get { return new AngleDelta(new AngleDelta(360).InRadians / PointsPerScan, AngleType.Radian); } }

        public PepperlFreq Frequency { get { return _freq; } }
        public PepperlFilter Filter { get { return _filter; } }
        public int FilterWidth { get { return _filterWidth; } }

        public double PointsDistanceAt(double distance)
        {
            return distance * Resolution.InRadians;
        }

        private void _manager_NewMeasure(List<PepperlManager.PepperlPoint> measure, Geometry.AnglePosition startAngle, Geometry.AngleDelta resolution)
        {
            List<RealPoint> points = ValuesToPositions(measure.Select(p => p.distance).ToList(), startAngle, resolution, false, 0, 5000, _position);
            _lastMeasure = new List<RealPoint>(points);
            _lockMeasure?.ReleaseMutex();

            OnNewMeasure(points);
        }

        public void ShowMessage(String txt1, String txt2)
        {
            _manager.ShowMessage(txt1, txt2);
        }

        public void SetFrequency(PepperlFreq freq)
        {
            _freq = freq;
            _manager.SetFrequency(freq);
        }

        public void SetFilter(PepperlFilter filter, int size)
        {
            _filter = filter;
            _filterWidth = size;
            _manager.SetFilter(_filter, _filterWidth);
        }

        protected override bool StartLoop()
        {
            bool ok;

            if (_manager.CreateChannelUDP())
            {
                _feedLink = ThreadManager.CreateThread(link => FeedWatchDog());
                _feedLink.Name = "R2000 Watchdog";
                _feedLink.StartInfiniteLoop(1000);
                ok = true;
            }
            else
            {
                ok = false;
            }

            return ok;
        }

        protected override void StopLoop()
        {
            if (_feedLink != null)
            {
                _manager.CloseChannelUDP();
                _feedLink.Cancel();
                _feedLink.WaitEnd();
                _feedLink = null;
            }
        }

        public void Reboot()
        {
            _manager.Reboot();
        }

        public AngleDelta ScanRange { get { return 360; } }

        private List<RealPoint> ValuesToPositions(List<uint> measures, AnglePosition startAngle, AngleDelta resolution, bool limitOnTable, int minDistance, int maxDistance, Position refPosition)
        {
            List<RealPoint> positions = new List<RealPoint>();

            for (int i = 0; i < measures.Count; i++)
            {
                AnglePosition angle = resolution * i;

                if (measures[i] > minDistance && (measures[i] < maxDistance || maxDistance == -1))
                {
                    AnglePosition anglePoint = new AnglePosition(angle.InPositiveRadians - refPosition.Angle.InPositiveRadians - ScanRange.InRadians / 2 - Math.PI / 2, AngleType.Radian);

                    RealPoint pos = new RealPoint(refPosition.Coordinates.X - anglePoint.Sin * measures[i], refPosition.Coordinates.Y - anglePoint.Cos * measures[i]);

                    int marge = 20; // Marge en mm de distance de detection à l'exterieur de la table (pour ne pas jeter les mesures de la bordure qui ne collent pas parfaitement)
                    if (!limitOnTable || (pos.X > -marge && pos.X < GameBoard.Width + marge && pos.Y > -marge && pos.Y < GameBoard.Height + marge))
                        positions.Add(pos);
                }
            }

            return positions;
        }

        private void FeedWatchDog()
        {
            _manager.FeedWatchDog();
        }

        public override List<RealPoint> GetPoints()
        {
            _lockMeasure = new Mutex();
            _lockMeasure.WaitOne();
            _lockMeasure.Dispose();
            _lockMeasure = null;

            return new List<RealPoint>(_lastMeasure);
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageGestionLogs.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.IO;
using System.Windows.Forms;

using GoBot.Communications;

namespace GoBot.IHM.Pages
{
    public partial class PageGestionLog : UserControl
    {
        private enum Columns : int
        {
            Date,
            Taille,
            TramesMove,
            TramesIO,
            TramesGB,
            Events
        }

        public PageGestionLog()
        {
            InitializeComponent();

            dataSet = new DataSet();
            dataSet.Tables.Add();
            dataSet.Tables[0].Columns.Add("Date", typeof(DateTime));
            dataSet.Tables[0].Columns.Add("Taille", typeof(TailleDossier));
            dataSet.Tables[0].Columns.Add("Trames Move", typeof(int));
            dataSet.Tables[0].Columns.Add("Trames IO", typeof(int));
            dataSet.Tables[0].Columns.Add("Trames GoBot", typeof(int));
            dataSet.Tables[0].Columns.Add("Events", typeof(int));

            dataSetArchivage = new DataSet();
            dataSetArchivage.Tables.Add();
            dataSetArchivage.Tables[0].Columns.Add("Nom", typeof(String));
            dataSetArchivage.Tables[0].Columns.Add("Date", typeof(DateTime));
            dataSetArchivage.Tables[0].Columns.Add("Taille", typeof(TailleDossier));
            dataSetArchivage.Tables[0].Columns.Add("Trames Move", typeof(int));
            dataSetArchivage.Tables[0].Columns.Add("Trames IO", typeof(int));
            dataSetArchivage.Tables[0].Columns.Add("Trames GoBot", typeof(int));
            dataSetArchivage.Tables[0].Columns.Add("Events", typeof(int));
        }

        DataSet dataSet = null;
        DataSet dataSetArchivage = null;

        public static long FolderSize(string path)
        {
            long size = 0;
            DirectoryInfo directoryInfo = new DirectoryInfo(path);
            IEnumerable<FileInfo> files = directoryInfo.GetFiles("*", SearchOption.AllDirectories);
            foreach (FileInfo file in files)
            {
                size += file.Length;
            }

            return size;
        }

        private BackgroundWorker worker = new BackgroundWorker();
        private void PanelGestionLog_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                worker.DoWork += new DoWorkEventHandler(worker_DoWork);
                worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
                worker.WorkerReportsProgress = true;
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
                worker.RunWorkerAsync();

                //dataGridViewHistoLog.DataSource = dataSet.Tables[0];
                //dataGridViewArchives.DataSource = dataSetArchivage.Tables[0];
            }
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            lblDatePlusVieux.Text = plusVieux.ToString();
            lblNombreLogs.Text = nbLogs.ToString();
            TailleDossier tailleDossier = new TailleDossier(FolderSize(Config.PathData + "/Logs/"));
            lblTailleTotale.Text = tailleDossier.ToString();

            dataGridViewHistoLog.DataSource = dataSet.Tables[0];
            dataGridViewArchives.DataSource = dataSetArchivage.Tables[0];
            progressBar.Visible = false;
        }


        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
        }

        private DateTime plusVieux = DateTime.Now;
        private int nbLogs;
        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            plusVieux = DateTime.Now;

            int currentLog = 0;

            List<String> dossiers = (List<String>)Directory.EnumerateDirectories(Config.PathData + @"\Logs\").ToList<String>();
            nbLogs = dossiers.Count;

            dossiers.Sort(OrdreAlphabetiqueInverse);

            foreach (String dossier in dossiers)
            {
                if (dossier.Contains(Execution.LaunchStartString))
                    continue;

                if (Execution.Shutdown)
                    return;

                currentLog++;
                String dossier1 = Path.GetFileName(dossier);
                DateTime date;

                int nbElements = 0;

                try
                {
                    String[] tab = dossier1.Split(new char[5] { '.', ' ', 'h', 'm', 's' });
                    date = new DateTime(Convert.ToInt16(tab[0]), Convert.ToInt16(tab[1]), Convert.ToInt16(tab[2]), Convert.ToInt16(tab[3]), Convert.ToInt16(tab[4]), Convert.ToInt16(tab[5]));

                    if (date < plusVieux)
                        plusVieux = date;

                    long taille = FolderSize(dossier);

                    Object[] datas = new Object[Enum.GetValues(typeof(Columns)).Length];

                    datas[(int)Columns.Date] = date;
                    datas[(int)Columns.Taille] = new TailleDossier(taille);
                    //TODO2018 ca marche plus ça avec les nouveaux fichiers
                    foreach (String fichier in Directory.EnumerateFiles(dossier))
                    {
                        if (Path.GetFileName(fichier) == "ActionsGros.elog")
                        {
                            EventsReplay r = new EventsReplay();
                            if (r.Charger(fichier))
                            {
                                datas[(int)Columns.Events] = r.Events.Count;
                                nbElements += r.Events.Count;
                            }
                            else
                                datas[(int)Columns.Events] = -1;
                        }
                        else if (Path.GetFileName(fichier) == "ConnexionGB.tlog")
                        {
                            FramesLog r = new FramesLog();
                            r.Import(fichier);
                            if (r.Import(fichier))
                            {
                                datas[(int)Columns.TramesGB] = r.Frames.Count;
                                nbElements += r.Frames.Count;
                            }
                            else
                                datas[(int)Columns.TramesGB] = -1;
                        }
                        else if (Path.GetFileName(fichier) == "ConnexionMove.tlog")
                        {
                            FramesLog r = new FramesLog();
                            r.Import(fichier);
                            if (r.Import(fichier))
                            {
                                datas[(int)Columns.TramesMove] = r.Frames.Count;
                                nbElements += r.Frames.Count;
                            }
                            else
                                datas[(int)Columns.TramesMove] = -1;
                        }
                        else if (Path.GetFileName(fichier) == "ConnexionIO.tlog")
                        {
                            FramesLog r = new FramesLog();
                            r.Import(fichier);
                            if (r.Import(fichier))
                            {
                                datas[(int)Columns.TramesIO] = r.Frames.Count;
                                nbElements += r.Frames.Count;
                            }
                            else
                                datas[(int)Columns.TramesIO] = -1;
                        }
                    }

                    // S'il y a moins de 10 éléments dans ce log, on le supprime
                    if (nbElements < 10)
                        Directory.Delete(dossier, true);
                    else
                        dataSet.Tables[0].Rows.Add(datas);
                }
                catch (Exception)
                {
                }

                worker.ReportProgress((int)(currentLog * 100 / nbLogs));
            }

            if (Directory.Exists(Config.PathData + "/Archives/"))
            {
                foreach (String dossier in Directory.EnumerateDirectories(Config.PathData + "/Archives/"))
                {
                    if (Execution.Shutdown)
                        return;

                    currentLog++;
                    String dossier1 = Path.GetFileName(dossier);
                    DateTime date;

                    try
                    {
                        String[] tab = dossier1.Split(new char[5] { '.', ' ', 'h', 'm', 's' });
                        date = new DateTime(Convert.ToInt16(tab[0]), Convert.ToInt16(tab[1]), Convert.ToInt16(tab[2]), Convert.ToInt16(tab[3]), Convert.ToInt16(tab[4]), Convert.ToInt16(tab[5]));

                        if (date < plusVieux)
                            plusVieux = date;

                        long taille = FolderSize(dossier);

                        Object[] datas = new Object[Enum.GetValues(typeof(Columns)).Length+1];

                        datas[(int)Columns.Date + 1] = date;
                        datas[(int)Columns.Taille + 1] = new TailleDossier(taille);

                        foreach (String fichier in Directory.EnumerateFiles(dossier))
                        {
                            if (Path.GetFileName(fichier) == "ActionsGros.elog")
                            {
                                EventsReplay r = new EventsReplay();
                                if (r.Charger(fichier))
                                    datas[(int)Columns.Events + 1] = r.Events.Count;
                                else
                                    datas[(int)Columns.Events + 1] = -1;
                            }
                            else if (Path.GetFileName(fichier) == "ConnexionGB.tlog")
                            {
                                FramesLog r = new FramesLog();
                                r.Import(fichier);
                                if (r.Import(fichier))
                                    datas[(int)Columns.TramesGB + 1] = r.Frames.Count;
                                else
                                    datas[(int)Columns.TramesGB + 1] = -1;
                            }
                            else if (Path.GetFileName(fichier) == "ConnexionMove.tlog")
                            {
                                FramesLog r = new FramesLog();
                                r.Import(fichier);
                                if (r.Import(fichier))
                                    datas[(int)Columns.TramesMove + 1] = r.Frames.Count;
                                else
                                    datas[(int)Columns.TramesMove + 1] = -1;
                            }
                            else if (Path.GetFileName(fichier) == "ConnexionIO.tlog")
                            {
                                FramesLog r = new FramesLog();
                                r.Import(fichier);
                                if (r.Import(fichier))
                                    datas[(int)Columns.TramesIO + 1] = r.Frames.Count;
                                else
                                    datas[(int)Columns.TramesIO + 1] = -1;
                            }
                            else if (Path.GetFileName(fichier) == "Archivage")
                            {
                                StreamReader reader = new StreamReader(fichier);
                                datas[0] = reader.ReadLine();
                                reader.Close();
                            }
                        }

                        dataSetArchivage.Tables[0].Rows.Add(datas);
                    }
                    catch (Exception)
                    {
                    }
                }
            }
        }

        private int OrdreAlphabetiqueInverse(String a, String b)
        {
            return a.CompareTo(b) * -1;
        }

        private void btnOuvrirDossier_Click(object sender, EventArgs e)
        {
            if (dataGridViewHistoLog.SelectedRows.Count > 0)
            {
                DateTime ligne = (DateTime)dataGridViewHistoLog.SelectedRows[0].Cells["Date"].Value;
                String dossier = Config.PathData + "\\Logs\\" + ligne.Year.ToString("0000") + "." + ligne.Month.ToString("00") + "." + ligne.Day.ToString("00") + " " + ligne.Hour.ToString("00") + "h" + ligne.Minute.ToString("00") + "m" + ligne.Second.ToString("00") + "s";
                System.Diagnostics.Process.Start("explorer.exe", dossier);
            }
        }

        private void btnSupprimer_Click(object sender, EventArgs e)
        {
            if (dataGridViewHistoLog.SelectedRows.Count > 0)
            {
                String listeDossiers = "";
                for (int i = 0; i < dataGridViewHistoLog.SelectedRows.Count && i <= 20; i++)
                {
                    DateTime ligne = (DateTime)dataGridViewHistoLog.SelectedRows[i].Cells["Date"].Value;
                    listeDossiers += Config.PathData + "\\Logs\\" + ligne.Year.ToString("0000") + "." + ligne.Month.ToString("00") + "." + ligne.Day.ToString("00") + " " + ligne.Hour.ToString("00") + "h" + ligne.Minute.ToString("00") + "m" + ligne.Second.ToString("00") + "s" + Environment.NewLine;

                    if (i == 20)
                        listeDossiers += "... Et " + (dataGridViewHistoLog.SelectedRows.Count - i) + " autres dossiers ...";
                }
                if (MessageBox.Show("Êtes vous sûr de vouloir supprimer le(s) dossier(s) de log suivant(s) ?" + Environment.NewLine + Environment.NewLine + listeDossiers + " ?", "Confirmation", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK)
                {
                    for (int i = dataGridViewHistoLog.SelectedRows.Count - 1; i >= 0; i--)
                    {
                        DateTime ligne = (DateTime)dataGridViewHistoLog.SelectedRows[i].Cells["Date"].Value;
                        String dossier = Config.PathData + "\\Logs\\" + ligne.Year.ToString("0000") + "." + ligne.Month.ToString("00") + "." + ligne.Day.ToString("00") + " " + ligne.Hour.ToString("00") + "h" + ligne.Minute.ToString("00") + "m" + ligne.Second.ToString("00") + "s" + Environment.NewLine;

                        Directory.Delete(dossier, true);
                        dataGridViewHistoLog.Rows.RemoveAt(dataGridViewHistoLog.SelectedRows[i].Index);
                    }
                }
            }
        }

        private void dataGridViewHistoLog_SelectionChanged(object sender, EventArgs e)
        {
            if (dataGridViewHistoLog.SelectedRows.Count > 0)
            {
                dataGridViewFichiersLog.Rows.Clear();

                DateTime ligne = (DateTime)dataGridViewHistoLog.SelectedRows[0].Cells["Date"].Value;
                String dossier = Config.PathData + "\\Logs\\" + ligne.Year.ToString("0000") + "." + ligne.Month.ToString("00") + "." + ligne.Day.ToString("00") + " " + ligne.Hour.ToString("00") + "h" + ligne.Minute.ToString("00") + "m" + ligne.Second.ToString("00") + "s";

                lblFichiers.Text = "Fichiers du " + ligne.ToString();

                if (Directory.Exists(dossier))
                {
                    foreach (String fichier in Directory.EnumerateFiles(dossier))
                    {
                        String extension = Path.GetExtension(fichier);
                        if (extension == FramesLog.FileExtension || extension == ".elog")
                        {
                            dataGridViewFichiersLog.Rows.Add(Path.GetFileName(fichier), fichier);
                        }
                    }
                }
            }
        }

        private void dataGridViewFichiersLog_DoubleClick(object sender, EventArgs e)
        {
            if (dataGridViewFichiersLog.SelectedRows.Count > 0)
            {
                String fichier = (String)(dataGridViewFichiersLog.Rows[dataGridViewFichiersLog.SelectedRows[0].Index].Cells["Chemin"].Value);
                FenGoBot.Instance.ChargerReplay(fichier);
            }
        }

        private void btnArchivage_Click(object sender, EventArgs e)
        {
            if (dataGridViewHistoLog.SelectedRows.Count > 0)
            {
                DateTime ligne = (DateTime)dataGridViewHistoLog.SelectedRows[0].Cells["Date"].Value;
                String dossier = Config.PathData + "\\Logs\\" + ligne.Year.ToString("0000") + "." + ligne.Month.ToString("00") + "." + ligne.Day.ToString("00") + " " + ligne.Hour.ToString("00") + "h" + ligne.Minute.ToString("00") + "m" + ligne.Second.ToString("00") + "s";
                String nomCourt = Path.GetFileName(dossier);
                FenNomArchivage fen = new FenNomArchivage();
                fen.Nom = ligne.ToString();
                fen.ShowDialog();

                if (!fen.OK)
                    return;

                Directory.Move(dossier, Config.PathData + "\\Archives\\" + nomCourt);
                StreamWriter writer = new StreamWriter(Config.PathData + "\\Archives\\" + nomCourt + "\\Archivage");
                writer.Write(fen.Nom);
                writer.Close();

                Object[] datas = new Object[Enum.GetValues(typeof(Columns)).Length + 1];

                datas[0] = fen.Nom;

                for (int i = 0; i < Enum.GetValues(typeof(Columns)).Length; i++)
                    datas[i + 1] = dataGridViewHistoLog.SelectedRows[0].Cells[i].Value;

                dataGridViewHistoLog.Rows.RemoveAt(dataGridViewHistoLog.SelectedRows[0].Index);

                dataSetArchivage.Tables[0].Rows.Add(datas);
            }
        }

        private void dataGridViewArchives_SelectionChanged(object sender, EventArgs e)
        {
            if (dataGridViewArchives.SelectedRows.Count > 0)
            {
                dataGridViewFichiersLog.Rows.Clear();

                DateTime ligne = (DateTime)dataGridViewArchives.SelectedRows[0].Cells["Date"].Value;
                String dossier = Config.PathData + "\\Archives\\" + ligne.Year.ToString("0000") + "." + ligne.Month.ToString("00") + "." + ligne.Day.ToString("00") + " " + ligne.Hour.ToString("00") + "h" + ligne.Minute.ToString("00") + "m" + ligne.Second.ToString("00") + "s";

                lblFichiers.Text = "Fichiers du " + ligne.ToString();

                if (Directory.Exists(dossier))
                {
                    foreach (String fichier in Directory.EnumerateFiles(dossier))
                    {
                        String extension = Path.GetExtension(fichier);
                        if (extension == FramesLog.FileExtension || extension == ".elog")
                        {
                            dataGridViewFichiersLog.Rows.Add(Path.GetFileName(fichier), fichier);
                        }
                    }
                }
            }
        }
    }

    class TailleDossier : IComparable
    {
        String taille;
        long tailleOctets;

        public TailleDossier(long t)
        {
            tailleOctets = t;

            long tailleModif = tailleOctets;

            String extension = " o";
            if (tailleModif > 1024)
            {
                tailleModif /= 1024;
                extension = " ko";
            }
            if (tailleModif > 1024)
            {
                tailleModif /= 1024;
                extension = " mo";
            }

            taille = tailleModif + extension;
        }

        public override string ToString()
        {
            return taille;
        }

        public int CompareTo(object obj)
        {
            TailleDossier autre = (TailleDossier)obj;

            if (autre.tailleOctets > tailleOctets)
                return 1;
            if (autre.tailleOctets == tailleOctets)
                return 0;

            return -1;
        }
    }
}
<file_sep>/GoBot/Composants/TrackBarPlus.cs
﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;

namespace Composants
{
    public partial class TrackBarPlus : UserControl
    {
        private bool _isReversed;
        private bool _isVertical;
        private double _value, _maxValue, _minValue;
        private int _decimalPlaces;
        private bool _isMoving;
        private int _cursorSize;

        private double _lastTickedValue = -1;

        public delegate void ValueChangedDelegate(object sender, double value);

        public event ValueChangedDelegate TickValueChanged;
        public event ValueChangedDelegate ValueChanged;

        public TrackBarPlus()
        {
            InitializeComponent();

            DecimalPlaces = 0;
            _isVertical = false;
            _isReversed = false;
            IntervalTimer = 1;
            TimerTickValue = new Timer();
            TimerTickValue.Tick += new EventHandler(TimerTickValue_Tick);

            _minValue = 0;
            _maxValue = 100;
            _isMoving = false;
            _decimalPlaces = 0;

            _cursorSize = Height - 1;
        }

        private Timer TimerTickValue { get; set; }

        public double Value => _value;

        public uint IntervalTimer { get; set; }

        public int DecimalPlaces { get; set; }

        public double Min
        {
            get { return _minValue; }
            set
            {
                _minValue = value;
                SetValue(_value);
            }
        }

        public double Max
        {
            get { return _maxValue; }
            set
            {
                _maxValue = value;
                SetValue(_value);
            }
        }

        public bool Vertical
        {
            get
            {
                return _isVertical;
            }
            set
            {
                int oldWidth = Width;
                int oldHeight = Height;

                if (value)
                {
                    MaximumSize = new Size(15, 3000);
                    MinimumSize = new Size(15, 30);

                    Height = Math.Max(oldWidth, oldHeight);
                    Width = Math.Min(oldWidth, oldHeight);
                }
                else
                {
                    MaximumSize = new Size(3000, 15);
                    MinimumSize = new Size(30, 15);

                    Width = Math.Max(oldWidth, oldHeight);
                    Height = Math.Min(oldWidth, oldHeight);
                }

                _isVertical = value;
                _cursorSize = _isVertical ? Width - 1 : Height - 1;
                Invalidate();
            }
        }

        public bool Reverse
        {
            get
            {
                return _isReversed;
            }
            set
            {
                _isReversed = value;
                Invalidate();
            }
        }

        public void SetValue(double val, bool tickEvent = true)
        {
            val = Math.Max(Min, Math.Min(Max, val));

            if (_value != val)
            {
                _value = Math.Round(val, DecimalPlaces);

                if (tickEvent)
                    TickValueChanged?.Invoke(this, _value);

                ValueChanged?.Invoke(this, _value);
            }

            Invalidate();
        }

        #region Events

        protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {
            int direction = Reverse ? -1 : 1;

            if (e.KeyCode == Keys.Down)
            {
                SetValue(Reverse ? Max : Min);
                e.IsInputKey = true;
            }
            else if (e.KeyCode == Keys.Up)
            {
                SetValue(Reverse ? Min : Max);
                e.IsInputKey = true;
            }
            else if (e.KeyCode == Keys.Left)
            {
                SetValue(_value - Math.Ceiling((Max - Min) * 0.05) * direction);
                e.IsInputKey = true;
            }
            else if (e.KeyCode == Keys.Right)
            {
                SetValue(_value + Math.Ceiling((Max - Min) * 0.05) * direction);
                e.IsInputKey = true;
            }
            else if (e.KeyCode == Keys.Add)
            {
                SetValue(_value + direction);
                e.IsInputKey = true;
            }
            else if (e.KeyCode == Keys.Subtract)
            {
                SetValue(_value - direction);
                e.IsInputKey = true;
            }
            base.OnPreviewKeyDown(e);
        }

        void img_MouseMove(object sender, MouseEventArgs e)
        {
            OnMouseMove(e);
        }

        void img_MouseUp(object sender, MouseEventArgs e)
        {
            OnMouseUp(e);
        }

        void img_MouseDown(object sender, MouseEventArgs e)
        {
            OnMouseDown(e);
        }

        protected override void OnEnter(EventArgs e)
        {
            Invalidate();
            base.OnEnter(e);
        }

        protected override void OnLeave(EventArgs e)
        {
            Invalidate();
            base.OnLeave(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            Focus();
            if (e.Button == MouseButtons.Left)
            {
                StartMoving();
                SetCursorPosition(this.PointToClient(Cursor.Position));
            }

            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                EndMoving();

            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (_isMoving) SetCursorPosition(this.PointToClient(Cursor.Position));

            base.OnMouseMove(e);
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            Focus();
            SetValue(_value + Math.Sign(e.Delta) * (_maxValue - _minValue) / 100, true);
        }

        private void TrackBarPlus_Leave(object sender, EventArgs e)
        {
            Invalidate();
        }

        private void TrackBarPlus_Enter(object sender, EventArgs e)
        {
            Invalidate();
        }

        private void TrackBarPlus_SizeChanged(object sender, EventArgs e)
        {
            _cursorSize = _isVertical ? Width - 1 : Height - 1;
        }

        #endregion

        private void StartMoving()
        {
            _isMoving = true;

            // Le premier tick se fait en 1 milliseconde, les autres suivant l'intervalle
            TimerTickValue.Interval = 1;
            TimerTickValue.Start();
        }

        private void EndMoving()
        {
            _isMoving = false;
        }

        void TimerTickValue_Tick(object sender, EventArgs e)
        {
            // les ticks suivants se font avec l'intervalle voulu
            TimerTickValue.Interval = (int)IntervalTimer;

            if (_lastTickedValue != _value)
            {
                _lastTickedValue = _value;
                TickValueChanged?.Invoke(this, _value);
            }

            if (!_isMoving)
                TimerTickValue.Stop();
        }

        private void TrackBarPlus_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            PaintBack(e.Graphics);
            PaintCursor(e.Graphics);
        }

        private void PaintBack(Graphics g)
        {
            GraphicsPath path = new GraphicsPath();
            int x, y, w, h;

            Brush b;

            if (_isVertical)
            {
                h = Height - 1;
                w = (int)(Width / 2.5);
                x = Width / 2 - w / 2;
                y = 0;

                path.AddLine(x + w - 1, y + w / 2 + 1, x + w - 1, y + h - w / 2 - 1);
                path.AddArc(new Rectangle(x + 1, y + h - w - 1, w - 2, w - 2), 0, 180);
                path.AddLine(x + 1, y + w - 1, x + 1, y + w / 2 + 1);
                path.AddArc(new Rectangle(x + 1, y + 1, w - 2, w - 2), 180, 180);

                if (_isReversed)
                    b = new LinearGradientBrush(new Rectangle(x, y, w, h), Color.Gainsboro, Color.White, LinearGradientMode.Vertical);
                else
                    b = new LinearGradientBrush(new Rectangle(x, y, w, h), Color.White, Color.Gainsboro, LinearGradientMode.Vertical);
            }
            else
            {
                h = (int)(Height / 2.5);
                w = Width - 1;
                x = 0;
                y = Height / 2 - h / 2;

                path.AddLine(x + h / 2 + 1, y + 1, x + w - h / 2 - 1, y + 1);
                path.AddArc(new Rectangle(x + w - h - 1, y + 1, h - 2, h - 2), -90, 180);
                path.AddLine(x + w - h / 2 - 1, y + h - 1, x + h / 2 + 1, y + h - 1);
                path.AddArc(new Rectangle(x + 1, y + 1, h - 2, h - 2), 90, 180);

                if (_isReversed)
                    b = new LinearGradientBrush(new Rectangle(x, y, w, h), Color.White, Color.Gainsboro, LinearGradientMode.Horizontal);
                else
                    b = new LinearGradientBrush(new Rectangle(x, y, w, h), Color.Gainsboro, Color.White, LinearGradientMode.Horizontal);
            }

            g.FillPath(b, path);
            b.Dispose();

            g.DrawPath(Pens.LightGray, path);

            path.Dispose();
        }

        private void PaintCursor(Graphics g)
        {
            Rectangle rBall;

            if (_isVertical)
            {
                int y;
                if (_isReversed)
                    y = (int)(((_value - _minValue) * (Height - 1 - _cursorSize)) / (_maxValue - _minValue));
                else
                    y = Height - _cursorSize - 1 - (int)(((_value - _minValue) * (Height - 1 - _cursorSize)) / (_maxValue - _minValue));

                if (_minValue == _maxValue)
                    y = 0;

                rBall = new Rectangle(0, y, _cursorSize, _cursorSize);
            }
            else
            {
                int x;
                if (_isReversed)
                    x = Width - _cursorSize - 1 - (int)(((_value - _minValue) * (Width - 1 - _cursorSize)) / (_maxValue - _minValue));
                else
                    x = (int)(((_value - _minValue) * (Width - 1 - _cursorSize)) / (_maxValue - _minValue));

                if (_minValue == _maxValue)
                    x = 0;

                rBall = new Rectangle(x, 0, _cursorSize, _cursorSize);
            }

            Brush bBall = new LinearGradientBrush(rBall, Color.WhiteSmoke, Color.LightGray, 100);
            g.FillEllipse(bBall, rBall);
            bBall.Dispose();

            g.DrawEllipse(Focused ? Pens.DodgerBlue : Pens.Gray, rBall);
        }

        private void SetCursorPosition(Point pos)
        {
            double val;

            if (!Vertical)
            {
                if (pos.X <= _cursorSize / 2)
                    val = (_isReversed) ? _maxValue : _minValue;
                else if (pos.X >= this.Width - _cursorSize / 2 - 1)
                    val = (_isReversed) ? _minValue : _maxValue;
                else
                {
                    val = _minValue + (_maxValue - _minValue) * (pos.X - _cursorSize / 2) / (float)(Width - 1 - _cursorSize);

                    if (_isReversed)
                        val = _maxValue - val + _minValue;
                }
            }
            else
            {
                if (pos.Y <= _cursorSize / 2)
                    val = (!_isReversed) ? _maxValue : _minValue;
                else if (pos.Y >= this.Height - _cursorSize / 2 - 1)
                    val = (!_isReversed) ? _minValue : _maxValue;
                else
                {
                    val = _minValue + (_maxValue - _minValue) * (pos.Y - _cursorSize / 2) / (float)(Height - 1 - _cursorSize);
                    if (!_isReversed)
                        val = _maxValue - val + _minValue;
                }
            }

            SetValue(val, false);
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelConnections.cs
﻿using Composants;
using GoBot.Communications;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class PanelConnexions : UserControl
    {
        private Timer _timerBatteries;

        public PanelConnexions()
        {
            InitializeComponent();
        }

        void timerBatteries_Tick(object sender, EventArgs e)
        {
            if (Robots.Simulation)
            {
                batteryPack.Enabled = false;
            }
            else
            {
                if (Connections.ConnectionIO.ConnectionChecker.Connected)
                {
                    batteryPack.Enabled = true;
                    batteryPack.CurrentVoltage = Robots.MainRobot.BatterieVoltage;
                    lblVoltage.Text = Robots.MainRobot.BatterieVoltage.ToString("0.00") + "V";
                }
                else
                {
                    batteryPack.Enabled = false;
                    lblVoltage.Text = "-";
                }
            }
        }

        private void PanelConnexions_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                ConnectionStatus statusAvoid = new ConnectionStatus();
                statusAvoid.Connection = Devices.AllDevices.LidarAvoid?.ConnectionChecker;
                statusAvoid.ConnectionName = "Avoid";
                _ledsPanel.Controls.Add(statusAvoid);

                foreach (Connection conn in Connections.AllConnections)
                {
                    ConnectionStatus status = new ConnectionStatus();
                    status.Connection = conn.ConnectionChecker;
                    status.ConnectionName = conn.Name;
                    _ledsPanel.Controls.Add(status);
                }

                batteryPack.VoltageHigh = Config.CurrentConfig.BatterieRobotVert;
                batteryPack.VoltageAverage = Config.CurrentConfig.BatterieRobotOrange;
                batteryPack.VoltageLow = Config.CurrentConfig.BatterieRobotRouge;
                batteryPack.VoltageVeryLow = Config.CurrentConfig.BatterieRobotCritique;

                _timerBatteries = new System.Windows.Forms.Timer();
                _timerBatteries.Interval = 1000;
                _timerBatteries.Tick += new EventHandler(timerBatteries_Tick);
                _timerBatteries.Start();
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/SegmentWithPolygon.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class SegmentWithPolygon
    {    
        public static bool Contains(Segment containingSegment, Polygon containedPolygon)
        {            
            // Contenir un polygone dans un segment revient à contenir tous les points du polygone
            return containedPolygon.Points.TrueForAll(p => SegmentWithRealPoint.Contains(containingSegment, p));
        }

        public static bool Cross(Segment segment, Polygon polygon)
        {
            return polygon.Sides.Exists(s => SegmentWithSegment.Cross(s, segment));
        }

        public static double Distance(Segment segment, Polygon polygon)
        {
            return polygon.Sides.Min(s => SegmentWithSegment.Distance(s, segment));
        }

        public static List<RealPoint> GetCrossingPoints(Segment segment, Polygon polygon)
        {
            return polygon.Sides.SelectMany(s => SegmentWithSegment.GetCrossingPoints(s, segment)).ToList();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/PagesPanda/PagePandaTable.cs
﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

using GoBot.BoardContext;
using GoBot.Communications;
using GoBot.Threading;

namespace GoBot.IHM.Pages
{
    public partial class PagePandaTable : UserControl
    {
        public PagePandaTable()
        {
            InitializeComponent();
        }

        private void PagePandaTable_Load(object sender, System.EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                btnTrap.Focus();

                Dessinateur.TableDessinee += Dessinateur_TableDessinee;
            }
        }

        private void Dessinateur_TableDessinee(Image img)
        {
            picTable.BackgroundImage = img;
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/PolygonWithCircle.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class PolygonWithCircle
    {
        public static bool Contains(Polygon containingPolygon, Circle containedCircle)
        {
            // Pour contenir un cercle, un polygone ne doit pas être contenu par le cercle, ne pas le croiser et contenir son centre
            return !CircleWithPolygon.Contains(containedCircle, containingPolygon) && !PolygonWithCircle.Cross(containingPolygon, containedCircle) && PolygonWithRealPoint.Contains(containingPolygon, containedCircle.Center);
        }

        public static bool Cross(Polygon polygon, Circle circle)
        {
            return CircleWithPolygon.Cross(circle, polygon);
        }

        public static double Distance(Polygon polygon, Circle circle)
        {
            return CircleWithPolygon.Distance(circle, polygon);
        }

        public static List<RealPoint> GetCrossingPoints(Polygon polygon, Circle circle)
        {
            return CircleWithPolygon.GetCrossingPoints(circle, polygon);
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Elements/ConnectionStatus.cs
﻿using GoBot.Communications;
using GoBot.Communications.UDP;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class ConnectionStatus : UserControl
    {
        private ConnectionChecker _connection;
        private Timer _timer;

        public ConnectionStatus()
        {
            InitializeComponent();

            _timer = new Timer();
            _timer.Interval = 500;
            _timer.Tick += _timer_Tick;
            _timer.Start();
        }

        private void _timer_Tick(object sender, EventArgs e)
        {
            if (_connection != null)
                this.InvokeAuto(() => _conIndicator.BlinkColor(GetColor(_connection.Connected, _connection.Connected)));
        }

        public ConnectionChecker Connection
        {
            get
            {
                return _connection;
            }
            set
            {
                if (_connection != null)
                    _connection.ConnectionStatusChange -= ConnexionCheck_ConnectionStatusChange;

                _connection = value;

                if (_connection != null)
                {
                    _conIndicator.Color = GetColor(_connection.Connected, _connection.Connected);
                    _connection.ConnectionStatusChange += ConnexionCheck_ConnectionStatusChange;
                }
            }
        }

        public String ConnectionName
        {
            set { _lblName.Text = value; }
        }

        public Color TextColor
        {
            set { _lblName.ForeColor = value; }
        }

        private void ConnexionCheck_ConnectionStatusChange(Connection sender, bool connected)
        {
            this.InvokeAuto(() => _conIndicator.BlinkColor(GetColor(_connection.Connected, connected)));
        }

        private ColorPlus GetColor(bool inOK, bool outOk)
        {
            ColorPlus output;

            if (inOK && outOk)
                output = Color.LimeGreen;
            else if (!inOK && !outOk)
                output = Color.Red;
            else
                output = Color.Orange;

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/PagesPanda/PagePandaMatch.cs
﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

using GoBot.BoardContext;
using GoBot.Communications;
using GoBot.Threading;

namespace GoBot.IHM.Pages
{
    public partial class PagePandaMatch : UserControl
    {
        public delegate void DoneDelegate();
        public event DoneDelegate CalibrationDone;

        public PagePandaMatch()
        {
            InitializeComponent();

            GameBoard.MyColorChange += GameBoard_MyColorChange;
            btnColorLeft.BackColor = GameBoard.ColorLeftBlue;
            btnColorRight.BackColor = GameBoard.ColorRightYellow;
        }

        private void PageMatch_Load(object sender, System.EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                btnTrap.Focus();

                Dessinateur.TableDessinee += Dessinateur_TableDessinee;
                Robots.MainRobot.SensorOnOffChanged += MainRobot_SensorOnOffChanged;
                Connections.AllConnections.ForEach(c => c.ConnectionChecker.ConnectionStatusChange += ConnectionChecker_ConnectionStatusChange);
                Devices.AllDevices.LidarAvoid.ConnectionChecker.ConnectionStatusChange += LidarAvoid_ConnectionStatusChange;
                Devices.AllDevices.LidarGround.ConnectionChecker.ConnectionStatusChange += LidarGround_ConnectionStatusChange;

                SetPicImage(picLidar, Devices.AllDevices.LidarAvoid.ConnectionChecker.Connected);
                //SetPicImage(picLidar2, Devices.AllDevices.LidarGround.ConnectionChecker.Connected);
                SetPicImage(picIO, Connections.ConnectionIO.ConnectionChecker.Connected);
                SetPicImage(picMove, Connections.ConnectionMove.ConnectionChecker.Connected);
                SetPicImage(picCAN, Connections.ConnectionCanBridge.ConnectionChecker.Connected);
                SetPicImage(picServo1, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo1].ConnectionChecker.Connected);
                SetPicImage(picServo2, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo2].ConnectionChecker.Connected);
                SetPicImage(picServo3, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo3].ConnectionChecker.Connected);
                SetPicImage(picServo4, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo4].ConnectionChecker.Connected);
                SetPicImage(picServo5, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo5].ConnectionChecker.Connected);
                SetPicImage(picServo6, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo6].ConnectionChecker.Connected);
                //SetPicImage(picAlim, Connections.ConnectionsCan[Communications.CAN.CanBoard.CanAlim].ConnectionChecker.Connected);

                bool jack = Robots.MainRobot.ReadStartTrigger();
                SetPicImage(picStartTrigger, jack);
                btnCalib.Enabled = jack;
            }
        }

        private void LidarAvoid_ConnectionStatusChange(Connection sender, bool connected)
        {
            SetPicImage(picLidar, connected);
        }

        private void LidarGround_ConnectionStatusChange(Connection sender, bool connected)
        {
            //SetPicImage(picLidar2, connected);
        }

        private void ConnectionChecker_ConnectionStatusChange(Connection sender, bool connected)
        {
            if (sender == Connections.ConnectionIO)
                SetPicImage(picIO, connected);
            else if (sender == Connections.ConnectionMove)
                SetPicImage(picMove, connected);
            else if (sender == Connections.ConnectionCanBridge)
                SetPicImage(picCAN, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo1])
                SetPicImage(picServo1, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo2])
                SetPicImage(picServo2, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo3])
                SetPicImage(picServo3, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo4])
                SetPicImage(picServo4, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo5])
                SetPicImage(picServo5, connected);
            else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanServo6])
                SetPicImage(picServo6, connected);
            //else if (sender == Connections.ConnectionsCan[Communications.CAN.CanBoard.CanAlim])
            //    SetPicImage(picAlim, connected);
        }

        private void MainRobot_SensorOnOffChanged(SensorOnOffID capteur, bool etat)
        {
            if (capteur == SensorOnOffID.StartTrigger)
            {
                SetPicImage(picStartTrigger, etat);
                btnCalib.InvokeAuto(() => btnCalib.Enabled = true);
            }
        }

        private static void SetPicImage(PictureBox pic, bool ok)
        {
            if (pic.Width > 50)
                pic.Image = ok ? Properties.Resources.ValidOk96 : Properties.Resources.ValidNok96;
            else
                pic.Image = ok ? Properties.Resources.ValidOk48 : Properties.Resources.ValidNok48;
        }

        private void GameBoard_MyColorChange(object sender, EventArgs e)
        {
            Rectangle r = new Rectangle(8, 8, 78, 78);

            Bitmap img = new Bitmap(picColor.Width, picColor.Height);
            Graphics g = Graphics.FromImage(img);
            Brush brush = new LinearGradientBrush(r, ColorPlus.GetIntense(GameBoard.MyColor), ColorPlus.GetPastel(GameBoard.MyColor), 24);

            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.FillEllipse(brush, r);
            g.DrawImage(Properties.Resources.Circle96, 0, 0, 96, 96);

            brush.Dispose();
            g.Dispose();

            picColor.Image = img;
        }

        private void Dessinateur_TableDessinee(Image img)
        {
            picTable.BackgroundImage = img;
        }

        private void btnCalib_Click(object sender, System.EventArgs e)
        {
            ThreadManager.CreateThread(link =>
            {
                link.Name = "Calibration";
                Recalibration.Calibration();
                picCalibration.InvokeAuto(() => picCalibration.Image = Properties.Resources.ValidOk96);
                CalibrationDone?.Invoke();
            }).StartThread();
            btnTrap.Focus();
        }

        private void btnColorLeft_Click(object sender, EventArgs e)
        {
            GameBoard.MyColor = GameBoard.ColorLeftBlue;
            btnTrap.Focus();
        }

        private void btnColorRight_Click(object sender, EventArgs e)
        {
            GameBoard.MyColor = GameBoard.ColorRightYellow;
            btnTrap.Focus();
        }

        private void btnInit_Click(object sender, EventArgs e)
        {
            btnTrap.Focus();
            Robots.MainRobot.ActuatorsStore();
            SetPicImage(picInit, true);
            btnInit.Enabled = false;
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/Fingers.cs
﻿using GoBot.GameElements;
using GoBot.Threading;
using System.Diagnostics;
using System.Drawing;
using System.Threading;

namespace GoBot.Actionneurs
{
    class FingerLeft : Finger
    {
        public FingerLeft()
        {
            _makeVacuum = ActuatorOnOffID.MakeVacuumLeftBack;
            _openVacuum = ActuatorOnOffID.OpenVacuumLeftBack;
            _pressure = SensorOnOffID.PressureSensorLeftBack;
            _finger = Config.CurrentConfig.ServoFingerLeft;
        }
    }

    class FingerRight : Finger
    {
        public FingerRight()
        {
            _makeVacuum = ActuatorOnOffID.MakeVacuumRightBack;
            _openVacuum = ActuatorOnOffID.OpenVacuumRightBack;
            _pressure = SensorOnOffID.PressureSensorRightBack;
            _finger = Config.CurrentConfig.ServoFingerRight;
        }
    }

    abstract class Finger
    {
        protected ActuatorOnOffID _makeVacuum, _openVacuum;
        protected SensorOnOffID _pressure;
        protected ServoFinger _finger;

        protected Color _load;

        public bool Loaded => _load != Color.Transparent;
        public Color Load => _load;

        public Finger()
        {
            _load = Color.Transparent;
        }

        public void DoDemoGrab(ThreadLink link = null)
        {
            Stopwatch swMain = Stopwatch.StartNew();
            bool ok;

            DoAirLock();

            while (!link.Cancelled && swMain.Elapsed.TotalMinutes < 1)
            {
                ok = false;
                Thread.Sleep(1000);

                if (!HasSomething())
                {
                    DoPositionGrab();

                    Stopwatch sw = Stopwatch.StartNew();

                    while (sw.ElapsedMilliseconds < 1000 && !ok)
                    {
                        Thread.Sleep(50);
                        ok = HasSomething();
                    }

                    if (ok)
                        DoPositionKeep();
                    else
                        DoPositionHide();
                }
            }

            DoPositionHide();
            DoAirUnlock();
        }

        public bool DoGrabGreen()
        {
            bool ok = false;

            DoAirLock();
            DoPositionGrab();

            Stopwatch sw = Stopwatch.StartNew();

            while (sw.ElapsedMilliseconds < 750 && !ok)
            {
                Thread.Sleep(50);
                ok = HasSomething();
            }

            if (ok)
            {
                _load = Buoy.Green;
                DoPositionKeep();
            }
            else
            {
                DoAirUnlock();
                DoPositionHide();
            }

            return ok;
        }

        public bool DoGrabColor(Color c)
        {
            bool ok = false;

            DoAirLock();
            DoPositionGrab();

            Stopwatch sw = Stopwatch.StartNew();

            while (sw.ElapsedMilliseconds < 750 && !ok)
            {
                Thread.Sleep(50);
                ok = HasSomething();
            }

            if (ok)
            {
                _load = c;
                DoPositionKeep();
            }
            else
            {
                DoAirUnlock();
                DoPositionHide();
            }

            return ok;
        }

        public void DoRelease()
        {
            DoAirUnlock();
            DoPositionHide();
            _load = Color.Transparent;
        }

        public void DoAirLock()
        {
            Robots.MainRobot.SetActuatorOnOffValue(_makeVacuum, true);
            Robots.MainRobot.SetActuatorOnOffValue(_openVacuum, false);
        }

        public void DoAirUnlock()
        {
            Robots.MainRobot.SetActuatorOnOffValue(_makeVacuum, false);
            Robots.MainRobot.SetActuatorOnOffValue(_openVacuum, true);
        }

        public void DoPositionHide()
        {
            _finger.SendPosition(_finger.PositionHide);
        }

        public void DoPositionKeep()
        {
            _finger.SendPosition(_finger.PositionKeep);
        }

        public void DoPositionGrab()
        {
            _finger.SendPosition(_finger.PositionGrab);
        }

        public bool HasSomething()
        {
            return Robots.MainRobot.ReadSensorOnOff(_pressure);
        }
    }
}<file_sep>/GoBot/Composants/Battery.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

namespace Composants
{
    public partial class Battery : PictureBox
    {
        private System.Windows.Forms.Timer timerBlink;

        private double currentVoltage;
        private State currentState;

        public enum State
        {
            High,
            MidHigh,
            Average,
            Low,
            VeryLow,
            Absent
        }

        /// <summary>
        /// Définit la tension au dessus de laquelle la tension est considérée comme élevée
        /// </summary>
        public double VoltageHigh { get; set; }

        /// <summary>
        /// Définit la tension au dessus de laquelle la tension est considérée comme moyenne
        /// </summary>
        public double VoltageAverage { get; set; }

        /// <summary>
        /// Définit la tension au dessus de laquelle la tension est considérée comme basse
        /// </summary>
        public double VoltageLow { get; set; }

        /// <summary>
        /// Définit la tension au dessus de laquelle la tension est considérée comme critique
        /// </summary>
        public double VoltageVeryLow { get; set; }
        
        public Battery()
        {
            InitializeComponent();
            timerBlink = new System.Windows.Forms.Timer();
            timerBlink.Interval = 300;
            timerBlink.Tick += new EventHandler(timer_Tick);
            CurrentVoltage = -1;
            CurrentState = State.Absent;
            VoltageLow = 0;
            VoltageAverage = 0;
            VoltageHigh = 0;
            toolTip.SetToolTip(this, "0V");
        }

        /// <summary>
        /// Permet d'obtenir ou de définir le visuel actuel de la batterie
        /// </summary>
        public State CurrentState
        {
            get { return currentState; }
            set
            {
                Image img;

                currentState = value;

                if (Enabled)
                {
                    switch (currentState)
                    {
                        case State.High:
                            img = Properties.Resources.BatHigh;
                            ChangeImage(Properties.Resources.BatHigh, false);
                            break;
                        case State.MidHigh:
                            ChangeImage(Properties.Resources.BatMidHigh, false);
                            break;
                        case State.Average:
                            ChangeImage(Properties.Resources.BatMid, false);
                            break;
                        case State.Low:
                            img = Properties.Resources.BatLow;
                            ChangeImage(Properties.Resources.BatLow, true);
                            break;
                        case State.VeryLow:
                            img = Properties.Resources.BatCrit;
                            ChangeImage(Properties.Resources.BatCrit, true);
                            break;
                        case State.Absent:
                            ChangeImage(Properties.Resources.BatNo);
                            break;
                    }
                }
                else
                    ChangeImage(Properties.Resources.BatNo, false);
            }
        }

        /// <summary>
        /// Permet de définir ou d'obtenir la tension qui détermine le visuel
        /// </summary>
        public double CurrentVoltage
        {
            get { return currentVoltage; }
            set
            {
                toolTip.SetToolTip(this, currentVoltage + "V");

                currentVoltage = value;

                if (value > VoltageHigh)
                    CurrentState = State.High;
                else if (value > VoltageAverage)
                    CurrentState = State.Average;
                else if (value > VoltageLow)
                    CurrentState = State.Low;
                else if (value >= VoltageVeryLow)
                    CurrentState = State.VeryLow;
            }
        }

        private void ChangeImage(Image img, bool blink = false)
        {
            timerBlink.Stop();
            Visible = true;

            Image = img;

            if (blink)
                timerBlink.Start();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            if (Visible)
                Visible = false;
            else
            {
                Visible = true;
                timerBlink.Stop();
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelSensorsOnOff.cs
﻿using System;
using System.Drawing;
using System.Windows.Forms;

namespace GoBot.IHM
{
    public partial class PanelSensorsOnOff : UserControl
    {
        public PanelSensorsOnOff()
        {
            InitializeComponent();
        }

        private void PanelSensorsOnOff_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                int y = 20;

                foreach (SensorOnOffID sensor in Enum.GetValues(typeof(SensorOnOffID)))
                {
                    PanelSensorOnOff panel = new PanelSensorOnOff();
                    panel.SetBounds(5, y, grpSensors.Width - 10, panel.Height);
                    panel.SetSensor(sensor);
                    y += panel.Height;
                    grpSensors.Controls.Add(panel);
                }

                grpSensors.Height = y + 5;
                this.Height = grpSensors.Bottom + 3;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Enums.cs
﻿namespace GoBot
{
    public enum SensAR
    {
        Avant = 0,
        Arriere = 1
    }

    public enum SensGD
    {
        Gauche = 2,
        Droite = 3
    }

    public enum StopMode
    {
        Freely = 0x00,
        Smooth = 0x01,
        Abrupt = 0x02
    }

    public enum ServomoteurID
    {
        Unused00 = 0,
        LockerRight = 1,
        LockerLeft = 2,
        Unused03 = 3,

        PushArmRight = 4,
        FlagRight = 5,
        Unused06 = 6,
        Unused07 = 7,

        Clamp1 = 8,
        Clamp2 = 9,
        Tilter = 10,
        Lifter = 11,

        Unused12 = 12,
        Unused13 = 13,
        FlagLeft = 14,
        PushArmLeft = 15,

        Clamp3 = 16,
        Unused17 = 17,
        Clamp4 = 18,
        Clamp5 = 19,

        GrabberLeft = 20,
        FingerRight = 21,
        FingerLeft = 22,
        GrabberRight = 23,

        // Petit robot (astuce no >= 100 = modulo)
        ElevatorRight = 100,
        Unued101 = 101,
        ArmLeft = 102,
        ElevatorLeft = 103,

        ElevatorBack = 104,
        Retractor = 105,
        ArmRight = 106,
        Selector = 107
    }

    public enum MotorID
    {
        ElevatorLeft = 0x00,
        ElevatorRight = 0x01,
        AvailableOnRecIO2 = 0x02,
        AvailableOnRecIO3 = 0x03,

        AvailableOnRecMove11 = 0x11,
        AvailableOnRecMove12 = 0x12
    }

    public enum ActuatorOnOffID
    {
        PowerSensorColorBuoyRight = 0x00,
        PowerSensorColorBuoyLeft = 0x01,
        MakeVacuumRightFront = 0x11,
        MakeVacuumLeftFront = 0x12,
        MakeVacuumRightBack = 0x13,
        MakeVacuumLeftBack = 0x14,
        OpenVacuumRightFront = 0x21,
        OpenVacuumLeftFront = 0x20,
        OpenVacuumRightBack = 0x22,
        OpenVacuumLeftBack = 0x23,

        // Petit robot
        MakeVacuumLeft = 0x24,
        MakeVacuumRight = 0x25,
        MakeVacuumBack = 0x26,
        OpenVacuumLeft = 0x27,
        OpenVacuumRight = 0x28,
        OpenVacuumBack = 0x29
    }

    public enum SensorOnOffID
    {
        StartTrigger = 0x10,
        PressureSensorRightFront = 0x11,
        PressureSensorLeftFront = 0x12,
        PressureSensorRightBack = 0x13,
        PressureSensorLeftBack = 0x14,
        PresenceBuoyRight = 0x20,

        // Petit robot
        PressureSensorLeft = 0x21,
        PressureSensorRight = 0x22,
        PressureSensorBack = 0x23
    }

    public enum SensorColorID
    {
        BuoyRight = 0,
        BuoyLeft = 1
    }

    public enum CodeurID
    {
        Manuel = 1
    }

    public enum BaliseID
    {
        Principale = 0
    }

    public enum LidarID
    {
        Ground = 0,
        Avoid = 1
    }

    public enum Board
    {
        PC = 0xA1,
        RecMove = 0xC1,
        RecIO = 0xC4,
        RecCan = 0xC5
    }

    public enum ServoBaudrate
    {
        b1000000 = 1,
        b500000 = 3,
        b400000 = 4,
        b250000 = 7,
        b200000 = 9,
        b115200 = 16,
        b57600 = 34,
        b19200 = 103,
        b9600 = 207
    }

    public static class EnumExtensions
    {
        public static int Factor(this SensAR sens)
        {
            return sens == SensAR.Avant ? 1 : -1;
        }

        public static int Factor(this SensGD sens)
        {
            return sens == SensGD.Droite ? 1 : -1;
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelAnalogique.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Timers;
using GoBot.Communications;
using GoBot.Threading;

namespace GoBot.IHM
{
    public partial class PanelAnalogique : UserControl
    {
        private ThreadLink _linkPolling, _linkDraw;

        public PanelAnalogique()
        {
            InitializeComponent();
        }

        public Board Carte { get; set; }

        private void PanelAnalogique_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                ctrlGraphique.BorderVisible = true;
                ctrlGraphique.BorderColor = Color.LightGray;
            }
        }

        void AskValues()
        {
            Robots.MainRobot.ReadAnalogicPins(Carte, true);

            if (Robots.MainRobot.AnalogicPinsValue[Carte] != null)
            {
                this.InvokeAuto(() =>
                {
                    List<double> values = Robots.MainRobot.AnalogicPinsValue[Carte];
                    lblAN1.Text = values[0].ToString("0.0000") + " V";
                    lblAN2.Text = values[1].ToString("0.0000") + " V";
                    lblAN3.Text = values[2].ToString("0.0000") + " V";
                    lblAN4.Text = values[3].ToString("0.0000") + " V";
                    lblAN5.Text = values[4].ToString("0.0000") + " V";
                    lblAN6.Text = values[5].ToString("0.0000") + " V";
                    lblAN7.Text = values[6].ToString("0.0000") + " V";
                    lblAN8.Text = values[7].ToString("0.0000") + " V";
                    lblAN9.Text = values[8].ToString("0.0000") + " V";

                    ctrlGraphique.AddPoint("AN1", values[0], ColorPlus.FromHsl(360 / 9 * 0, 1, 0.4));
                    ctrlGraphique.AddPoint("AN2", values[1], ColorPlus.FromHsl(360 / 9 * 1, 1, 0.4));
                    ctrlGraphique.AddPoint("AN3", values[2], ColorPlus.FromHsl(360 / 9 * 2, 1, 0.4));
                    ctrlGraphique.AddPoint("AN4", values[3], ColorPlus.FromHsl(360 / 9 * 3, 1, 0.4));
                    ctrlGraphique.AddPoint("AN5", values[4], ColorPlus.FromHsl(360 / 9 * 4, 1, 0.4));
                    ctrlGraphique.AddPoint("AN6", values[5], ColorPlus.FromHsl(360 / 9 * 5, 1, 0.4));
                    ctrlGraphique.AddPoint("AN7", values[6], ColorPlus.FromHsl(360 / 9 * 6, 1, 0.4));
                    ctrlGraphique.AddPoint("AN8", values[7], ColorPlus.FromHsl(360 / 9 * 7, 1, 0.4));
                    ctrlGraphique.AddPoint("AN9", values[8], ColorPlus.FromHsl(360 / 9 * 8, 1, 0.4));
                });
            }
        }

        private void switchBouton_ValueChanged(object sender, bool value)
        {
            if(value)
            {
                _linkPolling = ThreadManager.CreateThread(link => AskValues());
                _linkPolling.Name = "Ports analogiques " + Carte.ToString();
                _linkPolling.StartInfiniteLoop(50);

                _linkDraw = ThreadManager.CreateThread(link => ctrlGraphique.DrawCurves());
                _linkDraw.Name = "Graph ports analogiques " + Carte.ToString();
                _linkDraw.StartInfiniteLoop(100);
            }
            else
            {
                _linkPolling.Cancel();
                _linkPolling.WaitEnd();
                _linkPolling = null;

                _linkDraw.Cancel();
                _linkDraw.WaitEnd();
                _linkDraw = null;
            }
        }

        private void boxAN1_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN1", boxIOAN1.Checked);
        }

        private void boxAN2_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN2", boxIOAN2.Checked);
        }

        private void boxAN3_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN3", boxIOAN3.Checked);
        }

        private void boxAN4_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN4", boxIOAN4.Checked);
        }

        private void boxAN5_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN5", boxIOAN5.Checked);
        }

        private void boxAN6_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN6", boxIOAN6.Checked);
        }

        private void boxAN7_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN7", boxIOAN7.Checked);
        }

        private void boxAN8_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN8", boxIOAN8.Checked);
        }

        private void boxAN9_CheckedChanged(object sender, EventArgs e)
        {
            ctrlGraphique.ShowCurve("AN9", boxIOAN9.Checked);
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Elements/PanelBoardCanServos.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GoBot.Threading;
using System.Threading;
using GoBot.Devices.CAN;
using GoBot.Devices;
using GoBot.Communications.CAN;

namespace GoBot.IHM
{
    public partial class PanelBoardCanServos : UserControl
    {
        private CanBoard _boardID;

        private CanServo _servo1, _servo2, _servo3, _servo4;

        public PanelBoardCanServos()
        {
            InitializeComponent();
        }

        public void SetBoardID(CanBoard board)
        {
            _boardID = board;

            if (_servo1 != null)
            {
                _servo1.TorqueAlert -= PanelBoardCanServos_TorqueAlert1;
                _servo2.TorqueAlert -= PanelBoardCanServos_TorqueAlert2;
                _servo3.TorqueAlert -= PanelBoardCanServos_TorqueAlert3;
                _servo4.TorqueAlert -= PanelBoardCanServos_TorqueAlert4;
            }

            _servo1 = AllDevices.CanServos[(ServomoteurID)(((int)_boardID - 1) * 4 + 0)];
            _servo2 = AllDevices.CanServos[(ServomoteurID)(((int)_boardID - 1) * 4 + 1)];
            _servo3 = AllDevices.CanServos[(ServomoteurID)(((int)_boardID - 1) * 4 + 2)];
            _servo4 = AllDevices.CanServos[(ServomoteurID)(((int)_boardID - 1) * 4 + 3)];

            lblTitle.Text = _boardID.ToString();
            lblServo1.Text = Parse(_servo1.ID);
            lblServo2.Text = Parse(_servo2.ID);
            lblServo3.Text = Parse(_servo3.ID);
            lblServo4.Text = Parse(_servo4.ID);

            if (!Execution.DesignMode)
            {
                _servo1.TorqueAlert += PanelBoardCanServos_TorqueAlert1;
                _servo2.TorqueAlert += PanelBoardCanServos_TorqueAlert2;
                _servo3.TorqueAlert += PanelBoardCanServos_TorqueAlert3;
                _servo4.TorqueAlert += PanelBoardCanServos_TorqueAlert4;
            }
        }

        private void PanelBoardCanServos_TorqueAlert1()
        {
            ThreadManager.CreateThread(link =>
            {
                lblServo1.InvokeAuto(() => lblServo1.ForeColor = Color.Red);
                Thread.Sleep(2000);
                lblServo1.InvokeAuto(() => lblServo1.ForeColor = Color.Black);
            }).StartThread();
        }

        private void PanelBoardCanServos_TorqueAlert2()
        {
            ThreadManager.CreateThread(link =>
            {
                lblServo2.InvokeAuto(() => lblServo2.ForeColor = Color.Red);
                Thread.Sleep(2000);
                lblServo2.InvokeAuto(() => lblServo2.ForeColor = Color.Black);
            }).StartThread();
        }

        private void PanelBoardCanServos_TorqueAlert3()
        {
            ThreadManager.CreateThread(link =>
            {
                lblServo3.InvokeAuto(() => lblServo3.ForeColor = Color.Red);
                Thread.Sleep(2000);
                lblServo3.InvokeAuto(() => lblServo3.ForeColor = Color.Black);
            }).StartThread();
        }

        private void PanelBoardCanServos_TorqueAlert4()
        {
            ThreadManager.CreateThread(link =>
            {
                lblServo4.InvokeAuto(() => lblServo4.ForeColor = Color.Red);
                Thread.Sleep(2000);
                lblServo4.InvokeAuto(() => lblServo4.ForeColor = Color.Black);
            }).StartThread();
        }

        private String Parse(ServomoteurID servo)
        {
            return servo.ToString().Replace("Unused", "");
        }

        public delegate void ServoClickDelegate(ServomoteurID servoNo);
        public event ServoClickDelegate ServoClick;

        private void lblServo1_Click(object sender, EventArgs e)
        {
            ServoClick?.Invoke((ServomoteurID)(((int)_boardID - 1) * 4 + 0));
        }

        private void lblServo2_Click(object sender, EventArgs e)
        {
            ServoClick?.Invoke((ServomoteurID)(((int)_boardID - 1) * 4 + 1));
        }

        private void lblServo3_Click(object sender, EventArgs e)
        {
            ServoClick?.Invoke((ServomoteurID)(((int)_boardID - 1) * 4 + 2));
        }

        private void lblServo4_Click(object sender, EventArgs e)
        {
            ServoClick?.Invoke((ServomoteurID)(((int)_boardID - 1) * 4 + 3));
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/ITimeableAction.cs
﻿using System;

namespace GoBot.Actions
{
    public interface ITimeableAction : IAction
    {
        TimeSpan Duration { get; }
    }
}
<file_sep>/GoBot/Composants/ColorPickup.cs
﻿using System.Drawing;
using System.Windows.Forms;

namespace Composants
{
    public partial class ColorPickup : PictureBox
    {
        public delegate void ColorDelegate(Color color);

        /// <summary>
        /// Se produit lorsque la souris passe sur une nouvelle couleur
        /// </summary>
        public event ColorDelegate ColorHover;

        /// <summary>
        /// Se produit lorsque la souris clique sur une couleur
        /// </summary>
        public event ColorDelegate ColorClick;

        public ColorPickup()
        {
            InitializeComponent();
            this.Image = Properties.Resources.Rainbow2D;
            this.Width = this.Image.Width;
            this.Height = this.Image.Height;
            this.MouseMove += ColorPickup_MouseMove;
            this.MouseClick += ColorPickup_MouseClick;
        }

        void ColorPickup_MouseClick(object sender, MouseEventArgs e)
        {
            ColorClick?.Invoke(GetColor(new Point(e.X, e.Y)));
        }

        void ColorPickup_MouseMove(object sender, MouseEventArgs e)
        {
            ColorHover?.Invoke(GetColor(new Point(e.X, e.Y)));
        }

        private Color GetColor(Point pos)
        {
            return Properties.Resources.Rainbow2D.GetPixel(pos.X, pos.Y);
        }
    }
}
<file_sep>/GoBot/GoBot/MinimumDelay.cs
﻿using System.Diagnostics;
using System.Threading;

namespace GoBot
{
    class MinimumDelay
    {
        private Stopwatch _lastCommandTime;
        private int _delay;

        public MinimumDelay(int delayMs)
        {
            _delay = delayMs;
        }

        public void Wait()
        {
            if (_lastCommandTime != null)
            {
                while (_lastCommandTime.ElapsedMilliseconds < _delay)
                    Thread.Sleep(1);
            }

            _lastCommandTime = Stopwatch.StartNew();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Forms/DebugLidar.cs
﻿using GoBot.Devices;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GoBot.IHM.Forms
{
    public partial class DebugLidar : Form
    {
        public DebugLidar()
        {
            InitializeComponent();
        }

        private void btnAsk_Click(object sender, EventArgs e)
        {
            Stopwatch sw = Stopwatch.StartNew();
            txtMessage.Text = ((HokuyoRec)AllDevices.LidarGround).ReadMessage();
            lblTime.Text = sw.ElapsedMilliseconds.ToString() + "ms";
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageServomotors.cs
﻿using System;
using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PageServomotors : UserControl
    {
        public PageServomotors()
        {
            InitializeComponent();
        }

        private void panelCan_ServoClick(ServomoteurID servoNo)
        {
            panelServoCan.SetServo(servoNo);
        }

        private void PageServomotors_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                panelBoardCanServos1.SetBoardID(Communications.CAN.CanBoard.CanServo1);
                panelBoardCanServos2.SetBoardID(Communications.CAN.CanBoard.CanServo2);
                panelBoardCanServos3.SetBoardID(Communications.CAN.CanBoard.CanServo3);
                panelBoardCanServos4.SetBoardID(Communications.CAN.CanBoard.CanServo4);
                panelBoardCanServos5.SetBoardID(Communications.CAN.CanBoard.CanServo5);
                panelBoardCanServos6.SetBoardID(Communications.CAN.CanBoard.CanServo6);
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Movements/MovementRandomPickup.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.BoardContext;
using GoBot.GameElements;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace GoBot.Movements
{
    class MovementRandomPickup : Movement
    {
        private bool _hasBuoys = true;
        private GameElementZone _zone;

        public MovementRandomPickup(GameElementZone zone)
        {
            _zone = zone;

            Positions.Add(new Position(-45, new RealPoint(1050, 400)));
            Positions.Add(new Position(-45 + 180, new RealPoint(1950, 400)));
        }

        public override bool CanExecute => _hasBuoys;

        public override int Score => 0;

        public override double Value => _hasBuoys ? 1 : 0;

        public override GameElement Element => _zone;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => Color.White;

        protected override void MovementBegin()
        {
        }

        protected override bool MovementCore()
        {
            bool found = true;

            while (found && Actionneurs.Actionneur.ElevatorLeft.CanStoreMore)
                found = Actionneurs.Actionneur.ElevatorLeft.DoSearchBuoy(Buoy.Green, new Circle(_zone.Position, _zone.HoverRadius));

            while (found && Actionneurs.Actionneur.ElevatorRight.CanStoreMore)
                found = Actionneurs.Actionneur.ElevatorRight.DoSearchBuoy(Buoy.Red, new Circle(_zone.Position, _zone.HoverRadius));

            _hasBuoys = false;

            return found;
        }

        protected override void MovementEnd()
        {
            Actionneurs.Actionneur.ElevatorLeft.DoStoreActuators();
            Actionneurs.Actionneur.ElevatorRight.DoStoreActuators();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageTable.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

using Geometry;
using Geometry.Shapes;
using GoBot.Threading;
using GoBot.BoardContext;
using GoBot.GameElements;
using GoBot.Strategies;
using GoBot.Movements;

namespace GoBot.IHM.Pages
{

    public partial class PageTable : UserControl
    {
        private static ThreadLink _linkDisplay;

        public static GameBoard Plateau { get; set; }

        public PageTable()
        {
            InitializeComponent();
            Plateau = new GameBoard();
            GameBoard.ScoreChange += new GameBoard.ScoreChangeDelegate(Plateau_ScoreChange);
            GameBoard.MyColorChange += GameBoard_MyColorChange;
            Dessinateur.TableDessinee += Dessinateur_TableDessinee;

            btnColorLeft.BackColor = GameBoard.ColorLeftBlue;
            btnColorRight.BackColor = GameBoard.ColorRightYellow;

            checkedListBox.SetItemChecked(0, true);
            checkedListBox.SetItemChecked(1, true);
            checkedListBox.SetItemChecked(5, true);

            toolTip.SetToolTip(btnTeleportRPFace, "Téléportation de face");
            toolTip.SetToolTip(btnTeleportRPCentre, "Téléportation de centre");
            toolTip.SetToolTip(btnPathRPFace, "Path finding de face");
            toolTip.SetToolTip(btnPathRPCentre, "Path finding du centre");
        }

        private void GameBoard_MyColorChange(object sender, EventArgs e)
        {
            picColor.BackColor = GameBoard.MyColor;
        }

        void Dessinateur_TableDessinee(Image img)
        {
            this.InvokeAuto(() => pictureBoxTable.Image = img);
        }

        void Plateau_ScoreChange(int score)
        {
            this.InvokeAuto(() => lblScore.Text = score.ToString());
        }

        void DisplayInfos()
        {
            this.InvokeAuto(() =>
            {
                Position pos = Robots.MainRobot.Position;
                lblPosX.Text = pos.Coordinates.X.ToString("0.00");
                lblPosY.Text = pos.Coordinates.Y.ToString("0.00");
                lblPosTheta.Text = pos.Angle.InDegrees.ToString("0.00") + "°";
            });

            if (GameBoard.Strategy != null)
            {
                TimeSpan tempsRestant = GameBoard.Strategy.TimeBeforeEnd;
                if (tempsRestant.TotalMilliseconds <= 0)
                    tempsRestant = new TimeSpan(0);

                Color couleur;
                if (tempsRestant.TotalSeconds > GameBoard.Strategy.MatchDuration.TotalSeconds / 2)
                    couleur = Color.FromArgb((int)((GameBoard.Strategy.MatchDuration.TotalSeconds - tempsRestant.TotalSeconds) * (150.0 / (GameBoard.Strategy.MatchDuration.TotalSeconds / 2.0))), 150, 0);
                else
                    couleur = Color.FromArgb(150, 150 - (int)((GameBoard.Strategy.MatchDuration.TotalSeconds / 2.0 - tempsRestant.TotalSeconds) * (150.0 / (GameBoard.Strategy.MatchDuration.TotalSeconds / 2.0))), 0);

                this.InvokeAuto(() =>
                {
                    lblSecondes.Text = tempsRestant.TotalSeconds.ToString("0");
                    lblSecondes.ForeColor = couleur;
                });
            }
        }

        private void btnAffichage_Click(object sender, EventArgs e)
        {
            if (btnAffichage.Text == "Lancer l'affichage")
            {
                _linkDisplay = ThreadManager.CreateThread(link => DisplayInfos());
                _linkDisplay.Name = "Affichage des données";
                _linkDisplay.StartInfiniteLoop(new TimeSpan(0, 0, 0, 0, 100));

                Dessinateur.Start();

                btnAffichage.Text = "Stopper l'affichage";
                btnAffichage.Image = GoBot.Properties.Resources.Pause16;
            }
            else
            {
                _linkDisplay?.Cancel();
                _linkDisplay?.WaitEnd();
                _linkDisplay = null;

                Dessinateur.Stop();

                btnAffichage.Text = "Lancer l'affichage";
                btnAffichage.Image = GoBot.Properties.Resources.Play16;
            }
        }

        DateTime dateCapture = DateTime.Now;
        Semaphore semMove = new Semaphore(1, 1);

        Random rand = new Random();
        private void pictureBoxTable_MouseMove(object sender, MouseEventArgs e)
        {
            semMove.WaitOne();
            Dessinateur.PositionCurseur = pictureBoxTable.PointToClient(MousePosition);

            if (pSelected != -1)
            {
                pointsPolaires[pSelected] = Dessinateur.Scale.ScreenToRealPosition(e.Location);

                trajectoirePolaire = BezierCurve.GetPoints(pointsPolaires, (int)(numNbPoints.Value));//((int)pointsPolaires[0].Distance(pointsPolaires[pointsPolaires.Count - 1])) / 50);
                Dessinateur.TrajectoirePolaire = trajectoirePolaire;
                Dessinateur.PointsPolaire = pointsPolaires;
            }

            if (boxSourisObstacle.Checked)
            {
                if ((DateTime.Now - dateCapture).TotalMilliseconds > 50)
                {
                    dateCapture = DateTime.Now;

                    Point p = Dessinateur.Scale.ScreenToRealPosition(pictureBoxTable.PointToClient(MousePosition));
                    List<RealPoint> positions = new List<RealPoint>();

                    positions.Add(Dessinateur.Scale.ScreenToRealPosition(pictureBoxTable.PointToClient(MousePosition)));
                    GameBoard.SetOpponents(positions);
                    //SuiviBalise.MajPositions(positions, Plateau.Enchainement == null || Plateau.Enchainement.DebutMatch == null);
                }
            }
            else
            {
                RealPoint posOnTable = Dessinateur.Scale.ScreenToRealPosition(pictureBoxTable.PointToClient(MousePosition));
                lblX.Text = posOnTable.X.ToString("0");
                lblY.Text = posOnTable.Y.ToString("0");

                bool hoverElement = false;

                foreach (GameElement element in GameBoard.Elements)
                {
                    if (posOnTable.Distance(element.Position) < element.HoverRadius)
                    {
                        element.IsHover = true;
                        hoverElement = true;
                    }
                    else
                        element.IsHover = false;
                }

                if (hoverElement)
                    this.Cursor = Cursors.Hand;
                else
                    this.Cursor = Cursors.Arrow;

                //System.Threading.Tasks.Task.Factory.StartNew(() => ChercheTraj(new Position(Robots.GrosRobot.Position)));
            }

            semMove.Release();
        }

        public void ThreadAction()
        {
            if (!move.Execute())
            {
#if DEBUG
                MessageBox.Show("Echec");
#endif
            }
            move = null;
        }

        Movement move;

        private void btnReset_Click(object sender, EventArgs e)
        {
            // Todo

            GameBoard.Score = 0;
        }

        private void CheckElementClick()
        {
            foreach (GameElement element in GameBoard.Elements)
                if (element.IsHover)
                    ThreadManager.CreateThread(link =>
                    {
                        link.Name = "Action " + element.ToString();
                        element.ClickAction();
                    }).StartThread();
        }

        //MouseEventArgs ev;

        private void btnGo_Click(object sender, EventArgs e)
        {
            if (Config.CurrentConfig.IsMiniRobot)
                GameBoard.Strategy = new StrategyMini();
            else
                GameBoard.Strategy = new StrategyMatch();

            GameBoard.Strategy.ExecuteMatch();
        }

        private void PanelTable_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
            }
        }

        public void StartDisplay()
        {
            btnAffichage_Click(null, null);
        }

        private int pSelected = -1;
        private void pictureBoxTable_MouseDown(object sender, MouseEventArgs e)
        {
            Dessinateur.positionDepart = Dessinateur.Scale.ScreenToRealPosition(pictureBoxTable.PointToClient(MousePosition));
            Dessinateur.MouseClicked = true;

            if (Dessinateur.modeCourant == Dessinateur.MouseMode.TrajectoirePolaire)
            {
                moveMouse = false;
                Point pClic = e.Location;
                for (int i = 0; i < pointsPolaires.Count; i++)
                {
                    Point pPolaire = Dessinateur.Scale.RealToScreenPosition(pointsPolaires[i]);
                    if (new RealPoint(pClic).Distance(new RealPoint(pPolaire)) <= 3)
                    {
                        moveMouse = true;
                        pSelected = i;
                    }
                }
            }
        }

        List<RealPoint> trajectoirePolaire;
        List<RealPoint> pointsPolaires;

        private void pictureBoxTable_MouseUp(object sender, MouseEventArgs e)
        {
            if (pSelected != -1)
                pSelected = -1;

            if (Dessinateur.modeCourant == Dessinateur.MouseMode.PositionCentre || Dessinateur.modeCourant == Dessinateur.MouseMode.TeleportCentre)
            {
                Direction traj = Maths.GetDirection(Dessinateur.positionDepart, Dessinateur.Scale.ScreenToRealPosition(pictureBoxTable.PointToClient(MousePosition)));

                positionArrivee = new Position(new AnglePosition(-traj.angle), Dessinateur.positionDepart);

                if (Dessinateur.modeCourant == Dessinateur.MouseMode.PositionCentre)
                    ThreadManager.CreateThread(link => ThreadTrajectory(link)).StartThread();
                else
                    Robots.MainRobot.SetAsservOffset(positionArrivee);

                Dessinateur.modeCourant = Dessinateur.MouseMode.None;
            }
            else if (Dessinateur.modeCourant == Dessinateur.MouseMode.PositionFace || Dessinateur.modeCourant == Dessinateur.MouseMode.TeleportFace)
            {
                Point positionFin = pictureBoxTable.PointToClient(MousePosition);

                Direction traj = Maths.GetDirection(Dessinateur.positionDepart, Dessinateur.Scale.ScreenToRealPosition(pictureBoxTable.PointToClient(MousePosition)));

                Point pointOrigine = Dessinateur.positionDepart;
                Position departRecule = new Position(new AnglePosition(-traj.angle), pointOrigine);
                departRecule.Move(-Robots.MainRobot.LenghtFront);
                departRecule = new Position(new AnglePosition(-traj.angle), new RealPoint(departRecule.Coordinates.X, departRecule.Coordinates.Y));
                positionArrivee = departRecule;

                if (Dessinateur.modeCourant == Dessinateur.MouseMode.PositionFace)
                    ThreadManager.CreateThread(link => ThreadTrajectory(link)).StartThread();
                else
                    Robots.MainRobot.SetAsservOffset(positionArrivee);

                Dessinateur.modeCourant = Dessinateur.MouseMode.None;
            }

            Dessinateur.MouseClicked = false;
        }

        private void btnAleatoire_Click(object sender, EventArgs e)
        {
            GameBoard.Strategy = new StrategyRandomMoves();
            GameBoard.Strategy.ExecuteMatch();
        }
        Position positionArrivee;

        private void ThreadTrajectory(ThreadLink link)
        {
            link.RegisterName();

            this.InvokeAuto(() => btnPathRPCentre.Enabled = false);

            Robots.MainRobot.GoToPosition(new Position(positionArrivee.Angle.InDegrees, positionArrivee.Coordinates));

            this.InvokeAuto(() => btnPathRPCentre.Enabled = true);
        }

        #region GroupBox Déplacements

        private void btnPathRPCentre_Click(object sender, EventArgs e)
        {
            Dessinateur.positionDepart = null;
            if (Dessinateur.modeCourant != Dessinateur.MouseMode.PositionCentre)
                Dessinateur.modeCourant = Dessinateur.MouseMode.PositionCentre;
            else
                Dessinateur.modeCourant = Dessinateur.MouseMode.None;
        }

        private void btnPathRPFace_Click(object sender, EventArgs e)
        {
            Dessinateur.positionDepart = null;
            if (Dessinateur.modeCourant != Dessinateur.MouseMode.PositionFace)
                Dessinateur.modeCourant = Dessinateur.MouseMode.PositionFace;
            else
                Dessinateur.modeCourant = Dessinateur.MouseMode.None;
        }

        private void btnTeleportRPCentre_Click(object sender, EventArgs e)
        {
            Dessinateur.positionDepart = null;
            if (Dessinateur.modeCourant != Dessinateur.MouseMode.TeleportCentre)
                Dessinateur.modeCourant = Dessinateur.MouseMode.TeleportCentre;
            else
                Dessinateur.modeCourant = Dessinateur.MouseMode.None;
        }

        private void btnTeleportRPFace_Click(object sender, EventArgs e)
        {
            Dessinateur.positionDepart = null;
            if (Dessinateur.modeCourant != Dessinateur.MouseMode.TeleportFace)
                Dessinateur.modeCourant = Dessinateur.MouseMode.TeleportFace;
            else
                Dessinateur.modeCourant = Dessinateur.MouseMode.None;
        }

        #endregion

        private void checkedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            String ligne = (String)checkedListBox.Items[e.Index];
            if (ligne == "Plateau")
                Dessinateur.AfficheTable = e.NewValue == CheckState.Checked;
            if (ligne == "Plateau perspective")
                Dessinateur.AfficheTableRelief = e.NewValue == CheckState.Checked;
            if (ligne == "Obstacles")
                Dessinateur.AfficheObstacles = e.NewValue == CheckState.Checked;
            if (ligne == "Elements de jeu")
                Dessinateur.AfficheElementsJeu = e.NewValue == CheckState.Checked;
            if (ligne == "Graph (noeuds)")
                Dessinateur.AfficheGraph = e.NewValue == CheckState.Checked;
            if (ligne == "Graph (arcs)")
                Dessinateur.AfficheGraphArretes = e.NewValue == CheckState.Checked;
            if (ligne == "Coûts mouvements")
                Dessinateur.AfficheCoutsMouvements = e.NewValue == CheckState.Checked;
            if (ligne == "Calcul path finding")
                Config.CurrentConfig.AfficheDetailTraj = e.NewValue == CheckState.Checked ? 30 : 0;
            if (ligne == "Historique trajectoire")
                Dessinateur.AfficheHistoriqueCoordonnees = e.NewValue == CheckState.Checked;
        }

        private void btnZoneDepart_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(link => GoToDepart(link)).StartThread();
        }

        public void GoToDepart(ThreadLink link)
        {
            link.RegisterName();
            Robots.MainRobot.GoToPosition(Recalibration.StartPosition);
        }

        private void btnStratNul_Click(object sender, EventArgs e)
        {
            GameBoard.Strategy = new StrategyEmpty();
            GameBoard.Strategy.ExecuteMatch();
        }

        private void btnStratTest_Click(object sender, EventArgs e)
        {
            GameBoard.Strategy = new StrategyRoundTrip();
            GameBoard.Strategy.ExecuteMatch();
        }

        private void btnTestAsser_Click(object sender, EventArgs e)
        {
            ThreadLink th = ThreadManager.CreateThread(link => TestAsser(link));
            th.StartThread();
        }

        private void TestAsser(ThreadLink link)
        {
            link.RegisterName();

            Robots.MainRobot.MoveForward(2000);
            Robots.MainRobot.PivotRight(270);
            Robots.MainRobot.MoveForward(200);
            Robots.MainRobot.PivotRight(10);
            Robots.MainRobot.MoveForward(100);
            Robots.MainRobot.PivotRight(10);
            Robots.MainRobot.MoveForward(100);
            Robots.MainRobot.PivotRight(10);
            Robots.MainRobot.MoveForward(100);
            Robots.MainRobot.MoveBackward(1000);
            Robots.MainRobot.PivotLeft(90);
            Robots.MainRobot.MoveBackward(500);
            Robots.MainRobot.PivotLeft(10);
            Robots.MainRobot.MoveForward(1000);
            Robots.MainRobot.PivotLeft(10);
            Robots.MainRobot.MoveForward(100);
            Robots.MainRobot.PivotLeft(10);
            Robots.MainRobot.MoveForward(100);
            Robots.MainRobot.PivotLeft(10);
            Robots.MainRobot.MoveForward(100);
            Robots.MainRobot.PivotLeft(10);
            Robots.MainRobot.MoveForward(100);

            Robots.MainRobot.GoToPosition(Recalibration.StartPosition);

            Robots.MainRobot.MoveBackward(300);
        }

        private void btnTrajCreer_Click(object sender, EventArgs e)
        {
            Dessinateur.modeCourant = Dessinateur.MouseMode.TrajectoirePolaire;
            pSelected = -1;
            pointsPolaires = new List<RealPoint>();
        }

        private void btnTrajLancer_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.PolarTrajectory(SensAR.Avant, trajectoirePolaire, false);
        }

        bool moveMouse = false;
        private void pictureBoxTable_Click(object sender, EventArgs e)
        {
            if (boxSourisObstacle.Checked)
                boxSourisObstacle.Checked = false;
            else if (!moveMouse && Dessinateur.modeCourant == Dessinateur.MouseMode.TrajectoirePolaire)
            {
                RealPoint point = Dessinateur.Scale.ScreenToRealPosition(pictureBoxTable.PointToClient(MousePosition));
                //if (pointsPolaires.Count >= 2 && pointsPolaires.Count < 4)
                //    pointsPolaires.Insert(pointsPolaires.Count - 1, point);
                //else if (pointsPolaires.Count < 4)
                pointsPolaires.Add(point);

                if (pointsPolaires.Count > 1)
                {
                    trajectoirePolaire = BezierCurve.GetPoints(pointsPolaires, (int)(numNbPoints.Value));//((int)pointsPolaires[0].Distance(pointsPolaires[pointsPolaires.Count - 1])) / 50);
                    Dessinateur.TrajectoirePolaire = trajectoirePolaire;
                    Dessinateur.PointsPolaire = pointsPolaires;
                }
            }
            else
            {
                CheckElementClick();
            }
        }

        private void ThreadMouvement(Object o)
        {
            Movement m = (Movement)o;
            m.Execute();
        }

        private void ThreadEnchainement(Object o)
        {
            Strategy m = (Strategy)o;
            GameBoard.Strategy = m;
            GameBoard.Strategy.ExecuteMatch();
        }

        private void btnTestScore_Click(object sender, EventArgs e)
        {
            GameBoard.Score++;
        }

        private void btnRestartRecal_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(link =>
            {
                link.Name = "GoToCalibration";
                if (Recalibration.GoToCalibration()) Recalibration.Calibration();
            }).StartThread();
        }

        private void btnColorLeft_Click(object sender, EventArgs e)
        {
            GameBoard.MyColor = GameBoard.ColorLeftBlue;
        }

        private void btnColorRight_Click(object sender, EventArgs e)
        {
            GameBoard.MyColor = GameBoard.ColorRightYellow;
        }

        private void btnCalib_Click(object sender, EventArgs e)
        {
            ThreadManager.CreateThread(link =>
            {
                link.Name = "Calibration";
                Recalibration.Calibration();
            }).StartThread();
        }
    }
}
<file_sep>/GoBot/GoBot/Movements/MovementGreenDropoff.cs
﻿using Geometry.Shapes;
using GoBot.Actionneurs;
using GoBot.BoardContext;
using GoBot.GameElements;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace GoBot.Movements
{
    class MovementGreenDropoff : Movement
    {
        private RandomDropOff _randomDropOff;

        public MovementGreenDropoff(RandomDropOff dropoff)
        {
            _randomDropOff = dropoff;

            Positions.Add(new Geometry.Position(90, new RealPoint(_randomDropOff.Position.X + (_randomDropOff.Position.X < 1500 ? 50 : -50), 730)));
        }

        public override bool CanExecute => !_randomDropOff.HasLoadOnTop && Actionneur.Lifter.Loaded;

        public override int Score
        {
            get
            {
                if (!_randomDropOff.HasLoadOnBottom)
                    return 8;
                else
                    return 22 - 8;
            }
        }

        public override double Value => 2;

        public override GameElement Element => _randomDropOff;

        public override Robot Robot => Robots.MainRobot;

        public override Color Color => _randomDropOff.Owner;

        protected override void MovementBegin()
        {
        }

        protected override bool MovementCore()
        {
            Actionneur.Lifter.DoSequenceDropOff();
            _randomDropOff.SetLoadTop(Actionneur.Lifter.Load);
            Actionneur.Lifter.Load = null;
            GameBoard.AddObstacle(new Segment(new RealPoint(_randomDropOff.Position.X - 200, 525), new RealPoint(_randomDropOff.Position.X + 200, 525)));

            return true;
        }

        protected override void MovementEnd()
        {
        }
    }
}
<file_sep>/GoBot/GoBot/PathFinding/PathFinder.cs
﻿using AStarFolder;
using Geometry;
using Geometry.Shapes;
using GoBot.Threading;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using GoBot.BoardContext;

namespace GoBot.PathFinding
{
    static class PathFinder
    {
        private static ThreadLink _linkResetRadius;

        public static List<Node> CheminEnCoursNoeuds { get; private set; }
        public static List<Node> CheminEnCoursNoeudsSimplifyed { get; private set; }
        public static List<Arc> CheminEnCoursArcs { get; private set; }

        public static Segment CheminTest { get; private set; }
        public static IShape ObstacleTeste { get; private set; }
        public static IShape ObstacleProbleme { get; private set; }
        public static List<RealPoint> PointsTrouves { get; private set; }

        private static Trajectory DirectTrajectory(IEnumerable<IShape> obstacles, Position startPos, Position endPos, double securityRadius)
        {
            Segment directLine = new Segment(new RealPoint(startPos.Coordinates), new RealPoint(endPos.Coordinates));

            Trajectory output = null;
            if (obstacles.All(o => o.Distance(directLine) > securityRadius))
            {
                output = new Trajectory();
                output.StartAngle = startPos.Angle;
                output.AddPoint(startPos.Coordinates);
                output.AddPoint(endPos.Coordinates);
                output.EndAngle = endPos.Angle;
            }

            return output;
        }

        public static Trajectory ChercheTrajectoire(Graph graph, IEnumerable<IShape> obstacles, IEnumerable<IShape> opponents, Position startPos, Position endPos, double rayonSecurite, double distanceSecuriteCote)
        {
            Stopwatch sw = Stopwatch.StartNew();
            bool pathFound = false;

            Trajectory output = DirectTrajectory(obstacles.Concat(opponents), startPos, endPos, rayonSecurite);

            if (output != null)
                pathFound = true;
            else
            {
                _linkResetRadius = null;

                double distance;
                bool raccordable = false;

                output = new Trajectory();
                output.StartAngle = startPos.Angle;
                output.EndAngle = endPos.Angle;

                PointsTrouves = new List<RealPoint>();
                PointsTrouves.Add(new RealPoint(startPos.Coordinates));
                output.AddPoint(new RealPoint(startPos.Coordinates));

                lock (graph)
                {
                    Console.WriteLine("Cherche trajectoire début");
                    int nbPointsDepart = 0;
                    int nbPointsArrivee = 0;

                    Node debutNode = null, finNode = null;
                    Node nodeProche = graph.ClosestNode(startPos.Coordinates, out distance);
                    if (distance != 0)
                    {
                        debutNode = new Node(startPos.Coordinates);
                        nbPointsDepart = graph.AddNode(debutNode, obstacles, rayonSecurite);
                    }
                    else
                    {
                        debutNode = nodeProche;
                        nbPointsDepart = debutNode.OutgoingArcs.Count;
                    }

                    //TODO2018 phase d'approche finale pourrie, l'angle final peut se faire à la fin au lieu de à la fin du path finding avant l'approche finale
                    if (nbPointsDepart == 0)
                    {
                        // On ne peut pas partir de là où on est

                        Position positionTestee = new Position(startPos);
                        bool franchissable = true;

                        // Boucle jusqu'à trouver un point qui se connecte au graph jusqu'à 1m devant
                        int i;
                        for (i = 0; i < 100 && !raccordable; i += 1)
                        {
                            positionTestee.Move(10);

                            debutNode = new Node(positionTestee.Coordinates);
                            raccordable = graph.Raccordable(debutNode, obstacles, rayonSecurite);
                        }

                        // Le point à i*10 mm devant nous est reliable au graph, on cherche à l'atteindre
                        if (raccordable)
                        {
                            Segment segmentTest = new Segment(new RealPoint(positionTestee.Coordinates), new RealPoint(startPos.Coordinates));

                            // Test des obstacles

                            foreach (IShape obstacle in obstacles)
                            {
                                if (obstacle.Distance(segmentTest) < distanceSecuriteCote)
                                {
                                    franchissable = false;

                                    // Si l'obstacle génant est un adversaire, on diminue petit à petit son rayon pour pouvoir s'échapper au bout d'un moment
                                    if (opponents.Contains(obstacle) && GameBoard.OpponentRadius > 50)
                                    {
                                        Robots.MainRobot.Historique.Log("Adversaire au contact, impossible de s'enfuir, réduction du périmètre adverse", TypeLog.PathFinding);
                                        GameBoard.OpponentRadius -= 10;
                                        _linkResetRadius?.Cancel();
                                    }
                                }
                            }

                            // Si le semgent entre notre position et le graph relié au graph est parcourable on y va !
                            if (franchissable)
                            {
                                PointsTrouves.Add(new RealPoint(positionTestee.Coordinates));
                                output.AddPoint(new RealPoint(positionTestee.Coordinates));

                                debutNode = new Node(positionTestee.Coordinates);
                                nbPointsDepart = graph.AddNode(debutNode, obstacles, rayonSecurite);
                            }
                        }
                        else
                            franchissable = false;

                        // Si toujours pas, on teste en marche arrière
                        if (!franchissable)
                        {
                            franchissable = true;
                            raccordable = false;
                            nbPointsDepart = 0;
                            positionTestee = new Position(startPos);
                            for (i = 0; i > -100 && !raccordable; i--)
                            {
                                positionTestee.Move(-10);

                                debutNode = new Node(positionTestee.Coordinates);
                                raccordable = graph.Raccordable(debutNode, obstacles, rayonSecurite);
                            }

                            // Le point à i*10 mm derrière nous est reliable au graph, on cherche à l'atteindre
                            if (raccordable)
                            {
                                Segment segmentTest = new Segment(new RealPoint(positionTestee.Coordinates), new RealPoint(startPos.Coordinates));

                                // Test des obstacles
                                foreach (IShape obstacle in obstacles)
                                {
                                    if (obstacle.Distance(segmentTest) < distanceSecuriteCote)
                                    {
                                        franchissable = false;

                                        // Si l'obstacle génant est un adversaire, on diminue petit à petit son rayon pour pouvoir s'échapper au bout d'un moment
                                        if (opponents.Contains(obstacle) && GameBoard.OpponentRadius > 50)
                                        {
                                            Robots.MainRobot.Historique.Log("Adversaire au contact, impossible de s'enfuir, réduction du périmètre adverse", TypeLog.PathFinding);
                                            GameBoard.OpponentRadius -= 10;
                                            _linkResetRadius?.Cancel();
                                        }
                                    }
                                }

                                // Si le semgent entre notre position et le graph relié au graph est parcourable on y va !
                                if (franchissable)
                                {
                                    PointsTrouves.Add(new RealPoint(positionTestee.Coordinates));
                                    output.AddPoint(new RealPoint(positionTestee.Coordinates));

                                    debutNode = new Node(positionTestee.Coordinates);
                                    nbPointsDepart = graph.AddNode(debutNode, obstacles, rayonSecurite);
                                }
                            }
                        }
                    }

                    if (nbPointsDepart > 0)
                    {
                        finNode = graph.ClosestNode(endPos.Coordinates, out distance);
                        if (distance != 0)
                        {
                            finNode = new Node(endPos.Coordinates);
                            nbPointsArrivee = graph.AddNode(finNode, obstacles, rayonSecurite);
                        }
                        else
                        {
                            nbPointsArrivee = 1;
                        }
                        if (nbPointsArrivee == 0)
                        {
                            Console.WriteLine("Blocage arrivée");
                            // On ne peut pas arriver là où on souhaite aller
                            // On teste si on peut faire une approche en ligne 
                            // teta ne doit pas être nul sinon c'est qu'on ne maitrise pas l'angle d'arrivée et on ne connait pas l'angle d'approche

                            Position positionTestee = new Position(endPos);
                            bool franchissable = true;
                            raccordable = false;

                            // Boucle jusqu'à trouver un point qui se connecte au graph jusqu'à 1m devant
                            int i;
                            for (i = 0; i < 100 && !raccordable; i++)
                            {
                                positionTestee.Move(10);
                                raccordable = graph.Raccordable(new Node(positionTestee.Coordinates), obstacles, rayonSecurite);
                            }

                            // Le point à i*10 mm devant nous est reliable au graph, on cherche à l'atteindre
                            if (raccordable)
                            {
                                Segment segmentTest = new Segment(new RealPoint(positionTestee.Coordinates), new RealPoint(endPos.Coordinates));

                                // Test des obstacles
                                foreach (IShape obstacle in obstacles)
                                {
                                    if (obstacle.Distance(segmentTest) < distanceSecuriteCote)
                                        franchissable = false;
                                }
                            }
                            else
                                franchissable = false;

                            // Si toujours pas, on teste en marche arrière
                            if (!franchissable)
                            {
                                positionTestee = new Position(endPos);
                                nbPointsArrivee = 0;

                                for (i = 0; i > -100 && !raccordable; i--)
                                {
                                    positionTestee.Move(-10);
                                    raccordable = graph.Raccordable(new Node(positionTestee.Coordinates), obstacles, rayonSecurite);
                                }

                                if (raccordable)
                                {
                                    franchissable = true;
                                    Segment segmentTest = new Segment(new RealPoint(positionTestee.Coordinates), new RealPoint(endPos.Coordinates));

                                    // Test des obstacles
                                    foreach (IShape obstacle in obstacles)
                                    {
                                        if (obstacle.Distance(segmentTest) < distanceSecuriteCote)
                                            franchissable = false;
                                    }
                                }
                            }

                            // Si le semgent entre notre position et le node relié au graph est parcourable on y va !
                            if (franchissable)
                            {
                                finNode = new Node(positionTestee.Coordinates);
                                nbPointsArrivee = graph.AddNode(finNode, obstacles, rayonSecurite);
                            }
                        }
                    }

                    if (nbPointsDepart > 0 && nbPointsArrivee > 0)
                    {

                        // Teste s'il est possible d'aller directement à la fin sans passer par le graph
                        bool toutDroit = true;
                        Segment segment = new Segment(debutNode.Position, finNode.Position);

                        foreach (IShape forme in obstacles)
                        {
                            if (segment.Distance(forme) < rayonSecurite)
                            {
                                toutDroit = false;
                                break;
                            }
                        }

                        if (toutDroit)
                        {
                            Robots.MainRobot.Historique.Log("Chemin trouvé : ligne droite", TypeLog.PathFinding);
                            pathFound = true;

                            PointsTrouves.Add(finNode.Position);
                            output.AddPoint(finNode.Position);
                            if (endPos.Coordinates.Distance(finNode.Position) > 1)
                            {
                                PointsTrouves.Add(new RealPoint(endPos.Coordinates));
                                output.AddPoint(new RealPoint(endPos.Coordinates));
                            }
                        }

                        // Sinon on passe par le graph
                        else
                        {
                            AStar aStar = new AStar(graph);
                            aStar.DijkstraHeuristicBalance = 0;

                            //Console.WriteLine("Avant pathFinding : " + (DateTime.Now - debut).TotalMilliseconds + "ms");

                            if (aStar.SearchPath(debutNode, finNode))
                            {
                                //Console.WriteLine("PathFinding trouvé : " + (DateTime.Now - debut).TotalMilliseconds + "ms");

                                List<Node> nodes = aStar.PathByNodes.ToList<Node>();

                                Robots.MainRobot.Historique.Log("Chemin trouvé : " + (nodes.Count - 2) + " noeud(s) intermédiaire(s)", TypeLog.PathFinding);

                                CheminEnCoursNoeuds = new List<Node>();
                                CheminEnCoursArcs = new List<Arc>();

                                //Console.WriteLine("Début simplification : " + (DateTime.Now - debut).TotalMilliseconds + "ms");

                                // Simplification du chemin
                                // On part du début et on essaie d'aller au point du plus éloigné au moins éloigné en testant si le passage est possible
                                // Si c'est possible on zappe tous les points entre les deux
                                for (int iNodeDepart = 0; iNodeDepart < nodes.Count - 1; iNodeDepart++)
                                {
                                    if (iNodeDepart != 0)
                                    {
                                        PointsTrouves.Add(nodes[iNodeDepart].Position);
                                        output.AddPoint(nodes[iNodeDepart].Position);
                                    }

                                    CheminEnCoursNoeuds.Add(nodes[iNodeDepart]);

                                    bool raccourciPossible = true;
                                    for (int iNodeArrivee = nodes.Count - 1; iNodeArrivee > iNodeDepart; iNodeArrivee--)
                                    {
                                        raccourciPossible = true;

                                        Segment racourci = new Segment(nodes[iNodeDepart].Position, nodes[iNodeArrivee].Position);
                                        //Arc arcRacourci = new Arc(nodes[iNodeDepart], nodes[iNodeArrivee]);
                                        CheminTest = racourci;
                                        //arcRacourci.Passable = false;

                                        for (int i = obstacles.Count() - 1; i >= 4; i--) // > 4 pour ne pas tester les bordures
                                        {
                                            IShape forme = obstacles.ElementAt(i);
                                            ObstacleTeste = forme;
                                            ObstacleProbleme = null;

                                            if (racourci.Distance(forme) < rayonSecurite)
                                            {
                                                ObstacleProbleme = forme;

                                                raccourciPossible = false;
                                                break;
                                            }
                                            //else if(Config.CurrentConfig.AfficheDetailTraj > 0)
                                            //    Thread.Sleep(Config.CurrentConfig.AfficheDetailTraj);
                                        }

                                        if (Config.CurrentConfig.AfficheDetailTraj > 0)
                                            Thread.Sleep(Config.CurrentConfig.AfficheDetailTraj);

                                        ObstacleTeste = null;

                                        if (raccourciPossible)
                                        {
                                            //CheminEnCoursArcs.Add(arcRacourci);
                                            iNodeDepart = iNodeArrivee - 1;
                                            break;
                                        }
                                    }
                                    CheminTest = null;
                                    if (!raccourciPossible)
                                    {
                                        //Arc arc = new Arc(nodes[iNodeDepart], nodes[iNodeDepart + 1]);
                                        //CheminEnCoursArcs.Add(arc);
                                    }
                                }

                                CheminEnCoursNoeuds.Add(nodes[nodes.Count - 1]);
                                PointsTrouves.Add(nodes[nodes.Count - 1].Position);
                                output.AddPoint(nodes[nodes.Count - 1].Position);
                                Robots.MainRobot.Historique.Log("Chemin optimisé : " + (CheminEnCoursNoeuds.Count - 2) + " noeud(s) intermédiaire(s)", TypeLog.PathFinding);
                                pathFound = true;

                                if (endPos.Coordinates.Distance(finNode.Position) > 1)
                                {
                                    PointsTrouves.Add(new RealPoint(endPos.Coordinates));
                                    output.AddPoint(new RealPoint(endPos.Coordinates));
                                }
                            }
                            else
                            {
                                CheminEnCoursNoeuds = null;
                                CheminEnCoursArcs = null;
                                pathFound = false;
                            }
                        }
                    }

                    graph.CleanNodesArcsAdd();

                }
            }

            Dessinateur.CurrentTrack = null;
            //CheminEnCoursNoeuds = null;

            Console.WriteLine("Cherche trajectoire fin : " + sw.ElapsedMilliseconds + "ms");

            PointsTrouves = null;
            ObstacleProbleme = null;
            ObstacleTeste = null;

            //Console.WriteLine("PathFinding en " + (DateTime.Now - debut).TotalMilliseconds + " ms");

            if (!pathFound)
            {
                Robots.MainRobot.Historique.Log("Chemin non trouvé", TypeLog.PathFinding);
                return null;
            }
            else
            {
                if (GameBoard.OpponentRadius < GameBoard.OpponentRadiusInitial)
                {
                    _linkResetRadius = ThreadManager.CreateThread(link => ResetOpponentRadiusLoop());
                    _linkResetRadius.StartThread();
                }

                if (output.Lines.Count > 1)
                {
                    output = ReduceLines(output, obstacles.Concat(opponents), rayonSecurite);
                }

                return output;
            }
        }

        private static void ResetOpponentRadiusLoop()
        {
            _linkResetRadius.RegisterName();

            Thread.Sleep(1000);
            while (_linkResetRadius != null && !_linkResetRadius.Cancelled && GameBoard.OpponentRadius < GameBoard.OpponentRadiusInitial)
            {
                GameBoard.OpponentRadius++;
                Thread.Sleep(50);
            }
        }

        private static Trajectory ReduceLines(Trajectory traj, IEnumerable<IShape> obstacles, double securityRadius)
        {
            Trajectory output = traj;
            TimeSpan currentDuration = output.GetDuration(Robots.MainRobot);

            Console.WriteLine("Avant : " + currentDuration.ToString());

            int iSeg = 1;

            while (iSeg < output.Lines.Count - 1)
            {
                Trajectory tested = new Trajectory(output);
                tested.RemoveLine(iSeg);
                iSeg++;

                CheminEnCoursNoeudsSimplifyed = tested.Points.Select(o => new Node(o)).ToList();

                ObstacleProbleme = obstacles.FirstOrDefault(o => tested.Lines.Any(s => s.Distance(o) < securityRadius));
                Thread.Sleep(Config.CurrentConfig.AfficheDetailTraj * 15);

                if (ObstacleProbleme == null)
                {
                    TimeSpan testedDuration = tested.GetDuration(Robots.MainRobot);

                    if (testedDuration < currentDuration)
                    {
                        output = tested;
                        if (Config.CurrentConfig.AfficheDetailTraj > 0)
                        {
                            CheminEnCoursNoeuds = CheminEnCoursNoeudsSimplifyed;
                            CheminEnCoursNoeudsSimplifyed = null;
                            Thread.Sleep(Config.CurrentConfig.AfficheDetailTraj * 15);
                        }
                        currentDuration = testedDuration;
                        iSeg--;
                    }
                }
            }

            CheminEnCoursNoeuds = null;
            CheminEnCoursNoeudsSimplifyed = null;


            Console.WriteLine("Après : " + currentDuration.ToString());

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/Hokuyo/Hokuyo.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Threading;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using GoBot.BoardContext;

namespace GoBot.Devices
{
    public class Hokuyo : Lidar
    {
        #region Attributs

        private LidarID _id;

        private SerialPort _port;

        protected String _frameMeasure;
        protected String _frameDetails;
        protected String _frameSpecif;
        protected String _frameHighSentitivity, _frameLowSentitivity;

        protected String _romVendor, _romProduct, _romFirmware, _romProtocol, _romSerialNumber, _romModel;
        protected int _romMinDistance, _romMaxDistance, _romDivisions, _romFirstMeasure, _romLastMeasure, _romTotalMeasures, _romMotorSpeed;
        protected int _usefullMeasures;
        protected AngleDelta _scanRange, _resolution;

        protected int _distanceMinLimit, _distanceMaxLimit;

        protected int _keepFrom, _keepTo;
        protected bool _invertRotation;

        private Semaphore _lock;
        private ThreadLink _linkMeasures;

        private List<RealPoint> _lastMeasure;
        protected bool _connected;

        #endregion

        #region Propriétés

        public LidarID ID { get { return _id; } }

        public String Model { get { return _romModel; } }

        public AngleDelta ScanRange { get { return _scanRange; } }

        public override AngleDelta DeadAngle { get { return new AngleDelta(360 - _scanRange); } }

        public override bool Activated => _linkMeasures != null && _linkMeasures.Running;

        public int PointsCount { get { return _romTotalMeasures; } }

        public List<RealPoint> LastMeasure { get { return _lastMeasure; } }

        public int DistanceMaxLimit { get { return _distanceMaxLimit; } set { _distanceMaxLimit = value; } }

        public int KeepFrom
        {
            get { return _keepFrom; }
            set
            {
                _lock.WaitOne();
                _keepFrom = value;
                _frameMeasure = "MS" + _keepFrom.ToString("0000") + _keepTo.ToString("0000") + "00001\n";
                _lock.Release();
            }
        }

        public int KeepTo
        {
            get { return _keepTo; }
            set
            {
                _lock.WaitOne();
                _keepTo = value;
                _frameMeasure = "MS" + _keepFrom.ToString("0000") + _keepTo.ToString("0000") + "00001\n";
                _lock.Release();
            }
        }

        #endregion

        #region Constructeurs

        public Hokuyo(LidarID id)
        {
            _id = id;
            _lock = new Semaphore(1, 1);

            _distanceMaxLimit = 3999; // En dessous de 4000 parce que le protocole choisi seuille le maximum à 4000 niveau matos

            _position = new Position();

            _lastMeasure = null;

            _frameDetails = "VV\n";
            _frameSpecif = "PP\n";
            _frameLowSentitivity = "HS0\n";
            _frameHighSentitivity = "HS1\n";

            _invertRotation = false;
        }

        public Hokuyo(LidarID id, String portCom) : this(id)
        {
            _port = new SerialPort(portCom, 115200);
            _port.Open();

            IdentifyModel();
        }

        #endregion
        
        #region Fonctionnement externe

        protected override bool StartLoop()
        {
            _linkMeasures = ThreadManager.CreateThread(link => DoMeasure());
            _linkMeasures.Name = "Mesure Hokuyo " + _id.ToString();
            _linkMeasures.StartInfiniteLoop(new TimeSpan(0, 0, 0, 0, 100));

            return true;
        }

        protected override void StopLoop()
        {
            _linkMeasures.Cancel();
        }

        public override List<RealPoint> GetPoints()
        {
            return GetPoints(true);
        }

        public List<RealPoint> GetRawPoints()
        {
            return GetPoints(false);
        }

        #endregion

        #region Fonctionnement interne

        private List<RealPoint> GetPoints(bool useReference)
        {
            List<RealPoint> points = new List<RealPoint>();

            _lock.WaitOne();

            String reponse = GetMeasure();

            try
            {
                if (reponse != "")
                {
                    List<int> mesures = DecodeMessage(reponse);
                    points = ValuesToPositions(mesures, false, _distanceMinLimit, _distanceMaxLimit, (useReference ? _position : new Position())) ;
                }
            }
            catch (Exception) { }

            _lock.Release();

            return points;
        }

        private void IdentifyModel()
        {
            SendMessage(_frameDetails);
            String response = GetResponse();

            List<String> details = response.Split(new char[] { '\n', ':', ';' }).ToList();

            _romVendor = details[3];
            _romProduct = details[6];
            _romFirmware = details[9];
            _romProtocol = details[12];
            _romSerialNumber = details[15];

            SendMessage(_frameSpecif);
            response = GetResponse();

            details = response.Split(new char[] { '\n', ':', ';' }).ToList();

            _romModel = details[3];
            _romMinDistance = int.Parse(details[6]);
            _romMaxDistance = int.Parse(details[9]);
            _romDivisions = int.Parse(details[12]);
            _romFirstMeasure = int.Parse(details[15]);
            _romLastMeasure = int.Parse(details[18]);
            _romTotalMeasures = int.Parse(details[21]) * 2 + 1;
            _romMotorSpeed = int.Parse(details[24]);

            _usefullMeasures = _romTotalMeasures - (_romTotalMeasures - (_romLastMeasure + 1)) - _romFirstMeasure;
            _resolution = 360.0 / _romDivisions;
            _scanRange = _resolution * _usefullMeasures;

            _keepFrom = _romFirstMeasure;
            _keepTo = _romFirstMeasure + _usefullMeasures - 1;

            _frameMeasure = "MS" + _keepFrom.ToString("0000") + _keepTo.ToString("0000") + "00001\n";
        }

        private String GetMeasure(int timeout = 500)
        {
            SendMessage(_frameMeasure);
            return GetResponse(timeout);
        }

        protected virtual void SendMessage(String msg)
        {
            _port.Write(msg);
        }

        protected virtual String GetResponse(int timeout = 500)
        {
            Stopwatch chrono = Stopwatch.StartNew();
            String reponse = "";

            do
            {
                reponse += _port.ReadExisting();
            } while (!reponse.Contains("\n\n") && chrono.ElapsedMilliseconds < timeout);

            chrono.Stop();

            if (chrono.ElapsedMilliseconds >= timeout)
                return "";
            else
                return reponse;
        }

        private void DoMeasure()
        {
            try
            {
                _lastMeasure = GetPoints();
                if (!_linkMeasures.Cancelled) OnNewMeasure(_lastMeasure);
            }
            catch(Exception ex)
            {
                Debug.Print("ERREUR LIDAR : " + ex.Message);
            }
        }

        private List<RealPoint> ValuesToPositions(List<int> measures, bool limitOnTable, int minDistance, int maxDistance, Position refPosition)
        {
            List<RealPoint> positions = new List<RealPoint>();

            for (int i = 0; i < measures.Count; i++)
            {
                AnglePosition angle = _resolution * (i + _keepFrom);

                if (measures[i] > minDistance && (measures[i] < maxDistance || maxDistance == -1))
                {
                    AnglePosition anglePoint = new AnglePosition(angle.InPositiveRadians - refPosition.Angle.InPositiveRadians - ScanRange.InRadians / 2 - Math.PI / 2, AngleType.Radian);

                    RealPoint pos = new RealPoint(refPosition.Coordinates.X - anglePoint.Sin * measures[i], refPosition.Coordinates.Y - anglePoint.Cos * measures[i]);

                    int marge = 20; // Marge en mm de distance de detection à l'exterieur de la table (pour ne pas jeter les mesures de la bordure qui ne collent pas parfaitement)

                    if (!limitOnTable || (pos.X > -marge && pos.X < GameBoard.Width + marge && pos.Y > -marge && pos.Y < GameBoard.Height + marge))
                        positions.Add(pos);
                }
            }

            return positions;
        }

        private List<int> DecodeMessage(String message)
        {
            List<int> values = new List<int>();

            String[] tab = message.Split(new char[] { '\n' });
            Boolean started = false;

            for (int i = 0; i < tab.Length - 2; i++)
            {
                if (tab[i].Length > 64) started = true;

                if (started)
                {
                    for (int j = 0; j < tab[i].Length - 1; j += 2)
                    {
                        int val = DecodeValue(tab[i].Substring(j, 2));
                        values.Add(val);
                    }
                }
            }

            if (values.Count >= _romTotalMeasures)
                values = values.GetRange(_keepFrom, _keepTo - _keepFrom);

            if (_invertRotation)
                values.Reverse();

            return values;
        }

        private int DecodeValue(String data)
        {
            int value = 0;
            for (int i = 0; i < data.Length; ++i)
            {
                value <<= 6;
                value &= ~0x3f;
                value |= data[i] - 0x30;
            }
            return value;
        }

        #endregion
    }
}
<file_sep>/GoBot/GoBot/Devices/CAN/CanServo.cs
﻿using GoBot.Communications;
using GoBot.Communications.CAN;
using GoBot.Threading;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;

namespace GoBot.Devices.CAN
{
    /// <summary>
    /// Représente un servomoteur piloté par liaison CAN
    /// </summary>
    public class CanServo
    {
        private ServomoteurID _id;

        private int _position;
        private int _positionMin, _positionMax;
        private int _acceleration;
        private int _speedMax;
        private int _torqueCurrent, _torqueMax;
        private bool _enableAutoCut;

        private Semaphore _lockResponse;

        private iCanSpeakable _communication;

        private ThreadLink _nextDisable;

        public delegate void TorqueAlertDelegate();
        public event TorqueAlertDelegate TorqueAlert;

        public CanServo(ServomoteurID id, iCanSpeakable communication)
        {
            _id = (ServomoteurID)((int)id % 100);
            _communication = communication;
            _lockResponse = new Semaphore(0, int.MaxValue);
            _enableAutoCut = false;
            _nextDisable = null;
        }

        public ServomoteurID ID { get => _id; }
        public int LastPosition { get => _position; }
        public int LastPositionMin { get => _positionMin; }
        public int LastPositionMax { get => _positionMax; }
        public int LastSpeedMax { get => _speedMax; }
        public int LastTorqueCurrent { get => _torqueCurrent; }
        public int LastTorqueMax { get => _torqueMax; }
        public int LastAcceleration { get => _acceleration; }
        public bool AutoCut { get => _enableAutoCut; set => _enableAutoCut = value; }

        public int ReadPosition()
        {
            _communication.SendFrame(CanFrameFactory.BuildGetPosition(_id));
            bool ok = _lockResponse.WaitOne(200);
            return ok ? _position : -1;
        }

        public int ReadPositionMax()
        {
            _communication.SendFrame(CanFrameFactory.BuildGetPositionMax(_id));
            bool ok = _lockResponse.WaitOne(200);
            return ok ? _positionMax : -1;
        }

        public int ReadPositionMin()
        {
            _communication.SendFrame(CanFrameFactory.BuildGetPositionMin(_id));
            bool ok = _lockResponse.WaitOne(200);
            return ok ? _positionMin : -1;
        }

        public int ReadSpeedMax()
        {
            _communication.SendFrame(CanFrameFactory.BuildGetSpeedMax(_id));
            bool ok = _lockResponse.WaitOne(200);
            return ok ? _speedMax : -1;
        }

        public int ReadAcceleration()
        {
            _communication.SendFrame(CanFrameFactory.BuildGetAcceleration(_id));
            bool ok = _lockResponse.WaitOne(200);
            return ok ? _acceleration : -1;
        }

        public int ReadTorqueCurrent()
        {
            _communication.SendFrame(CanFrameFactory.BuildGetTorqueCurrent(_id));
            bool ok = _lockResponse.WaitOne(200);
            Debug.Print(_torqueCurrent.ToString());

            return ok ? _torqueCurrent : -1;
        }

        public int ReadTorqueMax()
        {
            _communication.SendFrame(CanFrameFactory.BuildGetTorqueMax(_id));
            bool ok = _lockResponse.WaitOne(200);
            return ok ? _torqueMax : -1;
        }

        public void SearchMax()
        {
            int initial = ReadPosition() / 100 * 100;
            int max = initial;
            int tempo = 500;
            int targetTorque = ReadTorqueMax();

            SetPositionMax(60000);

            while (ReadTorqueCurrent() < targetTorque)
            {
                max += 500;
                SetPosition(max);
                Thread.Sleep(tempo);
            }

            max -= 500;
            SetPosition(max);
            Thread.Sleep(tempo);

            while (ReadTorqueCurrent() < targetTorque)
            {
                max += 100;
                SetPosition(max);
                Thread.Sleep(tempo);
            }

            max -= 100;

            SetPositionMax(max);
        }

        public void SearchMin()
        {
            int initial = ReadPosition() / 100 * 100; ;
            int min = initial;
            int tempo = 500;
            int targetTorque = ReadTorqueMax();

            SetPositionMin(0);

            while (ReadTorqueCurrent() < targetTorque)
            {
                min -= 500;
                SetPosition(min);
                Thread.Sleep(tempo);
            }

            min += 500;
            SetPosition(min);
            Thread.Sleep(tempo);

            while (ReadTorqueCurrent() < targetTorque)
            {
                min -= 100;
                SetPosition(min);
                Thread.Sleep(tempo);
            }

            min += 100;

            SetPositionMin(min);
            SetPosition(initial);
        }

        public void SetAcceleration(int acceleration)
        {
            _communication.SendFrame(CanFrameFactory.BuildSetAcceleration(_id, acceleration));
            _acceleration = acceleration;
        }

        public void SetPosition(int position)
        {
            CancelDisable();
            _communication.SendFrame(CanFrameFactory.BuildSetPosition(_id, position));
        }

        public void SetPositionMax(int positionMax)
        {
            _communication.SendFrame(CanFrameFactory.BuildSetPositionMax(_id, positionMax));
            _positionMax = positionMax;
        }

        public void SetPositionMin(int positionMin)
        {
            _communication.SendFrame(CanFrameFactory.BuildSetPositionMin(_id, positionMin));
            _positionMin = positionMin;
        }

        public void SetSpeedMax(int speedMax)
        {
            _communication.SendFrame(CanFrameFactory.BuildSetSpeedMax(_id, speedMax));
            _speedMax = speedMax;
        }

        public void SetTorqueMax(int torqueMax)
        {
            _communication.SendFrame(CanFrameFactory.BuildSetTorqueMax(_id, torqueMax));
            _torqueMax = torqueMax;
        }

        public void SetTrajectory(int position, int speed, int accel)
        {
            _communication.SendFrame(CanFrameFactory.BuildSetTrajectory(_id, position, speed, accel));
        }

        public void DisableOutput(int delayMs = 0)
        {
            if (delayMs > 0)
            {
                CancelDisable();

                ThreadManager.CreateThread(link =>
                {
                    _nextDisable = link;
                    if (!link.Cancelled)
                        _communication.SendFrame(CanFrameFactory.BuildDisableOutput(_id));
                }).StartDelayedThread(delayMs);
            }
            else
            {
                _communication.SendFrame(CanFrameFactory.BuildDisableOutput(_id));
            }
        }

        public void FrameReceived(Frame frame)
        {
            CanFrameFunction function = CanFrameFactory.ExtractFunction(frame);

            try
            {
                switch (function)
                {
                    case CanFrameFunction.PositionResponse:
                        _position = CanFrameFactory.ExtractValue(frame);
                        _lockResponse.Release();
                        break;
                    case CanFrameFunction.PositionMaxResponse:
                        _positionMax = CanFrameFactory.ExtractValue(frame);
                        _lockResponse.Release();
                        break;
                    case CanFrameFunction.PositionMinResponse:
                        _positionMin = CanFrameFactory.ExtractValue(frame);
                        _lockResponse.Release();
                        break;
                    case CanFrameFunction.SpeedMaxResponse:
                        _speedMax = CanFrameFactory.ExtractValue(frame);
                        _lockResponse.Release();
                        break;
                    case CanFrameFunction.TorqueCurrentResponse:
                        _torqueCurrent = CanFrameFactory.ExtractValue(frame);
                        _lockResponse.Release();
                        break;
                    case CanFrameFunction.TorqueMaxResponse:
                        _torqueMax = CanFrameFactory.ExtractValue(frame);
                        _lockResponse.Release();
                        break;
                    case CanFrameFunction.AccelerationResponse:
                        _acceleration = CanFrameFactory.ExtractValue(frame);
                        _lockResponse.Release();
                        break;
                    case CanFrameFunction.TorqueAlert:
                        //AllDevices.RecGoBot.Buzz(".-.");
                        AllDevices.Buzzer.Buzz(2000, 250);
                        if (_enableAutoCut)
                            _communication.SendFrame(CanFrameFactory.BuildDisableOutput(_id));
                        TorqueAlert?.Invoke();
                        break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(DateTime.Now.ToLongTimeString() + ":" + DateTime.Now.Millisecond.ToString("000") + " : Erreur servo CAN : " + e.Message);
            }
        }

        private void CancelDisable()
        {
            if (_nextDisable != null)
            {
                _nextDisable.Cancel();
                _nextDisable = null;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/Actionneurs/Positionables.cs
﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace GoBot.Actionneurs
{
    public abstract class Positionable
    {
        private int? _lastPosition;

        public List<String> GetPositionsName()
        {
            PropertyInfo[] properties = this.GetType().GetProperties();
            List<String> propertiesName = new List<string>();
            foreach (PropertyInfo p in properties)
            {
                if (p.Name.StartsWith("Position"))
                    propertiesName.Add(p.Name);
            }

            return propertiesName;
        }

        public Dictionary<String, int> GetPositions()
        {
            PropertyInfo[] properties = this.GetType().GetProperties();
            Dictionary<String, int> positions = new Dictionary<String, int>();

            foreach (PropertyInfo p in properties)
            {
                if (p.Name.StartsWith("Position"))
                    positions.Add(p.Name, (int)p.GetValue(this, null));
            }

            return positions;
        }

        public int GetLastPosition()
        {
            return _lastPosition.HasValue ? _lastPosition.Value : (Minimum + Maximum) / 2;
        }

        public void SendPosition(int position)
        {
            _lastPosition = position;
            SendPositionSpecific(position);
        }

        protected abstract void SendPositionSpecific(int position);

        public override string ToString()
        {
            String typeName = this.GetType().Name;
            String name = "";

            foreach (char c in typeName)
            {
                char ch = c;
                if (c <= 'Z' && c >= 'A')
                    name += " " + (char)(c + 32);
                else
                    name += c;
            }

            name = typeName.Substring(0, 1) + name.Substring(2);

            return name;
        }

        public int Minimum { get; set; }
        public int Maximum { get; set; }
    }

    public abstract class PositionableServo : Positionable
    {
        public abstract ServomoteurID ID { get; }

        protected override void SendPositionSpecific(int position)
        {
            Devices.AllDevices.CanServos[ID].SetPosition(position);
        }

        public void DisableTorque()
        {
            Devices.AllDevices.CanServos[ID].DisableOutput();
        }
    }

    public abstract class PositionableMotorPosition : Positionable
    {
        public abstract MotorID ID { get; }

        protected override void SendPositionSpecific(int position)
        {
            Robots.MainRobot.SetMotorAtPosition(ID, position, true);
        }

        public void Stop(StopMode mode)
        {
            Robots.MainRobot.SetMotorStop(ID, mode);
        }

        public void OriginInit()
        {
            Stop(StopMode.Abrupt);
            Robots.MainRobot.SetMotorAtOrigin(ID, true);
            Robots.MainRobot.SetMotorReset(ID);
            Stop(StopMode.Abrupt);
            Robots.MainRobot.SetMotorAtPosition(ID, 30);
        }
    }

    public abstract class PositionableMotorSpeed : Positionable
    {
        public abstract MotorID ID { get; }

        protected override void SendPositionSpecific(int position)
        {
            Robots.MainRobot.SetMotorSpeed(ID, position > 0 ? SensGD.Gauche : SensGD.Droite, Math.Abs(position));
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageLidar.cs
﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

using Geometry.Shapes;
using GoBot.Devices;
using GoBot.BoardContext;

namespace GoBot.IHM.Pages
{
    public partial class PageLidar : UserControl
    {
        private Lidar _selectedLidar;
        private List<RealPoint> _lastMeasure;

        public PageLidar()
        {
            InitializeComponent();
            _lastMeasure = null;
            _selectedLidar = null;
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (!picWorld.RectangleToScreen(picWorld.ClientRectangle).Contains(MousePosition))
            {
                base.OnMouseWheel(e);
            }
            else
            {
                if (e.Delta > 0)
                    trackZoom.SetValue(trackZoom.Value * 0.90, true);
                else
                    trackZoom.SetValue(trackZoom.Value * 1.1, true);
            }
        }

        private void switchEnable_ValueChanged(object sender, bool value)
        {
            if (_selectedLidar != null)
            {
                _selectedLidar.FrequencyChange -= lidar_FrequencyChange;
                _selectedLidar.NewMeasure -= lidar_NewMeasure;
                _selectedLidar.StartLoopMeasure();
            }

            if (value)
            {
                if ((String)cboLidar.Text == "Ground")
                    _selectedLidar = AllDevices.LidarGround;
                else if ((String)cboLidar.Text == "Avoid")
                    _selectedLidar = AllDevices.LidarAvoid;
                else
                    _selectedLidar = null;

                if (_selectedLidar != null)
                {
                    _selectedLidar.FrequencyChange += lidar_FrequencyChange;
                    _selectedLidar.NewMeasure += lidar_NewMeasure;
                    _selectedLidar.StartLoopMeasure();
                }
            }
        }

        private void lidar_NewMeasure(List<RealPoint> measure)
        {
            _lastMeasure = measure;
            picWorld.Invalidate();
        }

        private void PanelLidar_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                trackZoom.SetValue(1);

                cboLidar.Items.Add("Ground");
                cboLidar.Items.Add("Avoid");
            }
        }

        private void lidar_FrequencyChange(double value)
        {
            lblMeasuresPerSecond.InvokeAuto(() => lblMeasuresPerSecond.Text = value.ToString("0.00") + " mesures/s");
        }

        private void trackZoom_ValueChanged(object sender, double value)
        {
            picWorld.Dimensions.SetZoomFactor(value);
        }

        private void picWorld_WorldChange()
        {
            picWorld.Invalidate();
        }

        private void picWorld_Paint(object sender, PaintEventArgs e)
        {
            List<RealPoint> points = _lastMeasure;
            Graphics g = e.Graphics;

            if (picWorld.Width > 0 && picWorld.Height > 0)
            {
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                g.DrawRectangle(Pens.Gray, 0, 0, picWorld.Width - 1, picWorld.Height - 1);


                if (boxScale.Checked)
                {
                    for (int i = 100; i < 5000; i += 100)
                    {
                        new Circle(new RealPoint(), i).Paint(g, Color.Gray, picWorld.Dimensions.WorldScale.Factor < 1 ? 2 : 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                    }

                    if (picWorld.Dimensions.WorldScale.Factor < 1)
                    {
                        for (int i = 10; i < 5000; i += 10)
                        {
                            if (i % 100 != 0)
                            {
                                new Circle(new RealPoint(), i).Paint(g, Color.LightGray, 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                            }
                        }
                    }
                }

                if (points?.Count > 0)
                {
                    if (rdoOutline.Checked)
                    {
                        points.Add(new RealPoint());
                        Polygon poly = new Polygon(points);
                        points.RemoveAt(points.Count - 1);
                        poly.Paint(g, Color.Red, 1, Color.LightGray, picWorld.Dimensions.WorldScale);
                    }
                    else if (rdoShadows.Checked)
                    {
                        points.Add(new RealPoint());
                        Polygon poly = new Polygon(points);
                        points.RemoveAt(points.Count - 1);
                        g.FillRectangle(Brushes.LightGray, 0, 0, picWorld.Width, picWorld.Height);
                        poly.Paint(g, Color.Red, 1, Color.White, picWorld.Dimensions.WorldScale);
                    }
                    else if (rdoObjects.Checked)
                    {
                        foreach (RealPoint p in points)
                        {
                            p.Paint(g, Color.Black, 3, Color.Red, picWorld.Dimensions.WorldScale);
                        }
                    }
                    else
                    {
                        foreach (RealPoint p in points)
                        {
                            Segment s = new Segment(new RealPoint(), p);
                            s.Paint(g, Color.Red, 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                        }
                    }

                    if (boxGroup.Checked)
                    {
                        points = points.Where(o => GameBoard.IsInside(o)).ToList();
                        List<List<RealPoint>> groups = points.GroupByDistance(50);

                        List<Color> colors = new List<Color>() { Color.Blue, Color.Green, Color.Red, Color.Brown };

                        List<IShape> shapes = new List<IShape>();

                        for (int i = 0; i < groups.Count; i++)
                        {
                            if (groups[i].Count > 5 && i < colors.Count)
                            {
                                Circle circle = groups[i].FitCircle();
                                //Line line = groups[i].FitLine();
                                //Segment line = groups[i].FitSegment();

                                shapes.Add(circle);

                                circle.Paint(g, colors[i], 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                                g.DrawString((circle.Radius * 2).ToString("0") + "mm / " + (groups[i].FitCircleScore(circle) * 100).ToString("0") + "% / " + (groups[i].FitLineCorrelation()).ToString("0.00") + "%", new Font("Calibri", 9), new SolidBrush(colors[i]), picWorld.Dimensions.WorldScale.RealToScreenPosition(circle.Center.Translation(circle.Radius, 0)));

                                //line.Paint(g, colors[i], 1, Color.Transparent, picWorld.Dimensions.WorldScale);

                            }
                        }

                        //Plateau.Detections = shapes;
                    }
                    else
                    {
                        //Plateau.Detections = new List<IShape>(points);
                    }

                    new Circle(_selectedLidar.Position.Coordinates, 20).Paint(g, Color.Black, 1, Color.White, picWorld.Dimensions.WorldScale);
                }
            }
        }

        private void picWorld_MouseMove(object sender, MouseEventArgs e)
        {
            lblMousePosition.Text = picWorld.Dimensions.WorldScale.ScreenToRealPosition(e.Location).ToString();
        }
    }
}
<file_sep>/GoBot/GoBot/GameElements/RandomDropOff.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.BoardContext;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace GoBot.GameElements
{
    public class RandomDropOff : GameElementZone
    {
        private List<List<Color>> _loads;
        private List<Color> _loadOnTop;
        private List<Color> _loadOnBottom;

        public RandomDropOff(RealPoint position, Color color) : base(position, color, 200)
        {
            _loads = new List<List<Color>>();

            _loadOnTop = null;
            _loadOnBottom = null;
        }

        public int LoadsCount => _loads.Count;
        public bool HasLoadOnTop => _loadOnTop != null;
        public bool HasLoadOnBottom => _loadOnBottom != null;

        public override void Paint(Graphics g, WorldScale scale)
        {
            base.Paint(g, scale);
            double x = (Owner == GameBoard.ColorLeftBlue ? 55 : 3000 - 55);
            double dx = (Owner == GameBoard.ColorLeftBlue ? 85 : -85);

            for (int i = 0; i < _loads.Count; i++)
                for (int j = 0; j < _loads[i].Count; j++)
                    new Circle(new RealPoint(x + dx * i, _position.Y - (j - 2) * 75), 36).Paint(g, Color.Black, 1, _loads[i][j], scale);

            x = _position.X + (Owner == GameBoard.ColorLeftBlue ? -130 : 130);
            dx = (Owner == GameBoard.ColorLeftBlue ? 85 : -85);

            if (_loadOnTop != null)
                for (int j = 0; j < _loadOnTop.Count; j++)
                    new Circle(new RealPoint(x + dx * j, _position.Y - 280), 36).Paint(g, Color.Black, 1, _loadOnTop[j], scale);

            if (_loadOnBottom != null)
                for (int j = 0; j < _loadOnBottom.Count; j++)
                    new Circle(new RealPoint(x + dx * j, _position.Y + 280), 36).Paint(g, Color.Black, 1, _loadOnBottom[j], scale);
        }

        public void AddLoad(List<Color> load)
        {
            if (load != null)
            {
                load = new List<Color>(load);

                if (Owner == GameBoard.ColorLeftBlue)
                    load.Reverse();

                _loads.Add(load);
            }
        }

        public void SetLoadTop(List<Color> load)
        {
            _loadOnTop = new List<Color>(load);
        }

        public void SetLoadBottom(List<Color> load)
        {
            _loadOnBottom = new List<Color>(load);
            _loadOnBottom.Reverse();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/PagesPanda/PagePandaLidar.cs
﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

using Geometry.Shapes;
using GoBot.Devices;
using GoBot.BoardContext;
using System.Drawing.Drawing2D;

namespace GoBot.IHM.Pages
{
    public partial class PagePandaLidar : UserControl
    {
        private Lidar _selectedLidar;
        private List<RealPoint> _measureObjects, _measureBoard;

        private Bitmap _background;
        private RectangleF _backgroundRect;

        private bool _enableBoard;
        private bool _showLines, _showPoints;

        private bool _enableGroup;

        private bool _running;

        public PagePandaLidar()
        {
            InitializeComponent();
            _measureObjects = null;
            _selectedLidar = null;

            picWorld.Dimensions.SetZoomFactor(5);

            _enableGroup = false;
            _enableBoard = true;
            _showPoints = true;
            _showLines = false;
            _running = false;
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (!picWorld.RectangleToScreen(picWorld.ClientRectangle).Contains(MousePosition))
            {
                base.OnMouseWheel(e);
            }
            else
            {
                if (e.Delta > 0)
                    picWorld.Dimensions.SetZoomFactor(picWorld.Dimensions.WorldScale.Factor * 0.8);
                else
                    picWorld.Dimensions.SetZoomFactor(picWorld.Dimensions.WorldScale.Factor * 1.25);
            }
        }

        private void lidar_NewMeasure(List<RealPoint> measure)
        {
            List<IShape> obstacles = GameBoard.ObstaclesBoardConstruction.ToList();
            List<RealPoint> tmpObjects, tmpBoard;

            if (!_enableBoard)
            {
                tmpObjects = measure.Where(o => GameBoard.IsInside(o, 50)).ToList();
                tmpObjects = tmpObjects.Where(p => !obstacles.Exists(o => o.Distance(p) < 30)).ToList();

                tmpBoard = measure.Where(p => !tmpObjects.Contains(p)).ToList();
                _measureObjects = tmpObjects.Select(p => new RealPoint(p.X - _selectedLidar.Position.Coordinates.X, p.Y - _selectedLidar.Position.Coordinates.Y)).ToList();
                _measureBoard = tmpBoard.Select(p => new RealPoint(p.X - _selectedLidar.Position.Coordinates.X, p.Y - _selectedLidar.Position.Coordinates.Y)).ToList();
            }
            else
            {
                _measureObjects = measure.Select(p => new RealPoint(p.X - _selectedLidar.Position.Coordinates.X, p.Y - _selectedLidar.Position.Coordinates.Y)).ToList();
                _measureBoard = null;
            }

            picWorld.Invalidate();
        }

        private void PanelLidar_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                btnTrap.Focus();
                _running = false;
                _selectedLidar = null;
            }
        }

        private void picWorld_WorldChange()
        {
            picWorld.Invalidate();
        }

        private Bitmap GenerateBackground()
        {
            Bitmap b = new Bitmap(picWorld.Width, picWorld.Height);
            Graphics g = Graphics.FromImage(b);

            if (picWorld.Width > 0 && picWorld.Height > 0)
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;

                PolygonRectangle bounds = new PolygonRectangle(picWorld.Dimensions.WorldRect);

                for (int i = 100; i < 5000; i += 100)
                {
                    Circle c = new Circle(new RealPoint(), i);
                    if (c.Cross(bounds) || bounds.Contains(c))
                    {
                        c.Paint(g, i % 1000 == 0 ? Color.LightGray : Color.DimGray, 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                    }
                }

                if (picWorld.Dimensions.WorldScale.Factor < 1 && !picWorld.Moving)
                {
                    for (int i = 10; i < 5000; i += 10)
                    {
                        if (i % 100 != 0)
                        {
                            Circle c = new Circle(new RealPoint(), i);
                            if (c.Cross(bounds) || bounds.Contains(c))
                            {
                                c.Paint(g, Color.FromArgb(60, 60, 60), 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                            }
                        }
                    }
                }
            }

            g.Dispose();

            return b;
        }

        private void picWorld_Paint(object sender, PaintEventArgs e)
        {
            if (_background == null || _backgroundRect != picWorld.Dimensions.WorldRect)
            {
                _background = GenerateBackground();
                _backgroundRect = picWorld.Dimensions.WorldRect;
            }

            Bitmap background = _background;

            List<RealPoint> pointsObjects = _measureObjects;
            List<RealPoint> pointsBoard = _measureBoard;

            Graphics g = e.Graphics;

            if (picWorld.Width > 0 && picWorld.Height > 0)
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;

                g.DrawImage(background, 0, 0);

                if (picWorld.Moving)
                {
                    int diam = 100;
                    Point p = picWorld.Dimensions.WorldScale.RealToScreenPosition(picWorld.ClickedPoint);
                    //g.FillEllipse(Brushes.White, p.X - diam / 2, p.Y - diam / 2, diam, diam);

                    GraphicsPath path = new GraphicsPath();
                    path.AddEllipse(p.X - diam / 2, p.Y - diam / 2, diam, diam);
                    PathGradientBrush b = new PathGradientBrush(path);
                    b.CenterPoint = p;
                    b.CenterColor = Color.White;
                    b.SurroundColors = new[] { Color.Transparent };
                    b.FocusScales = new PointF(0, 0);

                    g.FillEllipse(b, p.X - diam / 2, p.Y - diam / 2, diam, diam);
                    b.Dispose();
                    path.Dispose();
                }

                if (pointsBoard?.Count > 0)
                {
                    if (_showPoints)
                    {
                        foreach (RealPoint p in pointsBoard)
                        {
                            p.Paint(g, Color.DarkRed, 3, Color.Red, picWorld.Dimensions.WorldScale);
                        }
                    }
                    if (_showLines)
                    {
                        foreach (RealPoint p in pointsBoard)
                        {
                            Segment s = new Segment(new RealPoint(), p);
                            s.Paint(g, Color.Red, 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                        }
                    }
                }

                if (pointsObjects?.Count > 0)
                {
                    if (_showPoints)
                    {
                        foreach (RealPoint p in pointsObjects)
                        {
                            p.Paint(g, Color.DarkGreen, 3, Color.Lime, picWorld.Dimensions.WorldScale);
                        }
                    }
                    if (_showLines)
                    {
                        foreach (RealPoint p in pointsObjects)
                        {
                            Segment s = new Segment(new RealPoint(), p);
                            s.Paint(g, Color.Lime, 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                        }
                    }

                    if (_enableGroup)
                    {
                        //points = points.Where(o => GameBoard.IsInside(o)).ToList();
                        List<List<RealPoint>> groups = pointsObjects.GroupByDistance(80);

                        //for (int i = 0; i < groups.Count; i++)
                        //{
                        //    Circle circle = groups[i].FitCircle();
                        //    if (circle.Radius < 100 && groups[i].Count > 4)
                        //    {
                        //        circle.Paint(g, Color.White, 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                        //    }
                        //}

                        for (int i = 0; i < groups.Count; i++)
                        {
                            if (groups[i].Count > 4)
                            {
                                RealPoint center = groups[i].GetBarycenter();
                                double var = Math.Sqrt(groups[i].Average(p => p.Distance(center) * p.Distance(center))) * 2;
                                new Circle(center, var).Paint(g, var > 35 ? Color.Lime : Color.Red, 1, Color.Transparent, picWorld.Dimensions.WorldScale);
                            }
                        }

                        //Plateau.Detections = shapes;
                    }
                    else
                    {
                        //Plateau.Detections = new List<IShape>(points);
                    }
                }
            }
        }

        public void LidarEnable(bool lidarEnable)
        {
            if (_selectedLidar != null && !lidarEnable && _running)
            {
                _selectedLidar.NewMeasure -= lidar_NewMeasure;
                _running = false;
            }

            if (lidarEnable && !_running)
            {
                if (_selectedLidar != null)
                {
                    _selectedLidar.NewMeasure += lidar_NewMeasure;
                    if (!_selectedLidar.Activated)
                        _selectedLidar.StartLoopMeasure();
                    _running = true;
                }
            }
        }

        private void picWorld_StopMove()
        {
            _background = GenerateBackground();
            picWorld.Invalidate();
        }

        private void picWorld_StartMove()
        {
            picWorld.Invalidate();
        }

        private void btnZoomPlus_Click(object sender, EventArgs e)
        {
            picWorld.Dimensions.SetZoomFactor(picWorld.Dimensions.WorldScale.Factor * 0.8);

            btnTrap.Focus();
        }

        private void btnZoomMinus_Click(object sender, EventArgs e)
        {
            picWorld.Dimensions.SetZoomFactor(picWorld.Dimensions.WorldScale.Factor * 1.25);

            btnTrap.Focus();
        }

        private void btnZoomReset_Click(object sender, EventArgs e)
        {
            picWorld.Dimensions.SetZoomFactor(5);
            picWorld.Dimensions.SetWorldCenter(new RealPoint(0, 0));

            btnTrap.Focus();
        }

        private void SetLidar(Lidar lidar)
        {
            if (_selectedLidar != lidar)
            {
                if (_selectedLidar != null)
                {
                    _selectedLidar.NewMeasure -= lidar_NewMeasure;
                    if (_selectedLidar == AllDevices.LidarGround)
                        _selectedLidar.StopLoopMeasure();
                }

                _selectedLidar = lidar;

                if (_selectedLidar != null)
                {
                    _selectedLidar.NewMeasure += lidar_NewMeasure;
                    if (_selectedLidar == AllDevices.LidarGround)
                        _selectedLidar.StartLoopMeasure();
                }

                _measureObjects = null;
                _measureBoard = null;
                picWorld.Invalidate();
            }
        }

        private void btnGroup_Click(object sender, EventArgs e)
        {
            _enableGroup = !_enableGroup;
            btnGroup.Image = _enableGroup ? Properties.Resources.LidarGroup : Properties.Resources.LidarGroupDisable;
        }

        private void btnLidarAvoid_Click(object sender, EventArgs e)
        {
            SetLidar(AllDevices.LidarAvoid);
        }

        private void btnLidarGround_Click(object sender, EventArgs e)
        {
            SetLidar(AllDevices.LidarGround);
        }

        private void btnEnableBoard_Click(object sender, EventArgs e)
        {
            _enableBoard = !_enableBoard;

            btnEnableBoard.Image = _enableBoard ? Properties.Resources.LidarBoardOn : Properties.Resources.LidarBoardOff;

            btnTrap.Focus();
        }

        private void btnPoints_Click(object sender, EventArgs e)
        {
            _showPoints = !_showPoints;
            _showLines = !_showLines;

            btnPoints.Image = _showPoints ? Properties.Resources.LidarPoints : Properties.Resources.LidarLines;

            btnTrap.Focus();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/PagesPanda/PagePandaMove.cs
﻿using System;
using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PagePandaMove : UserControl
    {
        bool _asserEna = true; // TODO faut garder la vraie valeur dans le robot...

        public PagePandaMove()
        {
            InitializeComponent();
        }

        private void PagePandaMove_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                btnTrap.Focus();
            }
        }

        private void btnRight_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.PivotRight(90);
            btnTrap.Focus();
        }

        private void btnUp_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.MoveForward(100);
            btnTrap.Focus();
        }

        private void btnDown_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.MoveBackward(100);
            btnTrap.Focus();
        }

        private void btnLeft_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.PivotLeft(90);
            btnTrap.Focus();
        }

        private void btnAsserv_Click(object sender, EventArgs e)
        {
            _asserEna = !_asserEna;

            if (_asserEna)
            {
                Robots.MainRobot.Stop(StopMode.Abrupt);
                btnAsserv.Image = Properties.Resources.GearsOn124;
            }
            else
            {
                Robots.MainRobot.Stop(StopMode.Freely);
                btnAsserv.Image = Properties.Resources.GearsOff124;
            }

            btnTrap.Focus();
        }

        private void btnCalibration_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.SetSpeedVerySlow();
            Robots.MainRobot.Recalibration(SensAR.Arriere, true);
            Robots.MainRobot.SetSpeedFast();
            btnTrap.Focus();
        }

        private void btnFast_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.SetSpeedFast();
        }

        private void btnSlow_Click(object sender, EventArgs e)
        {
            Robots.MainRobot.SetSpeedVerySlow();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PanelStorage.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

using GoBot.Threading;
using GoBot.Actionneurs;

namespace GoBot.IHM.Panels
{
    public partial class PanelStorage : UserControl
    {
        int _lateralPosition = 0;
        int _verticalPosition = 0;

        bool _isOnLeft = false;
        ThreadLink _linkAnimation;

        Color _red, _green;

        List<Color> _leftStorage, _rightStorage;

        Color _onGround;

        public PanelStorage()
        {
            InitializeComponent();

            _red = Color.FromArgb(150, 0, 0);
            _green = Color.FromArgb(0, 150, 0);

            _onGround = Color.Transparent;

            _rightStorage = new List<Color>
            {
                Color.Transparent,
                Color.Transparent,
                Color.Transparent
            };

            _leftStorage = new List<Color>
            {
                Color.Transparent,
                Color.Transparent,
                Color.Transparent
            };

            Bitmap img;
            img = new Bitmap(btnSpawnGreen.Width, btnSpawnGreen.Height);
            Graphics g = Graphics.FromImage(img);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.Clear(Color.Transparent);
            PaintBuoy(g, new PointF(img.Width / 2, 6), _green, 0.45f);
            g.Dispose();
            btnSpawnGreen.Image = img;

            img = new Bitmap(btnSpawnRed.Width, btnSpawnRed.Height);
            g = Graphics.FromImage(img);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.Clear(Color.Transparent);
            PaintBuoy(g, new PointF(img.Width / 2, 6), _red, 0.45f);
            g.Dispose();
            btnSpawnRed.Image = img;
        }

        private void picStorage_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            Rectangle r = new Rectangle(50 + _lateralPosition, 143, 260, 300);

            Brush bsh = new LinearGradientBrush(r, Color.White, Color.WhiteSmoke, 30);
            e.Graphics.FillRectangle(bsh, r);
            e.Graphics.DrawRectangle(Pens.LightGray, r);

            PaintBuoy(e.Graphics, new PointF(250, 450 - _verticalPosition), _onGround, 1.2f);

            PaintBuoy(e.Graphics, new PointF(110 + _lateralPosition, 290), _leftStorage[2], 1.2f);
            PaintLocker(e.Graphics, new PointF(110 + _lateralPosition, 290), true);
            PaintBuoy(e.Graphics, new PointF(110 + _lateralPosition, 220), _leftStorage[1], 1.2f);
            PaintLocker(e.Graphics, new PointF(110 + _lateralPosition, 220), true);
            PaintBuoy(e.Graphics, new PointF(110 + _lateralPosition, 150), _leftStorage[0], 1.2f);
            PaintLocker(e.Graphics, new PointF(110 + _lateralPosition, 150), true);

            PaintBuoy(e.Graphics, new PointF(250 + _lateralPosition, 290), _rightStorage[2], 1.2f);
            PaintLocker(e.Graphics, new PointF(250 + _lateralPosition, 290), false);
            PaintBuoy(e.Graphics, new PointF(250 + _lateralPosition, 220), _rightStorage[1], 1.2f);
            PaintLocker(e.Graphics, new PointF(250 + _lateralPosition, 220), false);
            PaintBuoy(e.Graphics, new PointF(250 + _lateralPosition, 150), _rightStorage[0], 1.2f);
            PaintLocker(e.Graphics, new PointF(250 + _lateralPosition, 150), false);
        }

        private void PaintBuoy(Graphics g, PointF center, ColorPlus color, float scale)
        {
            if (color.Alpha != 0)
            {
                List<PointF> pts = BuoyPoints(center, scale);

                Brush bsh = new LinearGradientBrush(new PointF(pts.Min(o => o.X), pts.Min(o => o.Y)), new PointF(pts.Max(o => o.X), pts.Min(o => o.Y)), ColorPlus.FromAhsl(color.Alpha, color.Hue, color.Saturation, Math.Min(color.Lightness * 2, 1)), color);
                g.FillPolygon(bsh, pts.ToArray());
                bsh.Dispose();

                g.DrawPolygon(Pens.Black, pts.ToArray());
            }
        }

        private void PaintLocker(Graphics g, PointF center, bool onLeft)
        {
            List<PointF> pts = LockerPoints(center, onLeft);

            //g.DrawLines(Pens.Black, pts.ToArray());
            g.FillPolygon(Brushes.WhiteSmoke, pts.ToArray());
            g.DrawPolygon(Pens.Black, pts.ToArray());
        }

        private List<PointF> BuoyPoints(PointF topCenter, float scale)
        {
            float topWidth, bottomWidth, height;

            topWidth = 54;
            bottomWidth = 72;
            height = 115;

            List<PointF> pts = new List<PointF>();
            pts.Add(new PointF(topCenter.X - topWidth / 2, topCenter.Y + 0));
            pts.Add(new PointF(topCenter.X + topWidth / 2, topCenter.Y + 0));
            pts.Add(new PointF(topCenter.X + bottomWidth / 2, topCenter.Y + height));
            pts.Add(new PointF(topCenter.X - bottomWidth / 2, topCenter.Y + height));

            return pts.Select(o => new PointF((o.X - topCenter.X) * scale + topCenter.X, (o.Y - topCenter.Y) * scale + topCenter.Y)).ToList();
        }

        private List<PointF> LockerPoints(PointF topCenter, bool onLeft)
        {
            float scale, bottomWidth, height;

            scale = 1.2f;
            bottomWidth = 72;
            height = 115;

            PointF p;
            int factor;

            if (onLeft)
            {
                p = new PointF(topCenter.X - bottomWidth / 2 + 2, topCenter.Y + height + 1);
                factor = 1;
            }
            else
            {
                p = new PointF(topCenter.X + bottomWidth / 2 - 2, topCenter.Y + height + 1);
                factor = -1;
            }

            List<PointF> pts = new List<PointF>();
            pts.Add(p);

            pts.Add(new PointF(p.X - 4 * factor, p.Y + 8));
            pts.Add(new PointF(p.X - 11 * factor, p.Y + 8));
            pts.Add(new PointF(p.X - 12 * factor, p.Y + 7));
            pts.Add(new PointF(p.X - 12 * factor, p.Y - 10));
            pts.Add(new PointF(p.X - 8 * factor, p.Y - 10));
            pts.Add(new PointF(p.X - 6 * factor, p.Y));

            return pts.Select(o => new PointF((o.X - topCenter.X) * scale + topCenter.X, (o.Y - topCenter.Y) * scale + topCenter.Y)).ToList();
        }

        private void btnLateral_Click(object sender, EventArgs e)
        {
            _isOnLeft = !_isOnLeft;

            if (_linkAnimation != null)
            {
                _linkAnimation.Cancel();
                _linkAnimation.WaitEnd();
            }

            if (!_isOnLeft)
            {
                Actionneur.ElevatorRight.DoPushInside();
                btnLateral.Image = Properties.Resources.BigArrow;
            }
            else
            {
                Actionneur.ElevatorRight.DoPushOutside();
                Bitmap bmp = new Bitmap(Properties.Resources.BigArrow);
                bmp.RotateFlip(RotateFlipType.RotateNoneFlipX);
                btnLateral.Image = bmp;
            }

            _linkAnimation = ThreadManager.CreateThread(link => ThreadLateral(!_isOnLeft ? -4 : 4));
            _linkAnimation.StartInfiniteLoop(15);

            picStorage.Focus();
        }

        private void ThreadLateral(int offset)
        {
            _lateralPosition += offset;
            picStorage.Invalidate();

            if (_lateralPosition >= 140)
            {
                _lateralPosition = 140;
                _linkAnimation.Cancel();
            }
            else if (_lateralPosition <= 0)
            {
                _lateralPosition = 0;
                _linkAnimation.Cancel();
            }
        }

        private void ThreadVertical(int offset)
        {
            int index = 0;
            int maxVertical = 0;

            _verticalPosition += offset;
            picStorage.Invalidate();

            if (_isOnLeft)
            {
                if (_leftStorage[0] == Color.Transparent)
                {
                    maxVertical = 450 - 150;
                    index = 0;
                }
                else if (_leftStorage[1] == Color.Transparent)
                {
                    maxVertical = 450 - 220;
                    index = 1;
                }
                else if (_leftStorage[2] == Color.Transparent)
                {
                    maxVertical = 450 - 290;
                    index = 2;
                }
            }
            else
            {
                if (_rightStorage[0] == Color.Transparent)
                {
                    maxVertical = 450 - 150;
                    index = 0;
                }
                else if (_rightStorage[1] == Color.Transparent)
                {
                    maxVertical = 450 - 220;
                    index = 1;
                }
                else if (_rightStorage[2] == Color.Transparent)
                {
                    maxVertical = 450 - 290;
                    index = 2;
                }
            }

            if (_verticalPosition >= maxVertical)
            {
                if (_isOnLeft)
                    _leftStorage[index] = _onGround;
                else
                    _rightStorage[index] = _onGround;

                _verticalPosition = 0;
                _linkAnimation.Cancel();

                _onGround = Color.Transparent;

                this.InvokeAuto(() =>
                {
                    btnSpawnGreen.Visible = true;
                    btnSpawnRed.Visible = true;
                });
            }
            else if (_verticalPosition <= 0)
            {
                _verticalPosition = 0;
                _linkAnimation.Cancel();
            }
        }

        private void btnSpawnGreen_Click(object sender, EventArgs e)
        {
            _onGround = _green;
            picStorage.Invalidate();
            picStorage.Focus();

            btnSpawnGreen.Visible = false;
            btnSpawnRed.Visible = false;
        }

        private void btnSpawnRed_Click(object sender, EventArgs e)
        {
            _onGround = _red;
            picStorage.Invalidate();
            picStorage.Focus();

            btnSpawnGreen.Visible = false;
            btnSpawnRed.Visible = false;
        }

        private void btnUp_Click(object sender, EventArgs e)
        {
            if (_linkAnimation != null)
            {
                _linkAnimation.Cancel();
                _linkAnimation.WaitEnd();
            }

            _linkAnimation = ThreadManager.CreateThread(link => ThreadVertical(4));
            _linkAnimation.StartInfiniteLoop(10);

            picStorage.Focus();
        }

        private void btnDown_Click(object sender, EventArgs e)
        {
            if (_linkAnimation != null)
            {
                _linkAnimation.Cancel();
                _linkAnimation.WaitEnd();
            }

            List<Color> colors = _isOnLeft ? _leftStorage : _rightStorage;
            int index = 0;

            if (colors[2] != Color.Transparent)
            {
                _verticalPosition = 450 - 290;
                index = 2;
            }
            else if (colors[1] != Color.Transparent)
            {
                _verticalPosition = 450 - 220;
                index = 1;
            }
            else if (colors[0] != Color.Transparent)
            {
                _verticalPosition = 450 - 150;
                index = 0;
            }

            btnSpawnGreen.Visible = false;
            btnSpawnRed.Visible = false;

            _onGround = colors[index];
            colors[index] = Color.Transparent;

            _linkAnimation = ThreadManager.CreateThread(link => ThreadVertical(-4));
            _linkAnimation.StartInfiniteLoop(10);

            picStorage.Focus();
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/Hokuyo/HokuyoRec.cs
﻿using Geometry;
using Geometry.Shapes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Devices
{
    class HokuyoRec : Hokuyo
    {
        private double _deltaX, _deltaY;

        public HokuyoRec(LidarID id) : base(id)
        {
            //_model = "UBG-04LX-F01";
            //_pointsCount = 725;
            _scanRange = 240;
            //_pointsOffset = 44;
            _distanceMinLimit = 50;
            _distanceMaxLimit = 600;
            _keepFrom = 175;
            _keepTo = 575;
            _invertRotation = false;
            _resolution = 240 / 725.0;

            _deltaX = 112;
            _deltaY = 0;
        }

        protected override void SendMessage(string msg)
        {
            // non...
        }

        protected override String GetResponse(int timeout = 5000)
        {
            Position robotPos;

            String mesure = Robots.MainRobot.ReadLidarMeasure(ID, timeout, out robotPos);

            if (robotPos != null)
                _position = new Position(robotPos.Angle, new RealPoint(robotPos.Coordinates.X + _deltaX, robotPos.Coordinates.Y + _deltaY).Rotation(new AngleDelta(robotPos.Angle), robotPos.Coordinates));

            return mesure;
        }

        public string ReadMessage()
        {
            return GetResponse();
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageLogCan.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Threading;

using GoBot.Communications;
using GoBot.Communications.CAN;

namespace GoBot.IHM.Pages
{
    public partial class PageLogCan : UserControl
    {
        private FramesLog _log;
        private Dictionary<CanBoard, Color> _boardColor;

        private DateTime _startTime;
        private DateTime _previousTime, _previousDisplayTime;
        private System.Windows.Forms.Timer _displayTimer;
        private int _counter = 0;
        private Thread _thReplay;

        private List<CheckedListBox> _boxLists;

        bool _loading;

        public PageLogCan()
        {
            InitializeComponent();

            _loading = true;

            dgvLog.Columns.Add("ID", "ID");
            dgvLog.Columns[0].Width = 40;
            dgvLog.Columns.Add("Heure", "Heure");
            dgvLog.Columns[1].Width = 80;
            dgvLog.Columns.Add("Expediteur", "Expediteur");
            dgvLog.Columns[2].Width = 70;
            dgvLog.Columns.Add("Destinataire", "Destinataire");
            dgvLog.Columns[3].Width = 70;
            dgvLog.Columns.Add("Message", "Message");
            dgvLog.Columns[4].Width = 300;
            dgvLog.Columns.Add("Trame", "Trame");
            dgvLog.Columns[5].Width = dgvLog.Width - 18 - dgvLog.Columns[0].Width - dgvLog.Columns[1].Width - dgvLog.Columns[2].Width - dgvLog.Columns[3].Width - dgvLog.Columns[4].Width;

            _boardColor = new Dictionary<CanBoard, Color>();
            _boardColor.Add(CanBoard.PC, Color.FromArgb(180, 245, 245));
            _boardColor.Add(CanBoard.CanServo1, Color.FromArgb(70, 255, 220));
            _boardColor.Add(CanBoard.CanServo2, Color.FromArgb(100, 255, 220));
            _boardColor.Add(CanBoard.CanServo3, Color.FromArgb(130, 255, 220));
            _boardColor.Add(CanBoard.CanServo4, Color.FromArgb(160, 255, 220));
            _boardColor.Add(CanBoard.CanServo5, Color.FromArgb(190, 255, 220));
            _boardColor.Add(CanBoard.CanServo6, Color.FromArgb(220, 255, 220));
            _boardColor.Add(CanBoard.CanAlim, Color.FromArgb(250, 255, 220));

            _boxLists = new List<CheckedListBox>();
            _boxLists.Add(lstSender);
            _boxLists.Add(lstReceiver);
            _boxLists.Add(lstFunctions);

            if (Config.CurrentConfig.LogsCanFunctions == null)
                Config.CurrentConfig.LogsCanFunctions = new SerializableDictionary<CanFrameFunction, bool>();
            if (Config.CurrentConfig.LogsCanSenders == null)
                Config.CurrentConfig.LogsCanSenders = new SerializableDictionary<CanBoard, bool>();
            if (Config.CurrentConfig.LogsCanReceivers== null)
                Config.CurrentConfig.LogsCanReceivers = new SerializableDictionary<CanBoard, bool>();

            // L'ajout de champs déclenche le SetCheck event qui ajoute les éléments automatiquement dans le dictionnaire
            foreach (CanFrameFunction func in Enum.GetValues(typeof(CanFrameFunction)))
            {
                if (!Config.CurrentConfig.LogsCanFunctions.ContainsKey(func))
                    Config.CurrentConfig.LogsCanFunctions.Add(func, true);

                lstFunctions.Items.Add(func.ToString(), Config.CurrentConfig.LogsCanFunctions[func]);
            }

            foreach (CanBoard board in Enum.GetValues(typeof(CanBoard)))
            {
                if (!Config.CurrentConfig.LogsCanSenders.ContainsKey(board))
                    Config.CurrentConfig.LogsCanSenders.Add(board, true);
                lstSender.Items.Add(board.ToString(), Config.CurrentConfig.LogsCanSenders[board]);

                if (!Config.CurrentConfig.LogsCanReceivers.ContainsKey(board))
                    Config.CurrentConfig.LogsCanReceivers.Add(board, true);
                lstReceiver.Items.Add(board.ToString(), Config.CurrentConfig.LogsCanReceivers[board]);
            }

            _loading = false;
            _log = new FramesLog();
        }

        #region Publiques

        public void Clear()
        {
            _log = new FramesLog();
        }

        public void DisplayFrame(TimedFrame tFrame)
        {
            String time = "";

            try
            {
                if (rdoTimeAbsolute.Checked)
                    time = tFrame.Date.ToString("hh:mm:ss:fff");
                if (rdoTimeFromStart.Checked)
                    time = (tFrame.Date - _startTime).ToString(@"hh\:mm\:ss\:fff");
                if (rdoTimeFromPrev.Checked)
                    time = ((int)(tFrame.Date - _previousTime).TotalMilliseconds).ToString() + " ms";
                if (rdoTimeFromPrevDisplay.Checked)
                    time = ((int)(tFrame.Date - _previousDisplayTime).TotalMilliseconds).ToString() + " ms";

                CanBoard board = CanFrameFactory.ExtractBoard(tFrame.Frame);
                CanBoard sender = CanFrameFactory.ExtractSender(tFrame.Frame, tFrame.IsInputFrame);
                CanBoard receiver = CanFrameFactory.ExtractReceiver(tFrame.Frame, tFrame.IsInputFrame);

                if (board == CanBoard.PC) throw new Exception();

                bool receiverVisible = Config.CurrentConfig.LogsCanReceivers[receiver];
                bool senderVisible = Config.CurrentConfig.LogsCanSenders[sender];
                bool functionVisible = Config.CurrentConfig.LogsCanFunctions[CanFrameFactory.ExtractFunction(tFrame.Frame)];

                if (senderVisible && receiverVisible && functionVisible)
                {
                    dgvLog.Rows.Add(_counter, time, sender.ToString(), receiver.ToString(), CanFrameDecoder.Decode(tFrame.Frame), tFrame.Frame.ToString());
                    _previousDisplayTime = tFrame.Date;

                    if (rdoColorByBoard.Checked)
                        dgvLog.Rows[dgvLog.Rows.Count - 1].DefaultCellStyle.BackColor = _boardColor[board];
                    else if (rdoColorByReceiver.Checked)
                        dgvLog.Rows[dgvLog.Rows.Count - 1].DefaultCellStyle.BackColor = _boardColor[receiver];
                    else if (rdoColorBySender.Checked)
                        dgvLog.Rows[dgvLog.Rows.Count - 1].DefaultCellStyle.BackColor = _boardColor[sender];
                }
            }
            catch (Exception e)
            {
                dgvLog.Rows.Add(_counter, time, "?", "?", "Inconnu !", tFrame.Frame.ToString());
                dgvLog.Rows[dgvLog.Rows.Count - 1].DefaultCellStyle.BackColor = Color.Red;
            }

            _counter++;
            _previousTime = tFrame.Date;
        }

        public void DisplayLog()
        {
            try
            {
                dgvLog.Rows.Clear();

                if (_log.Frames.Count > 0)
                {

                    _startTime = _log.Frames[0].Date;
                    _previousTime = _log.Frames[0].Date;
                    _previousDisplayTime = _log.Frames[0].Date;
                    _counter = 0;

                    for (int iFrame = 0; iFrame < _log.Frames.Count; iFrame++)
                        DisplayFrame(_log.Frames[iFrame]);
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Impossible de décoder toutes les trames contenues dans ce fichier.", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void LoadLog()
        {
            OpenFileDialog open = new OpenFileDialog();
            open.Filter = "Fichiers replay trames (*" + FramesLog.FileExtension + ")| *" + FramesLog.FileExtension + "";
            open.Multiselect = true;

            if (open.ShowDialog() == DialogResult.OK)
            {
                foreach (String fichier in open.FileNames)
                {
                    LoadLog(fichier);
                }
                _log.Sort();
                DisplayLog();
            }
        }

        public void LoadLog(String file)
        {
            FramesLog log = new FramesLog();
            log.Import(file);

            foreach (TimedFrame t in log.Frames)
                _log.Frames.Add(t);
        }

        #endregion

        #region Privées

        private void EnableAllCheckBoxes(bool enable)
        {
            for (int iList = 0; iList < _boxLists.Count; iList++)
                for (int iItem = 0; iItem < _boxLists[iList].Items.Count; iItem++)
                    _boxLists[iList].SetItemChecked(iItem, enable);
        }

        private TimedFrame GetFrameFromLine(DataGridViewRow line)
        {
            return _log.Frames[Convert.ToInt32(dgvLog["ID", line.Index].Value)];
        }

        private void ShowFramesReceiver(CanBoard board, bool show)
        {
            lstReceiver.Items.Remove(board.ToString());
            lstReceiver.Items.Add(board.ToString(), show);
            Config.CurrentConfig.LogsCanReceivers[board] = show;
        }

        private void ShowFramesSender(CanBoard board, bool show)
        {
            lstSender.Items.Remove(board.ToString());
            lstSender.Items.Add(board.ToString(), show);
            Config.CurrentConfig.LogsCanSenders[board] = show;
        }

        private void ShowFrameFunction(CanFrameFunction func, bool show)
        {
            lstFunctions.Items.Remove(func.ToString());
            lstFunctions.Items.Add(func.ToString(), show);
            Config.CurrentConfig.LogsCanFunctions[func] = show;
        }

        private FramesLog CreateLogFromSelection()
        {
            FramesLog logSelection = new FramesLog();

            foreach (DataGridViewRow line in dgvLog.SelectedRows)
            {
                TimedFrame trameReplay = GetFrameFromLine(line);
                logSelection.AddFrame(trameReplay.Frame, trameReplay.IsInputFrame, trameReplay.Date);
            }

            return logSelection;
        }

        private void ReplayLog(FramesLog log)
        {
            _thReplay = new Thread(_log.ReplayInputFrames);
            _thReplay.Start();
        }

        #endregion

        #region Events

        private void btnReplayAll_Click(object sender, EventArgs e)
        {
            ReplayLog(_log);
        }

        private void btnReplaySelected_Click(object sender, EventArgs e)
        {
            ReplayLog(CreateLogFromSelection());
        }

        private void lstSender_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!Execution.DesignMode && !_loading)
            {
                String boardStr = (String)lstSender.Items[e.Index];
                CanBoard board = (CanBoard)Enum.Parse(typeof(CanBoard), boardStr);

                Config.CurrentConfig.LogsCanReceivers[board] = (e.NewValue == CheckState.Checked);
            }
        }

        private void lstReceiver_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!Execution.DesignMode && !_loading)
            {
                String boardStr = (String)lstReceiver.Items[e.Index];
                CanBoard board = (CanBoard)Enum.Parse(typeof(CanBoard), boardStr);

                Config.CurrentConfig.LogsCanReceivers[board] = (e.NewValue == CheckState.Checked);
            }
        }

        private void lstFunctions_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!Execution.DesignMode && !_loading)
            {
                String funcStr = (String)lstFunctions.Items[e.Index];
                CanFrameFunction func = (CanFrameFunction)Enum.Parse(typeof(CanFrameFunction), funcStr);

                Config.CurrentConfig.LogsCanFunctions[func] = (e.NewValue == CheckState.Checked);
            }
        }

        private void mnuHideSameSenderFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.SelectedRows)
                {
                    TimedFrame tFrame = GetFrameFromLine(line);
                    ShowFramesSender(CanFrameFactory.ExtractSender(tFrame.Frame, tFrame.IsInputFrame), false);
                }

                DisplayLog();
            }
        }

        private void mnuHideSameReceiverFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.SelectedRows)
                {
                    TimedFrame tFrame = GetFrameFromLine(line);
                    ShowFramesReceiver(CanFrameFactory.ExtractReceiver(tFrame.Frame, tFrame.IsInputFrame), false);
                }

                DisplayLog();
            }
        }

        private void mnuHideSameBoardFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.SelectedRows)
                {
                    CanBoard board = CanFrameFactory.ExtractBoard(GetFrameFromLine(line).Frame);
                    ShowFramesReceiver(board, false);
                    ShowFramesSender(board, false);
                }

                DisplayLog();
            }
        }

        private void mnuHideAllFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.Rows)
                {
                    TimedFrame tFrame = GetFrameFromLine(line);
                    ShowFrameFunction(CanFrameFactory.ExtractFunction(tFrame.Frame), false);
                }

                DisplayLog();
            }
        }

        private void mnuFrameCopy_Click(object sender, EventArgs e)
        {
            if (dgvLog.Rows.Count > 0)
                Clipboard.SetText(GetFrameFromLine(dgvLog.SelectedRows[0]).Frame.ToString());
        }

        private void btnAllCheck_Click(object sender, EventArgs e)
        {
            EnableAllCheckBoxes(true);
        }

        private void btnAllUncheck_Click(object sender, EventArgs e)
        {
            EnableAllCheckBoxes(false);
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            DisplayLog();
        }

        private void dgvLog_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right && e.RowIndex > 0 && e.ColumnIndex > 0)
                dgvLog.CurrentCell = dgvLog.Rows[e.RowIndex].Cells[e.ColumnIndex];
        }

        private void mnuHideSameTypeFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.SelectedRows)
                {
                    TimedFrame tFrame = GetFrameFromLine(line);
                    ShowFrameFunction(CanFrameFactory.ExtractFunction(tFrame.Frame), false);
                }

                DisplayLog();
            }
        }

        private void btnDisplay_Click(object sender, EventArgs e)
        {
            if (_displayTimer == null || !_displayTimer.Enabled)
            {
                _log = new FramesLog();
                _displayTimer = new System.Windows.Forms.Timer();
                _displayTimer.Interval = 1000;
                _displayTimer.Tick += displayTimer_Tick;
                _displayTimer.Start();

                Connections.ConnectionCan.FrameReceived += new CanConnection.NewFrameDelegate((frame) => _log.AddFrame(frame, true));
                Connections.ConnectionCan.FrameSend += new CanConnection.NewFrameDelegate((frame) => _log.AddFrame(frame, false));

                btnReplayAll.Enabled = false;
                btnReplaySelected.Enabled = false;
                btnLoad.Enabled = false;
                btnDisplay.Text = "Arrêter la surveillance";
                btnDisplay.Image = Properties.Resources.GlassPause48;
            }
            else
            {
                _displayTimer.Stop();


                Connections.ConnectionCan.FrameReceived -= new CanConnection.NewFrameDelegate((frame) => _log.AddFrame(frame, true));
                Connections.ConnectionCan.FrameSend -= new CanConnection.NewFrameDelegate((frame) => _log.AddFrame(frame, false));

                btnReplayAll.Enabled = true;
                btnReplaySelected.Enabled = true;
                btnLoad.Enabled = true;
                btnDisplay.Text = "Lancer la surveillance";
                btnDisplay.Image = Properties.Resources.GlassStart48;
            }
        }

        void displayTimer_Tick(object sender, EventArgs e)
        {
            int nbTrames = _log.Frames.Count;
            for (int i = _counter; i < nbTrames; i++)
                DisplayFrame(_log.Frames[i]);

            if (boxScroll.Checked && dgvLog.Rows.Count > 10)
                dgvLog.FirstDisplayedScrollingRowIndex = dgvLog.RowCount - 1;
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            LoadLog();
        }

        #endregion

    }
}
<file_sep>/GoBot/Geometry/Maths.cs
﻿using System;

using Geometry.Shapes;

namespace Geometry
{
    public struct Direction
    {
        public AngleDelta angle;
        public double distance;
    }

    public class Maths
    {
        /// <summary>
        /// Retourne la direction (angle et distance) à suivre pour arriver à un point donné en partant d'une corrdonnée précise (et par défaut, angle de 0°)
        /// </summary>
        /// <param name="startPoint">Coordonnées de départ</param>
        /// <param name="endPoint">Coordonnées d'arrivée</param>
        /// <returns>Direction à suivre</returns>
        public static Direction GetDirection(RealPoint startPoint, RealPoint endPoint)
        {
            Position startPosition = new Position(0, startPoint);
            return GetDirection(startPosition, endPoint);
        }

        /// <summary>
        ///  Retourne la direction (angle et distance) à suivre pour arriver à un point donné en partant d'une position précise (coordonnées et angle)
        /// </summary>
        /// <param name="startPosition">Position de départ</param>
        /// <param name="endPoint">Coordonnées d'arrivée</param>
        /// <returns>Direction à suivre</returns>
        public static Direction GetDirection(Position startPosition, RealPoint endPoint)
        {
            Direction result = new Direction();

            result.distance = startPosition.Coordinates.Distance(endPoint);

            double angleCalc = 0;

            // Deux points sur le même axe vertical : 90° ou -90° selon le point le plus haut
            if (endPoint.X == startPosition.Coordinates.X)
            {
                angleCalc = Math.PI / 2;
                if (endPoint.Y > startPosition.Coordinates.Y)
                    angleCalc = -angleCalc;
            }
            // Deux points sur le même axe horizontal : 0° ou 180° selon le point le plus à gauche
            else if (endPoint.Y == startPosition.Coordinates.Y)
            {
                angleCalc = Math.PI;
                if (endPoint.X > startPosition.Coordinates.X)
                    angleCalc = 0;
            }
            // Cas général : Calcul de l'angle
            else
            {
                angleCalc = Math.Acos((endPoint.X - startPosition.Coordinates.X) / result.distance);

                if (endPoint.Y > startPosition.Coordinates.Y)
                    angleCalc = -angleCalc;
            }

            // Prendre en compte l'angle initial
            AngleDelta angle = new AngleDelta(angleCalc, AngleType.Radian);
            angle = angle + startPosition.Angle;
            result.angle = angle.Modulo();

            return result;
        }

        /// <summary>
        /// Retourne les coordonnées d'une coordonnée initiale modifiée par une prise de direction
        /// </summary>
        /// <param name="startPoint">Coordonnée de départ</param>
        /// <param name="direction">Direction suivie</param>
        /// <returns>Coordonnées du point</returns>
        public static RealPoint GetDestination(RealPoint startPoint, Direction direction)
        {
            double x = startPoint.X + direction.angle.Cos * direction.distance;
            double y = startPoint.Y - direction.angle.Sin * direction.distance;

            return new RealPoint(new RealPoint(x, y));
        }

        /// <summary>
        /// Retourne les coordonnées d'une position initiale modifiée par une prise de direction
        /// </summary>
        /// <param name="startPosition">Position de départ</param>
        /// <param name="direction">Direction suivie</param>
        /// <returns>Coordonnées du point</returns>
        public static Position GetDestination(Position startPosition, Direction direction)
        {
            AnglePosition endAngle = startPosition.Angle + direction.angle;

            double x = startPosition.Coordinates.X + endAngle.Cos * direction.distance;
            double y = startPosition.Coordinates.Y - endAngle.Sin * direction.distance;

            return new Position(endAngle, new RealPoint(x, y));
        }

        /// <summary>
        /// Retourne la longueur de l'hypothenuse d'un triangle rectangle à partir des longueur des 2 autres côtés.
        /// </summary>
        /// <param name="side1">Longueur du 1er côté</param>
        /// <param name="side2">Longueur du 2ème côté</param>
        /// <returns>Longueur de l'hypothenuse</returns>
        public static double Hypothenuse(double side1, double side2)
        {
            return Math.Sqrt(side1 * side1 + side2 * side2);
        }

        public static double Scale(double value, double oldMax, double newMax)
        {
            return value / oldMax * newMax;
        }

        public static double Scale(double value, double oldMin, double oldMax, double newMin, double newMax)
        {
            return (value - oldMin) / (oldMax - oldMin) * (newMax - newMin) + newMin;
        }
    }
}
<file_sep>/GoBot/Geometry/Util.cs
﻿using System;

namespace Geometry
{
    public static class Util
    {
        public static dynamic ToRealType(Object o)
        {
            Type type = o.GetType();
            dynamic pp = Convert.ChangeType(o, type);
            return pp;
        }
    }
}
<file_sep>/GoBot/GoBot/Historique.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GoBot.Actions;
using System.Threading;
using System.Xml.Serialization;
using System.IO;

namespace GoBot
{
    [Serializable]
    public enum TypeLog
    {
        Action,
        Strat,
        PathFinding
    }
        
    [Serializable]
    public class HistoLigne : IComparable
    {
        private static int idActuel = 0;

        public int ID { get; set; }
        public String Message { get; set; }
        public DateTime Heure { get; set; }
        public TypeLog Type {get;set;}
        public IDRobot Robot { get; set; }

        public HistoLigne(IDRobot robot, DateTime heure, String message, TypeLog type = TypeLog.Strat)
        {
            ID = idActuel++;
            Robot = robot;
            Message = message;
            Heure = heure;
            Type = type;
        }

        public HistoLigne()
        {
        }

        public int CompareTo(object obj)
        {
            if (ID == ((HistoLigne)obj).ID)
                return Heure.CompareTo(((HistoLigne)obj).Heure);
            else
                return ID.CompareTo(((HistoLigne)obj).ID);
        }
    }

    public class Historique
    {
        int NBACTIONSMAX = 10;
        public IDRobot Robot { get; private set; }
        private List<IAction> actions;
        public List<IAction> Actions
        {
            get
            {
                return actions;
            }
        }

        public delegate void DelegateAction(IAction action);
        public event DelegateAction NouvelleAction;
        public delegate void DelegateLog(HistoLigne ligne);
        public event DelegateLog NouveauLog;

        public Historique(IDRobot robot)
        {
            Robot = robot;
            actions = new List<IAction>();
            HistoriqueLignes = new List<HistoLigne>();
        }

        public void AjouterAction(IAction action)
        {
            /*Actions.Add(action);
            Log(action.ToString(), TypeLog.Action);

            while (Actions.Count > NBACTIONSMAX)
                Actions.RemoveAt(0);

            if (NouvelleAction != null)
                NouvelleAction(action);*/
        }

        public void Log(String message, TypeLog type = TypeLog.Strat)
        {
            HistoLigne ligne = new HistoLigne(Robot, DateTime.Now, message, type);
            HistoriqueLignes.Add(ligne);
            if (NouveauLog != null)
                NouveauLog(ligne);
        }

        public List<HistoLigne> HistoriqueLignes { get; set; }

        /// <summary>
        /// Charge une sauvegarde d'historique
        /// </summary>
        /// <param name="nomFichier">Chemin du fichier</param>
        /// <returns>Vrai si la sauvegarde a été correctement chargée</returns>
        public bool Charger(String nomFichier)
        {
            try
            {
                XmlSerializer mySerializer = new XmlSerializer(typeof(List<HistoLigne>));
                using (FileStream myFileStream = new FileStream(nomFichier, FileMode.Open))
                    HistoriqueLignes = (List<HistoLigne>)mySerializer.Deserialize(myFileStream);

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Sauvegarde l'ensemble de l'historique dans un fichier
        /// </summary>
        /// <param name="nomFichier">Chemin du fichier</param>
        /// <returns>Vrai si la sauvegarde s'est correctement déroulée</returns>
        public bool Sauvegarder(String nomFichier)
        {
            try
            {
                XmlSerializer mySerializer = new XmlSerializer(typeof(List<HistoLigne>));
                using (StreamWriter myWriter = new StreamWriter(nomFichier))
                    mySerializer.Serialize(myWriter, HistoriqueLignes);

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/SpeedSampler.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot
{
    class SpeedSample
    {
        private List<double> _pos, _speed;
        private List<TimeSpan> _times;

        public SpeedSample(List<TimeSpan> times, List<double> positions, List<double> speeds)
        {
            _times = times;
            _pos = positions;
            _speed = speeds;
        }

        public List<double> Positions { get { return _pos; } }
        public List<double> Speeds { get { return _speed; } }
        public List<TimeSpan> Times { get { return _times; } }
        public TimeSpan Duration { get { return _times[_times.Count - 1] - _times[0]; } }
        public bool Valid { get { return _pos.Count > 0; } }
    }

    class SpeedSampler
    {
        private SpeedConfig _config;

        public SpeedSampler(SpeedConfig config)
        {
            _config = config;
        }

        public SpeedSample SampleLine(int startPos, int endPos, int samplesCnt)
        {
            TimeSpan accelDuration, maxSpeedDuration, brakingDuration, totalDuration;
            totalDuration = _config.LineDuration(Math.Abs(endPos - startPos), out accelDuration, out maxSpeedDuration, out brakingDuration);

            TimeSpan division = new TimeSpan(totalDuration.Ticks / samplesCnt);

            List<double> speeds = new List<double>();
            List<double> positions = new List<double>();
            List<TimeSpan> times = new List<TimeSpan>();

            double currentSpeed = 0, currentPosition = startPos;
            int direction = ((endPos - startPos) > 0 ? 1 : -1);
            double accelPerDiv = (_config.LineAcceleration * division.TotalSeconds) * direction;
            TimeSpan currentTime = new TimeSpan();

            while (speeds.Count < samplesCnt)
            {
                speeds.Add(currentSpeed);
                positions.Add(currentPosition);
                times.Add(currentTime);

                currentTime += division;

                if (currentTime < accelDuration)
                    currentSpeed += accelPerDiv;
                else if (currentTime > accelDuration + maxSpeedDuration)
                    currentSpeed -= accelPerDiv;
                else
                    currentSpeed = _config.LineSpeed * direction;

                if(direction < 0)
                    currentSpeed = Math.Min(0, Math.Max(currentSpeed, _config.LineSpeed *direction));
                else
                    currentSpeed = Math.Max(0, Math.Min(currentSpeed, _config.LineSpeed * direction));

                currentPosition += currentSpeed;
            }

            return new SpeedSample(times, positions, speeds);
        }
    }
}
<file_sep>/GoBot/GoBot/Strategies/StrategyEmpty.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Strategies
{
    class StrategyEmpty : Strategy
    {
        public override bool AvoidElements => false;

        protected override void SequenceBegin()
        {
        }

        protected override void SequenceCore()
        {
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/Pepperl/PepperlManager.cs
﻿using Geometry;
using GoBot.Communications;
using GoBot.Communications.UDP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace GoBot.Devices
{
    public class PepperlManager
    {
        public struct PepperlPoint
        {
            public uint distance;
            public ushort amplitude;

            public override string ToString()
            {
                return distance.ToString() + "mm - " + Math.Round(amplitude / 40.95f, 0).ToString() + "%";
            }
        }

        private IPAddress _ip;
        private int _port;
        private String _handle;
        private TimeSpan _timeout;

        private PepperlComm _comm;
        private UDPConnection _udp;

        List<PepperlPoint> _currentMeasure;
        int _currentScan;

        public delegate void NewMeasureHandler(List<PepperlPoint> measure, AnglePosition startAngle, AngleDelta resolution);
        public event NewMeasureHandler NewMeasure;

        public PepperlManager(IPAddress ip, int port)
        {
            _ip = ip;
            _port = port;
            _currentScan = -1;
            _timeout = new TimeSpan(0, 0, 10);

            _comm = new PepperlComm(_ip);
        }

        protected void OnNewMeasure(List<PepperlPoint> measure, AnglePosition startAngle, AngleDelta resolution)
        {
            NewMeasure?.Invoke(_currentMeasure, startAngle, resolution);
            _currentMeasure = null;
        }

        public void Reboot()
        {
            _comm.SendCommand(PepperlCmd.Reboot);
        }

        public bool CreateChannelUDP()
        {
            bool ok = false;

            try
            {
                Dictionary<String, String> rep = _comm.SendCommand(PepperlCmd.CreateChannelUDP,
                                        PepperlConst.ParamUdpAddress, FindMyIP().ToString(),
                                        PepperlConst.ParamUdpPort, _port.ToString(),
                                        PepperlConst.ParamUdpWatchdog, PepperlConst.ValueUdpWatchdogOn,
                                        PepperlConst.ParamUdpWatchdogTimeout, _timeout.TotalMilliseconds.ToString(),
                                        PepperlConst.ParamUdpPacketType, PepperlConst.ValueUdpPacketTypeDistanceAmplitudeCompact);

                if (rep != null)
                {
                    _handle = rep[PepperlConst.ParamUdpHandle];

                    if (_handle != "")
                    {
                        _udp = new UDPConnection();
                        _udp.Connect(_ip, _port, _port + 1);
                        _udp.StartReception();
                        _udp.FrameReceived += _udp_FrameReceived;

                        _comm.SendCommand(PepperlCmd.ScanStart,
                                            PepperlConst.ParamUdpHandle, _handle);

                        ok = true;
                    }
                }
            }
            catch (Exception)
            {
                ok = false;

                if (_udp != null && _udp.Connected)
                {
                    _udp.Close();
                    _udp = null;
                }
            }

            return ok;
        }

        public void CloseChannelUDP()
        {
            _comm.SendCommand(PepperlCmd.ScanStop,
                                PepperlConst.ParamUdpHandle, _handle);

            _udp?.Close();
            _udp = null;

            _handle = null;
        }

        private void _udp_FrameReceived(Frame frame)
        {
            int addr = 0;

            ushort magic = (ushort)Read(frame, ref addr, 2);
            ushort packetType = (ushort)Read(frame, ref addr, 2);
            uint packetSize = (uint)Read(frame, ref addr, 4);
            ushort headerSize = (ushort)Read(frame, ref addr, 2);
            ushort scanNumber = (ushort)Read(frame, ref addr, 2);
            ushort packetNumber = (ushort)Read(frame, ref addr, 2);
            long timestampRaw = (long)Read(frame, ref addr, 8);
            long timestampSync = (long)Read(frame, ref addr, 8);
            uint statusFlag = (uint)Read(frame, ref addr, 4);
            uint scanFrequency = (uint)Read(frame, ref addr, 4);
            ushort numPointsScan = (ushort)Read(frame, ref addr, 2);
            ushort numPointsPacket = (ushort)Read(frame, ref addr, 2);
            ushort firstIndex = (ushort)Read(frame, ref addr, 2);
            int firstAngle = ReadInt(frame, ref addr);
            int angularIncrement = ReadInt(frame, ref addr);
            uint iqInput = (uint)Read(frame, ref addr, 4);
            uint iqOverload = (uint)Read(frame, ref addr, 4);
            long iqTimestampRaw = (long)Read(frame, ref addr, 8);
            long iqTimestampSync = (long)Read(frame, ref addr, 8);

            if (firstIndex == 0)
            {
                _currentMeasure = new List<PepperlPoint>();
                _currentScan = scanNumber;
            }

            if (_currentMeasure != null && _currentScan == scanNumber)
            {
                String type = ((char)packetType).ToString();

                if (type == PepperlConst.ValueUdpPacketTypeDistance)
                {
                    for (int iPt = 0; iPt < numPointsPacket; iPt++)
                        _currentMeasure.Add(ReadMeasureA(frame, ref addr));
                }
                else if (type == PepperlConst.ValueUdpPacketTypeDistanceAmplitude)
                {
                    for (int iPt = 0; iPt < numPointsPacket; iPt++)
                        _currentMeasure.Add(ReadMeasureB(frame, ref addr));
                }
                else if (type == PepperlConst.ValueUdpPacketTypeDistanceAmplitudeCompact)
                {
                    for (int iPt = 0; iPt < numPointsPacket; iPt++)
                        _currentMeasure.Add(ReadMeasureC(frame, ref addr));
                }

                if (_currentMeasure.Count == numPointsScan)
                {
                    OnNewMeasure(_currentMeasure, firstAngle / 10000f, angularIncrement / 10000f);
                    _currentMeasure = null;
                }
            }

            // TODO vérification CRC32C ?
        }

        public void FeedWatchDog()
        {
            _comm.SendCommand(PepperlCmd.FeedWatchdog,
                              PepperlConst.ParamFeedWatchdogHandle, _handle);
        }

        public void ShowMessage(String txt1, String txt2)
        {
            _comm.SetParameters(PepperlConst.ParamHmiMode, PepperlConst.ValueHmiModeSoftText,
                                PepperlConst.ParamHmiSoftText1, txt1,
                                PepperlConst.ParamHmiSoftText2, txt2);
        }

        public void SetFrequency(PepperlFreq freq)
        {
            _comm.SetParameters(PepperlConst.ParamScanFrequency, freq.Frequency().ToString(),
                                PepperlConst.ParamSamplesPerScan, freq.SamplesPerScan().ToString());
        }

        public void SetFilter(PepperlFilter filter, int size)
        {
            _comm.SetParameters(PepperlConst.ParamFilterType, filter.GetText(),
                                PepperlConst.ParamFilterWidth, size.ToString());
        }

        private IPAddress FindMyIP()
        {
            return Dns.GetHostAddresses(Dns.GetHostName()).ToList().First(ip => ip.ToString().StartsWith("10.1.0."));
        }

        private long Read(Frame frame, ref int index, int lenght)
        {
            long val = 0;

            for (int i = index + lenght - 1; i >= index; i--)
            {
                val = val << 8;
                val += frame[i];
            }

            index += lenght;

            return val;
        }

        private int ReadInt(Frame frame, ref int index)
        {
            int val = frame[index] | frame[index + 1] << 8 | frame[index + 2] << 16 | frame[index + 3] << 24;

            index += 4;

            return val;
        }

        private PepperlPoint ReadMeasureA(Frame f, ref int i)
        {
            PepperlPoint p;

            p.distance = (uint)Read(f, ref i, 4);
            p.amplitude = 0xFFF;

            if (p.distance == 0xFFFFFFFF) p.distance = 0;

            return p;
        }

        private PepperlPoint ReadMeasureB(Frame f, ref int i)
        {
            PepperlPoint p;
            p.distance = (uint)Read(f, ref i, 4);
            p.amplitude = (ushort)Read(f, ref i, 2);

            if (p.distance == 0xFFFFF) p.distance = 0;

            return p;
        }

        private PepperlPoint ReadMeasureC(Frame f, ref int i)
        {
            long val = Read(f, ref i, 4);

            PepperlPoint p;
            p.distance = (uint)(val & 0xFFFFF);
            p.amplitude = (ushort)(val >> 20);

            if (p.distance == 0xFFFFF) p.distance = 0;

            return p;
        }
    }
}
<file_sep>/GoBot/Geometry/WorldRect.cs
﻿using System.Drawing;

using Geometry.Shapes;

namespace Geometry
{
    public class WorldDimensions
    {
        private Size _screenSize;

        public WorldScale WorldScale { get; protected set; }
        public RectangleF WorldRect { get; protected set; }

        public delegate void WorldChangeDelegate();
        public event WorldChangeDelegate WorldChange;

        public WorldDimensions()
        {
            WorldScale = new WorldScale(5, 0, 0);
            WorldRect = new RectangleF();
        }

        public void SetScreenSize(Size size)
        {
            WorldRect = WorldRect.ExpandWidth(WorldScale.ScreenToRealDistance(size.Width));
            WorldRect = WorldRect.ExpandHeight(WorldScale.ScreenToRealDistance(size.Height));
            _screenSize = size;

            WorldChange?.Invoke();
        }

        public void SetZoomFactor(double mmPerPixel)
        {
            RealPoint center = WorldRect.Center();

            WorldRect = WorldRect.ExpandWidth(WorldRect.Width * (mmPerPixel / WorldScale.Factor));
            WorldRect = WorldRect.ExpandHeight(WorldRect.Height * (mmPerPixel / WorldScale.Factor));

            WorldScale = new WorldScale(mmPerPixel, (int)(-WorldRect.X / mmPerPixel), (int)(-WorldRect.Y / mmPerPixel));

            WorldChange?.Invoke();
        }

        public void SetWorldCenter(RealPoint center)
        {
            WorldRect = WorldRect.SetCenter(center);
            WorldScale = new WorldScale(WorldScale.Factor, -WorldScale.RealToScreenDistance(WorldRect.X), -WorldScale.RealToScreenDistance(WorldRect.Y));

            WorldChange?.Invoke();
        }
    }
}
<file_sep>/GoBot/GoBot/Actions/ActionServo.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionServo : IAction
    {
        private int position;
        ServomoteurID pince;
        private Robot robot;

        public ActionServo(Robot r, int po, ServomoteurID pi)
        {
            robot = r;
            position = po;
            pince = pi;
        }

        public override String ToString()
        {
            return robot + " bouge " + NameFinder.GetName(pince) + " à " + NameFinder.GetName(position, pince);
        }

        void IAction.Executer()
        {
            Devices.AllDevices.CanServos[pince].SetPosition(position);
        }

        public System.Drawing.Image Image
        {
            get 
            { 
                return GoBot.Properties.Resources.Motor16;  
            }
        }
    }
}
<file_sep>/GoBot/GoBot/GameElements/GameElement.cs
﻿using Geometry;
using Geometry.Shapes;
using GoBot.Movements;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using GoBot.BoardContext;

namespace GoBot.GameElements
{
    public abstract class GameElement
    {
        protected RealPoint _position;
        protected bool _isAvailable;
        protected bool _isHover;
        protected int _hoverRadius;
        protected Color _owner;

        /// <summary>
        /// Obtient ou définit si l'élement de jeu est parti et donc n'est plus disponible
        /// </summary>
        public bool IsAvailable
        {
            get { return _isAvailable; }
            set { _isAvailable = value; }
        }

        /// <summary>
        /// Obtient ou définir la couleur de l'action (joueur propriétaire ou blanc)
        /// </summary>
        public Color Owner
        {
            get { return _owner; }
            set { _owner = value; }
        }

        /// <summary>
        /// Position du centre de l'action
        /// </summary>
        public RealPoint Position
        {
            get { return _position; }
            set { _position = value; }
        }
        
        /// <summary>
        /// Obtient ou définit si l'action est actuellement survolée par la souris
        /// </summary>
        public bool IsHover
        {
            get { return _isHover; }
            set { _isHover = value; }
        }
        
        /// <summary>
        /// Obtient ou définit le rayon du srvol de la souris
        /// </summary>
        public int HoverRadius
        {
            get { return _hoverRadius; }
            set { _hoverRadius = value; }
        }

        /// <summary>
        /// Constructeur
        /// </summary>
        /// <param name="position">Position de l'élément</param>
        /// <param name="owner">Couleur d'appartenance de l'élément</param>
        /// <param name="hoverRadius">Rayon de survol de l'élément</param>
        public GameElement(RealPoint position, Color owner, int hoverRadius)
        {
            this._hoverRadius = hoverRadius;
            this._position = position;
            this._owner = owner;
            this._isAvailable = true;
        }

        /// <summary>
        /// Peint l'élément sur le Graphic donné à l'échelle donnée
        /// </summary>
        /// <param name="g">Graphic sur lequel peindre</param>
        /// <param name="scale">Echelle de peinture</param>
        public abstract void Paint(Graphics g, WorldScale scale);

        /// <summary>
        /// Action à executer au clic de la souris
        /// </summary>
        /// <returns>Vrai si l'éction a été correctement executée</returns>
        public virtual bool ClickAction()
        {
            IEnumerable<Movement> movements = GameBoard.Strategy.Movements.Where(m => m.Element == this);

            if(movements.Count() > 0)
            {
                Movement move = movements.OrderBy(m => m.GlobalCost).First();
                return move.Execute();
            }

            return false;
        }
    }
}
<file_sep>/GoBot/GoBot/TicksPerSecond.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace GoBot
{
    class TicksPerSecond
    {
        private int _counter;
        private List<int> _measures;
        private Timer _timer;
        private double _lastValue;
        
        public int MeasuresCount { get; set; }

        public delegate void ValueChangeDelegate(double value);
        public event ValueChangeDelegate ValueChange;

        public TicksPerSecond()
        {
            _measures = new List<int>();
            _counter = 0;
            MeasuresCount = 10;
            _lastValue = -1;
        }

        public void Start()
        {
            if(_timer == null)
            {
                _counter = 0;
                _timer = new Timer(1000);
                _timer.Elapsed += _timer_Elapsed;
                _timer.Start();
            }
        }

        public void Stop()
        {
            if(_timer != null)
            {
                _timer.Stop();
                _timer = null;
                
                _measures.Clear();
                _lastValue = 0;
                ValueChange?.Invoke(_lastValue);
            }
        }

        public void AddTick()
        {
            _counter++;
        }

        private void _timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            _measures.Add(_counter);
            _counter = 0;

            if (_measures.Count > MeasuresCount)
                _measures.RemoveRange(0, _measures.Count - MeasuresCount);

            double newValue = _measures.Average();
            if (newValue != _lastValue)
            {
                _lastValue = newValue;
                ValueChange?.Invoke(_lastValue);
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageLogUdp.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Threading;

using GoBot.Communications;
using GoBot.Communications.UDP;

namespace GoBot.IHM.Pages
{
    public partial class PageLogUdp : UserControl
    {
        private FramesLog _log;
        private Dictionary<Board, Color> _boardColor;

        private DateTime _startTime;
        private DateTime _previousTime, _previousDisplayTime;
        private System.Windows.Forms.Timer _displayTimer;
        private int _counter = 0;
        private Thread _thReplay;

        private List<CheckedListBox> _boxLists;
        private Dictionary<CheckedListBox, Dictionary<UdpFrameFunction, bool>> _configFunctions;
        private Dictionary<Board, CheckedListBox> _lstFunctions;

        bool _loading;

        public PageLogUdp()
        {
            InitializeComponent();

            _loading = true;

            dgvLog.Columns.Add("ID", "ID");
            dgvLog.Columns[0].Width = 40;
            dgvLog.Columns.Add("Heure", "Heure");
            dgvLog.Columns[1].Width = 80;
            dgvLog.Columns.Add("Expediteur", "Expediteur");
            dgvLog.Columns[2].Width = 70;
            dgvLog.Columns.Add("Destinataire", "Destinataire");
            dgvLog.Columns[3].Width = 70;
            dgvLog.Columns.Add("Message", "Message");
            dgvLog.Columns[4].Width = 300;
            dgvLog.Columns.Add("Trame", "Trame");
            dgvLog.Columns[5].Width = dgvLog.Width - 18 - dgvLog.Columns[0].Width - dgvLog.Columns[1].Width - dgvLog.Columns[2].Width - dgvLog.Columns[3].Width - dgvLog.Columns[4].Width;

            _boardColor = new Dictionary<Board, Color>();
            _boardColor.Add(Board.PC, Color.FromArgb(180, 245, 245));
            _boardColor.Add(Board.RecMove, Color.FromArgb(143, 255, 143));
            _boardColor.Add(Board.RecIO, Color.FromArgb(210, 254, 211));
            _boardColor.Add(Board.RecCan, Color.FromArgb(254, 244, 188));

            _boxLists = new List<CheckedListBox>();
            _boxLists.Add(lstSender);
            _boxLists.Add(lstReceiver);
            _boxLists.Add(lstRecIOFunctions);
            _boxLists.Add(lstRecMoveFunctions);
            _boxLists.Add(lstRecGoBotFunctions);
            _boxLists.Add(lstRecCanFunctions);

            if (Config.CurrentConfig.LogsFonctionsMove == null)
                Config.CurrentConfig.LogsFonctionsMove = new SerializableDictionary<UdpFrameFunction, bool>();
            if (Config.CurrentConfig.LogsFonctionsIO == null)
                Config.CurrentConfig.LogsFonctionsIO = new SerializableDictionary<UdpFrameFunction, bool>();
            if (Config.CurrentConfig.LogsFonctionsGB == null)
                Config.CurrentConfig.LogsFonctionsGB = new SerializableDictionary<UdpFrameFunction, bool>();
            if (Config.CurrentConfig.LogsFonctionsCan == null)
                Config.CurrentConfig.LogsFonctionsCan = new SerializableDictionary<UdpFrameFunction, bool>();
            if (Config.CurrentConfig.LogsExpediteurs == null)
                Config.CurrentConfig.LogsExpediteurs = new SerializableDictionary<Board, bool>();
            if (Config.CurrentConfig.LogsDestinataires == null)
                Config.CurrentConfig.LogsDestinataires = new SerializableDictionary<Board, bool>();

            _configFunctions = new Dictionary<CheckedListBox, Dictionary<UdpFrameFunction, bool>>();
            _configFunctions.Add(lstRecIOFunctions, Config.CurrentConfig.LogsFonctionsIO);
            _configFunctions.Add(lstRecMoveFunctions, Config.CurrentConfig.LogsFonctionsMove);
            _configFunctions.Add(lstRecGoBotFunctions, Config.CurrentConfig.LogsFonctionsGB);
            _configFunctions.Add(lstRecCanFunctions, Config.CurrentConfig.LogsFonctionsCan);

            _lstFunctions = new Dictionary<Board, CheckedListBox>();
            _lstFunctions.Add(Board.RecIO, lstRecIOFunctions);
            _lstFunctions.Add(Board.RecMove, lstRecMoveFunctions);
            _lstFunctions.Add(Board.RecCan, lstRecCanFunctions);

            foreach (CheckedListBox lst in _configFunctions.Keys)
            {
                // L'ajout de champs déclenche le SetCheck event qui ajoute les éléments automatiquement dans le dictionnaire
                foreach (UdpFrameFunction func in Enum.GetValues(typeof(UdpFrameFunction)))
                {
                    if (!_configFunctions[lst].ContainsKey(func))
                        _configFunctions[lst].Add(func, true);

                    lst.Items.Add(func.ToString(), _configFunctions[lst][func]);
                }
            }

            foreach (Board board in Enum.GetValues(typeof(Board)))
            {
                if (!Config.CurrentConfig.LogsExpediteurs.ContainsKey(board))
                    Config.CurrentConfig.LogsExpediteurs.Add(board, true);
                lstSender.Items.Add(board.ToString(), Config.CurrentConfig.LogsExpediteurs[board]);

                if (!Config.CurrentConfig.LogsDestinataires.ContainsKey(board))
                    Config.CurrentConfig.LogsDestinataires.Add(board, true);
                lstReceiver.Items.Add(board.ToString(), Config.CurrentConfig.LogsDestinataires[board]);
            }

            _loading = false;
            _log = new FramesLog();
        }

        #region Publiques

        public void Clear()
        {
            _log = new FramesLog();
        }

        public void DisplayFrame(TimedFrame tFrame)
        {
            String time = "";

            try
            {
                if (rdoTimeAbsolute.Checked)
                    time = tFrame.Date.ToString("hh:mm:ss:fff");
                if (rdoTimeFromStart.Checked)
                    time = (tFrame.Date - _startTime).ToString(@"hh\:mm\:ss\:fff");
                if (rdoTimeFromPrev.Checked)
                    time = ((int)(tFrame.Date - _previousTime).TotalMilliseconds).ToString() + " ms";
                if (rdoTimeFromPrevDisplay.Checked)
                    time = ((int)(tFrame.Date - _previousDisplayTime).TotalMilliseconds).ToString() + " ms";

                Board board = UdpFrameFactory.ExtractBoard(tFrame.Frame);
                Board sender = UdpFrameFactory.ExtractSender(tFrame.Frame, tFrame.IsInputFrame);
                Board receiver = UdpFrameFactory.ExtractReceiver(tFrame.Frame, tFrame.IsInputFrame);
                UdpFrameFunction func = UdpFrameFactory.ExtractFunction(tFrame.Frame);

                if (board == Board.PC) throw new Exception();

                bool receiverVisible = Config.CurrentConfig.LogsDestinataires[receiver];
                bool senderVisible = Config.CurrentConfig.LogsExpediteurs[sender];
                bool functionVisible = (_configFunctions[_lstFunctions[board]][func]);

                if (senderVisible && receiverVisible && functionVisible)
                {
                    dgvLog.Rows.Add(_counter, time, sender.ToString(), receiver.ToString(), UdpFrameDecoder.Decode(tFrame.Frame), tFrame.Frame.ToString());
                    _previousDisplayTime = tFrame.Date;

                    if (rdoColorByBoard.Checked)
                        dgvLog.Rows[dgvLog.Rows.Count - 1].DefaultCellStyle.BackColor = _boardColor[board];
                    else if (rdoColorByReceiver.Checked)
                        dgvLog.Rows[dgvLog.Rows.Count - 1].DefaultCellStyle.BackColor = _boardColor[receiver];
                    else if (rdoColorBySender.Checked)
                        dgvLog.Rows[dgvLog.Rows.Count - 1].DefaultCellStyle.BackColor = _boardColor[sender];
                }
            }
            catch (Exception)
            {
                dgvLog.Rows.Add(_counter, time, "?", "?", "Inconnu !", tFrame.Frame.ToString());
                dgvLog.Rows[dgvLog.Rows.Count - 1].DefaultCellStyle.BackColor = Color.Red;
            }

            _counter++;
            _previousTime = tFrame.Date;
        }

        public void DisplayLog()
        {
            try
            {
                dgvLog.Rows.Clear();

                if (_log.Frames.Count > 0)
                {

                    _startTime = _log.Frames[0].Date;
                    _previousTime = _log.Frames[0].Date;
                    _previousDisplayTime = _log.Frames[0].Date;
                    _counter = 0;

                    for (int iFrame = 0; iFrame < _log.Frames.Count; iFrame++)
                        DisplayFrame(_log.Frames[iFrame]);
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Impossible de décoder toutes les trames contenues dans ce fichier.", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void LoadLog()
        {
            OpenFileDialog open = new OpenFileDialog();
            open.Filter = "Fichiers replay trames (*" + FramesLog.FileExtension + ")| *" + FramesLog.FileExtension + "";
            open.Multiselect = true;

            if (open.ShowDialog() == DialogResult.OK)
            {
                foreach (String fichier in open.FileNames)
                {
                    LoadLog(fichier);
                }
                _log.Sort();
                DisplayLog();
            }
        }

        public void LoadLog(String file)
        {
            FramesLog log = new FramesLog();
            log.Import(file);

            foreach (TimedFrame t in log.Frames)
                _log.Frames.Add(t);
        }

        #endregion

        #region Privées

        private void EnableAllCheckBoxes(bool enable)
        {
            for (int iList = 0; iList < _boxLists.Count; iList++)
                for (int iItem = 0; iItem < _boxLists[iList].Items.Count; iItem++)
                    _boxLists[iList].SetItemChecked(iItem, enable);
        }

        private TimedFrame GetFrameFromLine(DataGridViewRow line)
        {
            return _log.Frames[Convert.ToInt32(dgvLog["ID", line.Index].Value)];
        }

        private void ShowFramesReceiver(Board board, bool show)
        {
            lstReceiver.Items.Remove(board.ToString());
            lstReceiver.Items.Add(board.ToString(), show);
            Config.CurrentConfig.LogsDestinataires[board] = show;
        }

        private void ShowFramesSender(Board board, bool show)
        {
            lstSender.Items.Remove(board.ToString());
            lstSender.Items.Add(board.ToString(), show);
            Config.CurrentConfig.LogsExpediteurs[board] = show;
        }

        private void ShowFrameFunction(Board board, UdpFrameFunction func, bool show)
        {
            _lstFunctions[board].Items.Remove(func.ToString());
            _lstFunctions[board].Items.Add(func.ToString(), show);
            _configFunctions[_lstFunctions[board]][func] = show;
        }

        private FramesLog CreateLogFromSelection()
        {
            FramesLog logSelection = new FramesLog();

            foreach (DataGridViewRow line in dgvLog.SelectedRows)
            {
                TimedFrame trameReplay = GetFrameFromLine(line);
                logSelection.AddFrame(trameReplay.Frame, trameReplay.IsInputFrame, trameReplay.Date);
            }

            return logSelection;
        }

        private void ReplayLog(FramesLog log)
        {
            _thReplay = new Thread(_log.ReplayInputFrames);
            _thReplay.Start();
        }

        #endregion

        #region Events

        private void btnReplayAll_Click(object sender, EventArgs e)
        {
            ReplayLog(_log);
        }

        private void btnReplaySelected_Click(object sender, EventArgs e)
        {
            ReplayLog(CreateLogFromSelection());
        }

        private void lstSender_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!Execution.DesignMode && !_loading)
            {
                String boardStr = (String)lstSender.Items[e.Index];
                Board board = (Board)Enum.Parse(typeof(Board), boardStr);

                Config.CurrentConfig.LogsExpediteurs[board] = (e.NewValue == CheckState.Checked);
            }
        }

        private void lstReceiver_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!Execution.DesignMode && !_loading)
            {
                String boardStr = (String)lstReceiver.Items[e.Index];
                Board board = (Board)Enum.Parse(typeof(Board), boardStr);

                Config.CurrentConfig.LogsDestinataires[board] = (e.NewValue == CheckState.Checked);
            }
        }

        private void lstFunctions_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!Execution.DesignMode && !_loading)
            {
                CheckedListBox lst = (CheckedListBox)sender;
                String funcStr = (String)lst.Items[e.Index];
                UdpFrameFunction func = (UdpFrameFunction)Enum.Parse(typeof(UdpFrameFunction), funcStr);

                _configFunctions[lst][func] = (e.NewValue == CheckState.Checked);
            }
        }

        private void mnuHideSameSenderFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.SelectedRows)
                {
                    TimedFrame tFrame = GetFrameFromLine(line);
                    ShowFramesSender(UdpFrameFactory.ExtractSender(tFrame.Frame, tFrame.IsInputFrame), false);
                }

                DisplayLog();
            }
        }

        private void mnuHideSameReceiverFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.SelectedRows)
                {
                    TimedFrame tFrame = GetFrameFromLine(line);
                    ShowFramesReceiver(UdpFrameFactory.ExtractReceiver(tFrame.Frame, tFrame.IsInputFrame), false);
                }

                DisplayLog();
            }
        }

        private void mnuHideSameBoardFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.SelectedRows)
                {
                    Board board = UdpFrameFactory.ExtractBoard(GetFrameFromLine(line).Frame);
                    ShowFramesReceiver(board, false);
                    ShowFramesSender(board, false);
                }

                DisplayLog();
            }
        }

        private void mnuHideAllFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.Rows)
                {
                    TimedFrame tFrame = GetFrameFromLine(line);
                    ShowFrameFunction(UdpFrameFactory.ExtractBoard(tFrame.Frame), UdpFrameFactory.ExtractFunction(tFrame.Frame), false);
                }

                DisplayLog();
            }
        }

        private void mnuFrameCopy_Click(object sender, EventArgs e)
        {
            if (dgvLog.Rows.Count > 0)
                Clipboard.SetText(GetFrameFromLine(dgvLog.SelectedRows[0]).Frame.ToString());
        }

        private void btnAllCheck_Click(object sender, EventArgs e)
        {
            EnableAllCheckBoxes(true);
        }

        private void btnAllUncheck_Click(object sender, EventArgs e)
        {
            EnableAllCheckBoxes(false);
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            DisplayLog();
        }

        private void dgvLog_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right && e.RowIndex > 0 && e.ColumnIndex > 0)
                dgvLog.CurrentCell = dgvLog.Rows[e.RowIndex].Cells[e.ColumnIndex];
        }

        private void mnuHideSameTypeFrames_Click(object sender, EventArgs e)
        {
            if (dgvLog.SelectedRows.Count >= 1)
            {
                foreach (DataGridViewRow line in dgvLog.SelectedRows)
                {
                    TimedFrame tFrame = GetFrameFromLine(line);
                    ShowFrameFunction(UdpFrameFactory.ExtractBoard(tFrame.Frame), UdpFrameFactory.ExtractFunction(tFrame.Frame), false);
                }

                DisplayLog();
            }
        }

        private void btnDisplay_Click(object sender, EventArgs e)
        {
            if (_displayTimer == null || !_displayTimer.Enabled)
            {
                _log = new FramesLog();
                _displayTimer = new System.Windows.Forms.Timer();
                _displayTimer.Interval = 1000;
                _displayTimer.Tick += displayTimer_Tick;
                _displayTimer.Start();

                Connections.AllConnections.ForEach(conn =>
                {
                    if (conn.GetType() == typeof(UDPConnection))
                    {
                        conn.FrameReceived += new Connection.NewFrameDelegate((frame) => _log.AddFrame(frame, true));
                        conn.FrameSend += new Connection.NewFrameDelegate((frame) => _log.AddFrame(frame, false));
                    }
                });

                btnReplayAll.Enabled = false;
                btnReplaySelected.Enabled = false;
                btnLoad.Enabled = false;
                btnDisplay.Text = "Arrêter la surveillance";
                btnDisplay.Image = Properties.Resources.GlassPause48;
            }
            else
            {
                _displayTimer.Stop();

                Connections.AllConnections.ForEach(conn =>
                {
                    if (conn.GetType() == typeof(UDPConnection))
                    {
                        conn.FrameReceived -= new Connection.NewFrameDelegate((frame) => _log.AddFrame(frame, true));
                        conn.FrameSend -= new Connection.NewFrameDelegate((frame) => _log.AddFrame(frame, false));
                    }
                });

                btnReplayAll.Enabled = true;
                btnReplaySelected.Enabled = true;
                btnLoad.Enabled = true;
                btnDisplay.Text = "Lancer la surveillance";
                btnDisplay.Image = Properties.Resources.GlassStart48;
            }
        }

        void displayTimer_Tick(object sender, EventArgs e)
        {
            int nbTrames = _log.Frames.Count;
            for (int i = _counter; i < nbTrames; i++)
                DisplayFrame(_log.Frames[i]);

            if (boxScroll.Checked && dgvLog.Rows.Count > 10)
                dgvLog.FirstDisplayedScrollingRowIndex = dgvLog.RowCount - 1;
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            LoadLog();
        }

        #endregion

    }
}
<file_sep>/GoBot/GoBot/Devices/Pepperl/PepperlConst.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Devices
{
    public class PepperlConst
    {
        public const String CmdReboot = "reboot_device";
        public const String CmfFactoryReset = "factory_reset";
        public const String CmdHandleUdp = "request_handle_udp";
        public const String CmdHandleTcp = "request_handle_tcp";
        public const String CmdProtocolInfo = "get_protocol_info";
        public const String CmdFeedWatchdog = "feed_watchdog";
        public const String CmdListParameters = "list_parameters";
        public const String CmdGetParameter = "get_parameter";
        public const String CmdSetParameter = "set_parameter";
        public const String CmdResetParameter = "reset_parameter";
        public const String CmdReleaseHandle = "release_handle";
        public const String CmdStartScan = "start_scanoutput";
        public const String CmdStopScan = "stop_scanoutput";
        public const String CmdGetScanConfig = "get_scanoutput_config";
        public const String CmdSetScanConfig = "set_scanoutput_config";

        public const String ParamVendor = "vendor";
        public const String ParamProduct = "product";
        public const String ParamPart = "part";
        public const String ParamSerial = "serial";
        public const String ParamRevisionFw = "revision_fw";
        public const String ParamRevisionHw = "revision_hw";
        public const String ParamMaxConnections = "max_connections";
        public const String ParamFeatureFlags = "feature_flags";
        public const String ParamRadialRangeMin = "radial_range_min";
        public const String ParamRadialRangeMax = "radial_range_max";
        public const String ParamRadialResolution = "radial_resolution";
        public const String ParamAngularFov = "angular_fov";
        public const String ParamAngularResolution = "angular_resolution";
        public const String ParamScanFrequencyMin = "scan_frequency_min";
        public const String ParamScanFrequencyMax = "scan_frequency_max";
        public const String ParamSamplingRateMin = "sampling_rate_min";
        public const String ParamSamplingRateMax = "sampling_rate_max";
        public const String ParamSamplesPerScan = "samples_per_scan";
        public const String ParamScanFrequencyMeasured = "scan_frequency_measured";
        public const String ParamStatusFlags = "status_flags";
        public const String ParamLoadIndication = "load_indication";
        public const String ParamDeviceFamily = "device_family";
        public const String ParamMacAddress = "mac_address";
        public const String ParamIpModeCurrent = "ip_mode_current";
        public const String ParamIpAddressCurrent = "ip_address_current";
        public const String ParamSubnetMaskCurrent = "subnet_mask_current";
        public const String ParamGatewayCurrent = "gateway_current";
        public const String ParamSystemTimeRaw = "system_time_raw";
        public const String ParamUserTag = "user_tag";
        public const String ParamUserNotes = "user_notes";
        public const String ParamEmitterType = "emitter_type";
        public const String ParamUptime = "up_time";
        public const String ParamPowerCycles = "power_cycles";
        public const String ParamOperationTime = "operation_time";
        public const String ParamOperationTimeScaled = "operation_time_scaled";
        public const String ParamTemperatureCurrent = "temperature_current";
        public const String ParamTemperatureMin = "temperature_min";
        public const String ParamTemperatureMax = "temperature_max";
        public const String ParamHmiHardBitmap = "hmi_static_logo";
        public const String ParamHmiSoftBitmap = "hmi_application_bitmap";
        public const String ParamHmiHardText1 = "hmi_static_text_1";
        public const String ParamHmiHardText2 = "hmi_static_text_2";
        public const String ParamHmiSoftText1 = "hmi_application_text_1";
        public const String ParamHmiSoftText2 = "hmi_application_text_2";
        public const String ParamFilterWidth = "filter_width";
        public const String ParamFilterMaximumMargin = "filter_maximum_margin";
        public const String ParamUdpAddress = "address";
        public const String ParamUdpPort = "port";
        public const String ParamUdpWatchdogTimeout = "watchdogtimeout";
        public const String ParamUdpPacketCrc = "packet_crc";
        public const String ParamUdpStartAngle = "start_angle";
        public const String ParamUdpPointsCount = "max_num_points_scan";
        public const String ParamUdpSkipScans = "skip_scans";
        public const String ParamUdpHandle = "handle";
        public const String ParamTcpAddress = "address";
        public const String ParamTcpPort = "port";
        public const String ParamTcpPacketCrc = "packet_crc";
        public const String ParamTcpStartAngle = "start_angle";
        public const String ParamTcpPointsCount = "max_num_points_scan";
        public const String ParamTcpSkipScans = "skip_scans";
        public const String ParamTcpHandle = "handle";
        public const String ParamProtocolName = "protocol_name";
        public const String ParamProtocolVersionMajor = "version_major";
        public const String ParamProtocolVersionMinor = "version_minor";
        public const String ParamProtocolCommands = "commands";
        public const String ParamErrorCode = "error_code";
        public const String ParamErrorText = "error_text";
        public const String ParamContaminationDetectionWarnings = "lcm_sector_warn_flags";
        public const String ParamContaminationDetectionErrors = "lcm_sector_error_flags";
        public const String ParamContaminationDetectionPeriod = "lcm_detection_period";
        public const String ParamFeedWatchdogHandle = "handle";

        public const String ParamUdpWatchdog = "watchdog";
        public const String ValueUdpWatchdogOn = "on";
        public const String ValueUdpWatchdogOff = "off";

        public const String ParamTcpWatchdog = "watchdog";
        public const String ValueTcpWatchdogOn = "on";
        public const String ValueTcpWatchdogOff = "off";
        
        public const String ParamIpMode = "ip_mode";
        public const String ValueIpModeStatic = "static";
        public const String ValueIpModeAuto = "autoip";
        public const String ValueIpModeDhcp = "dhcp";

        public const String ParamIpAddress = "ip_address";
        public const String ParamSubnetMask = "subnet_mask";
        public const String ParamGateway = "gateway";
        public const String ParamScanFrequency = "scan_frequency";

        public const String ParamScanDirection = "scan_direction";
        public const String ValueScanDirectionClockwise = "cw";
        public const String ValueScanDirectionCounterClockwise = "ccw";

        public const String ParamHmiMode = "hmi_display_mode";
        public const String ValueHmiModeOff = "off";
        public const String ValueHmiModeHardLogo = "static_logo";
        public const String ValueHmiModeHardText = "static_text";
        public const String ValueHmiModeBargraphDistance = "bargraph_distance";
        public const String ValueHmiModeBargraphEcho = "bargraph_echo";
        public const String ValueHmiModeBargraphReflector = "bargraph_reflector";
        public const String ValueHmiModeSoftBitmap = "application_bitmap";
        public const String ValueHmiModeSoftText = "application_text";

        public const String ParamHmiLanguage = "hmi_language";
        public const String ValueHmiLanguageEnglish = "engligh";
        public const String ValueHmiLanguageGerman = "german";

        public const String ParamHmiButtonLock = "hmi_button_lock";
        public const String ValueHmiButtonLockOn = "on";
        public const String ValueHmiButtonLockOff = "off";

        public const String ParamHmiParameterLock = "hmi_parameter_lock";
        public const String ValueHmiParameterLockOn = "on";
        public const String ValueHmiParameterLockOff = "off";

        public const String ParamLocatorIndication = "locator_indication";
        public const String ValueLocatorIndicationLockOn = "on";
        public const String ValueLocatorIndicationOff = "off";
        
        public const String ParamOperatingMode = "operating_mode";
        public const String ValueOperatingModeMeasure = "measure";
        public const String ValueOperatingModeOff = "emitter_off";

        public const String ParamFilterType = "filter_type";
        public const String ValueFilterTypeNone = "none";
        public const String ValueFilterTypeAverage = "average";
        public const String ValueFilterTypeMedian = "median";
        public const String ValueFilterTypeMaximum = "maximum";
        public const String ValueFilterTypeRemission = "remission";

        public const String ParamFilterErrorHandling = "filter_error_handling";
        public const String ValueFilterErrorHandlingStrict = "strict";
        public const String ValueFilterErrorHandlingTolerant = "tolerant";

        public const String ParamFilterRemissionThreshold = "filter_remission_threshold";
        public const String ValueFilterRemissionThresholdDiffuseLow = "diffuse_low";
        public const String ValueFilterRemissionThresholdDiffuseHigh = "diffuse_high";
        public const String ValueFilterRemissionThresholdReflectorMin = "reflector_min";
        public const String ValueFilterRemissionThresholdReflectorLow = "reflector_low";
        public const String ValueFilterRemissionThresholdReflectorStd = "reflector_std";
        public const String ValueFilterRemissionThresholdReflectorHigh = "reflector_high";
        public const String ValueFilterRemissionThresholdReflectorMax = "reflector_max";

        public const String ParamUdpPacketType = "packet_type";
        public const String ValueUdpPacketTypeDistance = "A";
        public const String ValueUdpPacketTypeDistanceAmplitude = "B";
        public const String ValueUdpPacketTypeDistanceAmplitudeCompact = "C";

        public const String ParamTcpWatchdogTimeout = "watchdogtimeout";

        public const String ParamTcpPacketType = "packet_type";
        public const String ValueTcpPacketTypeDistance = "A";
        public const String ValueTcpPacketTypeDistanceAmplitude = "B";
        public const String ValueTcpPacketTypeDistanceAmplitudeCompact = "C";

        public const String ParamContaminationDetectionSensivity = "lcm_detection_sensitivity";
        public const String ValueContaminationDetectionSensivityDisabled = "disabled";
        public const String ValueContaminationDetectionSensivityLow = "low";
        public const String ValueContaminationDetectionSensivityMedium = "medium";
        public const String ValueContaminationDetectionSensivityHigh = "high";

        public const String ParamContaminationDetectionSectorEnable = "lcm_sector_enable";
        public const String ValueContaminationDetectionSectorEnableOn = "on";
        public const String ValueContaminationDetectionSectorEnableOff = "off";

        public const int FlagStatusInitialization = 1 << 00;
        public const int FlagStatusscanOutputMuted = 1 << 02;
        public const int FlagStatusUnstableRotation = 1 << 03;
        public const int FlagStatusDeviceWarning = 1 << 08;
        public const int FlagStatusLensContaminationWarning = 1 << 09;
        public const int FlagStatusLowTemperatureWarning = 1 << 10;
        public const int FlagStatusHighTemperatureWarning = 1 << 11;
        public const int FlagStatusDeviceOverloadWarning = 1 << 12;
        public const int FlagStatusDeviceError = 1 << 16;
        public const int FlagStatusLensContaminationError = 1 << 17;
        public const int FlagStatusLowTemperatureError = 1 << 18;
        public const int FlagStatusHighTemperatureError = 1 << 19;
        public const int FlagStatusDeviceOverloadError = 1 << 20;
        public const int FlagStatusDeviceDefect = 1 << 30;

        public const int ErrorSuccess = 0;
        public const int ErrorUnknownArgument = 100;
        public const int ErrorUnknownParameter = 110;
        public const int ErrorInvalidHandle = 120;
        public const int ErrorArgumentMissing = 130;
        public const int ErrorInvalidValue = 200;
        public const int ErrorOutOfRangeValue = 210;
        public const int ErrorReadOnly = 220;
        public const int ErrorMemory = 230;
        public const int ErrorAlreadyInUse = 240;
        public const int ErrorInternal = 333;
    }
}
<file_sep>/GoBot/GoBot/Actions/Deplacement/ActionRecule.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Actions
{
    class ActionRecule : ITimeableAction
    {
        private int distance;
        private Robot robot;

        public ActionRecule(Robot r, int dist)
        {
            robot = r;
            distance = dist;
        }

        System.Drawing.Image IAction.Image
        {
            get { return GoBot.Properties.Resources.DownGreen16; }
        }

        public override String ToString()
        {
            return robot.Name + " recule de " + distance + "mm";
        }

        void IAction.Executer()
        {
            robot.MoveBackward(distance);
        }

        public TimeSpan Duration
        {
            get
            {
                return robot.SpeedConfig.LineDuration(distance);
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Pages/PageRobot.cs
﻿using System;
using System.Windows.Forms;

namespace GoBot.IHM.Pages
{
    public partial class PageRobot : UserControl
    {
        public PageRobot()
        {
            InitializeComponent();
        }

        public void Init()
        {
            panelDisplacement.Robot = Robots.MainRobot;
            panelDisplacement.Init();
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            Config.Save();
            Control parent = Parent;
            while (parent.Parent != null)
                parent = parent.Parent;

            if (parent != null)
                parent.Dispose();
        }

        private void rdoMainRobot_CheckedChanged(object sender, EventArgs e)
        {
            if (Config.CurrentConfig.IsMiniRobot != !rdoMainRobot.Checked)
            {
                Config.CurrentConfig.IsMiniRobot = !rdoMainRobot.Checked;
                Robots.Init();
            }
        }

        private void PageRobot_Load(object sender, EventArgs e)
        {
            rdoMainRobot.Checked = !Config.CurrentConfig.IsMiniRobot;
            rdoSecondaryRobot.Checked = Config.CurrentConfig.IsMiniRobot;
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/PanelTestLiaisons.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GoBot.Communications;
using System.Threading;

namespace GoBot.IHM
{
    public partial class PanelTestLiaisons : UserControl
    {
        List<LiaisonDataCheck> Liaisons { get; set; }
        System.Windows.Forms.Timer Timer { get; set; }
        System.Windows.Forms.Timer TimerIhm { get; set; }

        public PanelTestLiaisons()
        {
            InitializeComponent();
            Timer = null;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (Timer == null)
            {
                Liaisons = new List<LiaisonDataCheck>();

                Liaisons.Add(new LiaisonDataCheck(Connexions.ConnexionBun, Carte.RecBun, false));
                Liaisons.Add(new LiaisonDataCheck(Connexions.ConnexionBeu, Carte.RecBeu, false));
                Liaisons.Add(new LiaisonDataCheck(Connexions.ConnexionBoi, Carte.RecBoi, false));
                Liaisons.Add(new LiaisonDataCheck(Connexions.ConnexionPi, Carte.RecPi, true));

                Connexions.ConnexionMiwi.NouvelleTrameRecue += Liaisons[0].MessageRecu;
                Connexions.ConnexionMiwi.NouvelleTrameRecue += Liaisons[1].MessageRecu;
                Connexions.ConnexionMiwi.NouvelleTrameRecue += Liaisons[2].MessageRecu;
                Connexions.ConnexionMiwi.NouvelleTrameRecue += Liaisons[3].MessageRecu;

                Timer = new System.Windows.Forms.Timer();
                Timer.Interval = (int)numIntervalle.Value;
                Timer.Tick += new EventHandler(Timer_Tick);

                TimerIhm = new System.Windows.Forms.Timer();
                TimerIhm.Interval = 1000;
                TimerIhm.Tick += new EventHandler(TimerIhm_Tick);

                btnStart.Text = "Stop";
                btnStart.Image = GoBot.Properties.Resources.Pause;

                numIntervalle.Enabled = false;
                Timer.Start();
                TimerIhm.Start();
            }
            else
            {
                Timer.Stop();
                Timer = null;
                TimerIhm.Stop();
                btnStart.Text = "Lancer";
                numIntervalle.Enabled = true;
                btnStart.Image = GoBot.Properties.Resources.Play;
            }
        }

        void TimerIhm_Tick(object sender, EventArgs e)
        {
            this.Invoke(new EventHandler(delegate
            {
                lblB1Nombre.Text = Liaisons[0].NombreMessagesTotal.ToString();
                if (Liaisons[0].NombreMessagesTotal > 0)
                {
                    lblB1Corrects.Text = Liaisons[0].NombreMessagesCorrects + " -  " + (Liaisons[0].NombreMessagesCorrects * 100.0 / (double)Liaisons[0].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB1CorrompusEmission.Text = Liaisons[0].NombreMessagesCorrompusEmission + " -  " + (Liaisons[0].NombreMessagesCorrompusEmission * 100.0 / (double)Liaisons[0].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB1CorrompusReception.Text = Liaisons[0].NombreMessagesCorrompusReception + " -  " + (Liaisons[0].NombreMessagesCorrompusReception * 100.0 / (double)Liaisons[0].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB1PerdusEmission.Text = Liaisons[0].NombreMessagesPerdusEmission + " -  " + (Liaisons[0].NombreMessagesPerdusEmission * 100.0 / (double)Liaisons[0].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB1PerdusReception.Text = Liaisons[0].NombreMessagesPerdusReception + " -  " + (Liaisons[0].NombreMessagesPerdusReception * 100.0 / (double)Liaisons[0].NombreMessagesTotal).ToString("#.##") + "%";
                }

                lblB2Nombre.Text = Liaisons[1].NombreMessagesTotal.ToString();
                if (Liaisons[1].NombreMessagesTotal > 0)
                {
                    lblB2Corrects.Text = Liaisons[1].NombreMessagesCorrects + " -  " + (Liaisons[1].NombreMessagesCorrects * 100.0 / (double)Liaisons[1].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB2CorrompusEmission.Text = Liaisons[1].NombreMessagesCorrompusEmission + " -  " + (Liaisons[1].NombreMessagesCorrompusEmission * 100.0 / (double)Liaisons[1].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB2CorrompusReception.Text = Liaisons[1].NombreMessagesCorrompusReception + " -  " + (Liaisons[1].NombreMessagesCorrompusReception * 100.0 / (double)Liaisons[1].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB2PerdusEmission.Text = Liaisons[1].NombreMessagesPerdusEmission + " -  " + (Liaisons[1].NombreMessagesPerdusEmission * 100.0 / (double)Liaisons[1].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB2PerdusReception.Text = Liaisons[1].NombreMessagesPerdusReception + " -  " + (Liaisons[1].NombreMessagesPerdusReception * 100.0 / (double)Liaisons[1].NombreMessagesTotal).ToString("#.##") + "%";
                }

                lblB3Nombre.Text = Liaisons[2].NombreMessagesTotal.ToString();
                if (Liaisons[2].NombreMessagesTotal > 0)
                {
                    lblB3Corrects.Text = Liaisons[2].NombreMessagesCorrects + " -  " + (Liaisons[2].NombreMessagesCorrects * 100.0 / (double)Liaisons[2].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB3CorrompusEmission.Text = Liaisons[2].NombreMessagesCorrompusEmission + " -  " + (Liaisons[2].NombreMessagesCorrompusEmission * 100.0 / (double)Liaisons[2].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB3CorrompusReception.Text = Liaisons[2].NombreMessagesCorrompusReception + " -  " + (Liaisons[2].NombreMessagesCorrompusReception * 100.0 / (double)Liaisons[2].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB3PerdusEmission.Text = Liaisons[2].NombreMessagesPerdusEmission + " -  " + (Liaisons[2].NombreMessagesPerdusEmission * 100.0 / (double)Liaisons[2].NombreMessagesTotal).ToString("#.##") + "%";
                    lblB3PerdusReception.Text = Liaisons[2].NombreMessagesPerdusReception + " -  " + (Liaisons[2].NombreMessagesPerdusReception * 100.0 / (double)Liaisons[2].NombreMessagesTotal).ToString("#.##") + "%";
                }

                lblPRNombre.Text = Liaisons[3].NombreMessagesTotal.ToString();
                if (Liaisons[3].NombreMessagesTotal > 0)
                {
                    lblPRCorrects.Text = Liaisons[3].NombreMessagesCorrects + " -  " + (Liaisons[3].NombreMessagesCorrects * 100.0 / (double)Liaisons[3].NombreMessagesTotal).ToString("#.##") + "%";
                    lblPRCorrompusEmission.Text = Liaisons[3].NombreMessagesCorrompusEmission + " -  " + (Liaisons[3].NombreMessagesCorrompusEmission * 100.0 / (double)Liaisons[3].NombreMessagesTotal).ToString("#.##") + "%";
                    lblPRCorrompusReception.Text = Liaisons[3].NombreMessagesCorrompusReception + " -  " + (Liaisons[3].NombreMessagesCorrompusReception * 100.0 / (double)Liaisons[3].NombreMessagesTotal).ToString("#.##") + "%";
                    lblPRPerdusEmission.Text = Liaisons[3].NombreMessagesPerdusEmission + " -  " + (Liaisons[3].NombreMessagesPerdusEmission * 100.0 / (double)Liaisons[3].NombreMessagesTotal).ToString("#.##") + "%";
                    lblPRPerdusReception.Text = Liaisons[3].NombreMessagesPerdusReception + " -  " + (Liaisons[3].NombreMessagesPerdusReception * 100.0 / (double)Liaisons[3].NombreMessagesTotal).ToString("#.##") + "%";
                }
            }));
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            //for (int i = 0; i < Liaisons.Count; i++)
            //    Liaisons[i].EnvoiTest();

            Liaisons[0].EnvoiTest();

            if (Liaisons[0].IDTestEmissionActuel == 255)
            {
                btnStart_Click(null, null);

                Thread.Sleep(1000);

                
            }
        }
    }
}
<file_sep>/GoBot/Geometry/AngleDelta.cs
﻿using System;

namespace Geometry
{
    public struct AngleDelta
    {
        #region Constantes

        public const double PRECISION = 0.01;

        #endregion

        #region Attributs

        private double _angle;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Construit un angle avec la valeur passée en paramètre
        /// </summary>
        /// <param name="angle">Angle de départ</param>
        public AngleDelta(double angle, AngleType type = AngleType.Degre)
        {
            if (type == AngleType.Degre)
                _angle = angle;
            else
                _angle = (180 * angle / Math.PI);
        }

        #endregion

        /// <summary>
        /// Minimize l'angle en considérant que la valeur doit se situer entre -180° et +180°.
        /// Exemple : 370° est minimisé à 10°.
        /// </summary>
        public AngleDelta Modulo()
        {
            while (_angle > 180)
                _angle -= 360;
            while (_angle < -180)
                _angle += 360;

            return this;
        }


        #region Trigonométrie

        public double Cos
        {
            get
            {
                return Math.Cos(InRadians);
            }
        }

        public double Sin
        {
            get
            {
                return Math.Sin(InRadians);
            }
        }

        public double Tan
        {
            get
            {
                return Math.Tan(InRadians);
            }
        }

        #endregion

        #region Proprietes

        /// <summary>
        /// Retourne l'angle en radians
        /// </summary>
        public double InRadians
        {
            get
            {
                return (double)(_angle / 180 * Math.PI);
            }
        }

        /// <summary>
        /// Retourne l'angle en degrés
        /// </summary>
        public double InDegrees
        {
            get
            {
                return _angle;
            }
        }

        #endregion

        #region Operateurs

        public static AngleDelta operator +(AngleDelta a1, AngleDelta a2)
        {
            return new AngleDelta(a1.InDegrees + a2.InDegrees, AngleType.Degre);
        }

        public static AngleDelta operator -(AngleDelta a1, AngleDelta a2)
        {
            return new AngleDelta(a1.InDegrees - a2.InDegrees, AngleType.Degre);
        }

        public static bool operator ==(AngleDelta a1, AngleDelta a2)
        {
            return Math.Abs((a1 - a2).InDegrees) <= PRECISION;
        }

        public static bool operator !=(AngleDelta a1, AngleDelta a2)
        {
            return !(a1 == a2);
        }

        public static implicit operator AngleDelta(double angle)
        {
            return new AngleDelta(angle);
        }

        public static implicit operator double(AngleDelta angle)
        {
            return angle.InDegrees;
        }

        #endregion

        #region Overrides

        public override bool Equals(object obj)
        {
            try
            {
                return Math.Abs(((AngleDelta)obj)._angle - _angle) < PRECISION;
            }
            catch
            {
                return false;
            }
        }

        public override int GetHashCode()
        {
            return (int)(_angle * (1 / PRECISION));
        }

        public override string ToString()
        {
            return _angle.ToString("0.00") + "°";
        }

        #endregion
    }
}<file_sep>/GoBot/Geometry/Shapes/Segment.cs
﻿using Geometry.Shapes.ShapesInteractions;
using System;
using System.Collections.Generic;
using System.Drawing;

namespace Geometry.Shapes
{
    // Un segment est une Droite avec deux extrémités
    public class Segment : Line, IShapeModifiable<Segment>
    {
        #region Attributs

        protected RealPoint _startPoint, _endPoint;

        #endregion

        #region Constructeurs

        public Segment(RealPoint start, RealPoint end)
        {
            _startPoint = new RealPoint(start);
            _endPoint = new RealPoint(end);

            SetLine(StartPoint, EndPoint);
        }

        public Segment(Segment segment)
        {
            _startPoint = new RealPoint(segment.StartPoint);
            _endPoint = new RealPoint(segment.EndPoint);

            _a = segment.A;
            _b = segment.B;
            _c = segment.C;
            SetLine(StartPoint, EndPoint);
        }

        #endregion

        #region Propriétés

        /// <summary>
        /// Obtient la 1ère extremité du segment
        /// </summary>
        public RealPoint StartPoint
        {
            get
            {
                return _startPoint;
            }
            set
            {
                _startPoint = value;
                SetLine(StartPoint, EndPoint);
            }
        }

        /// <summary>
        /// Obtient la 2ème extremité du segment
        /// </summary>
        public RealPoint EndPoint
        {
            get
            {
                return _endPoint;
            }
            set
            {
                _endPoint = value;
                SetLine(StartPoint, EndPoint);
            }
        }

        /// <summary>
        /// Obtient la longueur du segment
        /// </summary>
        public Double Length
        {
            get
            {
                return StartPoint.Distance(EndPoint);
            }
        }

        /// <summary>
        /// Obtient la droite sur laquelle le segment se trouve
        /// </summary>
        public Line Line
        {
            get
            {
                return new Line(this);
            }
        }

        /// <summary>
        /// Obtient la surface du segment
        /// </summary>
        public override double Surface
        {
            get
            {
                return 0;
            }
        }

        /// <summary>
        /// Obtient le barycentre du segment
        /// </summary>
        public override RealPoint Barycenter
        {
            get
            {
                return new RealPoint((_startPoint.X + _endPoint.X) / 2, (_startPoint.Y + _endPoint.Y) / 2);
            }
        }

        #endregion

        #region Opérateurs & Surcharges

        public static bool operator ==(Segment a, Segment b)
        {
            if ((object)a == null || (object)b == null)
                return (object)a == null && (object)b == null;
            else
                return a.A == b.A
                    && a.B == b.B
                    && a.C == a.C
                    && a.StartPoint == b.StartPoint
                    && a.EndPoint == b.StartPoint;
        }

        public static bool operator !=(Segment a, Segment b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            Segment p = obj as Segment;
            if ((Object)p == null)
            {
                return false;
            }

            return (Segment)obj == this;
        }

        public override int GetHashCode()
        {
            return (int)StartPoint.X ^ (int)StartPoint.Y ^ (int)EndPoint.X ^ (int)EndPoint.Y;
        }

        public override string ToString()
        {
            return StartPoint + " -> " + EndPoint;
        }

        #endregion

        #region Croisements

        /// <summary>
        /// Retourne la liste des points de croisement avec la forme donnée
        /// </summary>
        /// <param name="shape">Forme à tester</param>
        /// <returns>Liste des points de croisement</returns>
        public override List<RealPoint> GetCrossingPoints(IShape shape)
        {
            List<RealPoint> output = new List<RealPoint>();

            if (shape is RealPoint) output = SegmentWithRealPoint.GetCrossingPoints(this, shape as RealPoint);
            else if (shape is Segment) output = SegmentWithSegment.GetCrossingPoints(this, shape as Segment);
            else if (shape is Polygon) output = SegmentWithPolygon.GetCrossingPoints(this, shape as Polygon);
            else if (shape is Circle) output = SegmentWithCircle.GetCrossingPoints(this, shape as Circle);
            else if (shape is Line) output = SegmentWithLine.GetCrossingPoints(this, shape as Line);

            return output;
        }

        private List<RealPoint> GetCrossingPointsWithPolygon(Polygon polygon)
        {
            return polygon.GetCrossingPoints(this); // Le polygone sait faire
        }

        /// <summary>
        /// Teste si le segment courant croise la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Vrai si le segment croise la forme donnée</returns>
        public override bool Cross(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = SegmentWithRealPoint.Cross(this, shape as RealPoint);
            else if (shape is Segment) output = SegmentWithSegment.Cross(this, shape as Segment);
            else if (shape is Polygon) output = SegmentWithPolygon.Cross(this, shape as Polygon);
            else if (shape is Circle) output = SegmentWithCircle.Cross(this, shape as Circle);
            else if (shape is Line) output = SegmentWithLine.Cross(this, shape as Line);

            return output;
        }

        /// <summary>
        /// Teste si le segment courant croise la droite donnée
        /// </summary>
        /// <param name="line">Droite testée</param>
        /// <returns>Vrai si la segment contient la droite donnée</returns>
        protected bool Cross(Line line)
        {
            return GetCrossingPoints(line).Count > 0;
        }

        /// <summary>
        /// Teste si le segment courant croise le polygone donné
        /// </summary>
        /// <param name="polygon">Polygone testé</param>
        /// <returns>Vrai si le segment croise le polygone donné</returns>
        protected  bool Cross(Polygon polygon)
        {
            return polygon.Cross(this);
        }

        #endregion

        #region Contient
        
        /// <summary>
        /// Teste si le segment courant contient la forme donnée
        /// </summary>
        /// <param name="shape">Forme testée</param>
        /// <returns>Vrai si le segment contient la forme donnée</returns>
        public override bool Contains(IShape shape)
        {
            bool output = false;

            if (shape is RealPoint) output = SegmentWithRealPoint.Contains(this, shape as RealPoint);
            else if (shape is Segment) output = SegmentWithSegment.Contains(this, shape as Segment);
            else if (shape is Polygon) output = SegmentWithPolygon.Contains(this, shape as Polygon);
            else if (shape is Circle) output = SegmentWithCircle.Contains(this, shape as Circle);
            else if (shape is Line) output = SegmentWithLine.Contains(this, shape as Line);

            return output;
        }

        #endregion

        #region Distance
        
        public override double Distance(IShape shape)
        {
            double output = 0;

            if (shape is RealPoint) output = SegmentWithRealPoint.Distance(this, shape as RealPoint);
            else if (shape is Segment) output = SegmentWithSegment.Distance(this, shape as Segment);
            else if (shape is Polygon) output = SegmentWithPolygon.Distance(this, shape as Polygon);
            else if (shape is Circle) output = SegmentWithCircle.Distance(this, shape as Circle);
            else if (shape is Line) output = SegmentWithLine.Distance(this, shape as Line);

            return output;
        }

        /// <summary>
        /// Retourne la distance minimale entre le segment courant et la droite donnée
        /// </summary>
        /// <param name="forme">Droite testée</param>
        /// <returns>Distance minimale</returns>
        public double Distance(Line line)
        {
            // Si la droite et le segment se croisent la distance est de 0
            if (Cross(line))
                return 0;

            // Sinon c'est la distance minimale entre chaque extremité du segment et la droite
            double minDistance = double.MaxValue;

            minDistance = Math.Min(minDistance, line.Distance(StartPoint));
            minDistance = Math.Min(minDistance, line.Distance(EndPoint));

            return minDistance;
        }
        
        #endregion

        #region Transformations

        /// <summary>
        /// Retourne un segment qui est translaté des distances données
        /// </summary>
        /// <param name="dx">Distance en X</param>
        /// <param name="dy">Distance en Y</param>
        /// <returns>Segment translaté des distances données</returns>
        public new Segment Translation(double dx, double dy)
        {
            return new Segment(_startPoint.Translation(dx, dy), _endPoint.Translation(dx, dy));
        }

        /// <summary>
        /// Retourne un segment qui est tournée de l'angle donné
        /// </summary>
        /// <param name="angle">Angle de rotation</param>
        /// <param name="rotationCenter">Centre de rotation, si null (0, 0) est utilisé</param>
        /// <returns>Segment tourné de l'angle donné</returns>
        public new Segment Rotation(AngleDelta angle, RealPoint rotationCenter = null)
        {
            if (rotationCenter == null) rotationCenter = Barycenter;

            return new Segment(_startPoint.Rotation(angle, rotationCenter), _endPoint.Rotation(angle, rotationCenter));
        }

        /// <summary>
        /// Retourne un segment qui prolonge l'actuel en ajoutant un point.
        /// Le résultat est valide uniquement si le point ajouté se trouve sur la droite formée par le segment.
        /// </summary>
        /// <param name="pt">Point à ajouter au segment</param>
        /// <returns>Segment prolongé jusqu'au point donné</returns>
        public Segment Join(RealPoint pt)
        {
            Segment s1 = new Segment(_startPoint, pt);
            Segment s2 = new Segment(_endPoint, pt);

            Segment res;

            if (this.Contains(pt))
                res = new Segment(this);
            else if (s1.Contains(_endPoint))
                res = s1;
            else if (s2.Contains(_startPoint))
                res = s2;
            else
                res = null;

            return res;
        }

        #endregion

        #region Peinture

        /// <summary>
        /// Peint le segment sur le Graphic donné
        /// </summary>
        /// <param name="g">Graphique sur lequel peindre</param>
        /// <param name="outlineColor">Couleur du contour</param>
        /// <param name="outlineWidth">Epaisseur du segment</param>
        /// <param name="fillColor">Couleur de remplissage</param>
        /// <param name="scale">Echelle de conversion</param>
        public override void Paint(Graphics g, Pen outline, Brush fill, WorldScale scale)
        {
            Point startPoint = scale.RealToScreenPosition(StartPoint);
            Point endPoint = scale.RealToScreenPosition(EndPoint);
            
            if (outline != null)
                    g.DrawLine(outline, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
        }

        #endregion
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/LineWithSegment.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class LineWithSegment
    {
        public static bool Contains(Line containingLine, Segment containedSegment)
        {
            // Contenir un segment revient à contenir la droite sur laquelle se trouve le segment

            return LineWithLine.Contains(containingLine, containedSegment);
        }
        
        public static bool Cross(Line line, Segment segment)
        {
            // Pas trouvé plus simple

            return GetCrossingPoints(line, segment).Count > 0;
        }

        public static double Distance(Line line, Segment segment)
        {
            double output;

            if (Cross(line, segment))
            {
                // Si la droite et le segment se croisent la distance est de 0
                output = 0;
            }
            else
            {
                // Sinon c'est la distance minimale entre chaque extremité du segment et la droite puisque c'est forcément un de ces deux points le plus proche
                output = Math.Min(line.Distance(segment.StartPoint), line.Distance(segment.EndPoint));
            }

            return output;
        }

        public static List<RealPoint> GetCrossingPoints(Line line, Segment segment)
        {
            // Vérifie de la même manière qu'une droite mais vérifie ensuite que le point obtenu (s'il existe) appartient bien au segment
            List<RealPoint>  output = LineWithLine.GetCrossingPoints(line, segment);

            if (output.Count > 0 && !segment.Contains(output[0]))
                output.Clear();

            return output;
        }
    }
}
<file_sep>/GoBot/GoBot/TestCode.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GoBot.Actions;
using System.Windows.Forms;
using GoBot.Communications;
using GoBot.Communications.UDP;
using System.Diagnostics;

namespace GoBot
{
    public static class TestCode
    {
        public static void TestEnums()
        {
            StringBuilder errors = new StringBuilder();
            errors.AppendLine("Les valeurs d'énumerations suivantes n'ont pas de traduction littérale dans le nommeur :");
            bool error = false;

            List<Type> typesEnum = new List<Type>();
            typesEnum.Add(typeof(SensorOnOffID));
            typesEnum.Add(typeof(ActuatorOnOffID));
            typesEnum.Add(typeof(ServomoteurID));
            typesEnum.Add(typeof(MotorID));
            typesEnum.Add(typeof(CodeurID));
            typesEnum.Add(typeof(SensorColorID));
            typesEnum.Add(typeof(BaliseID));
            typesEnum.Add(typeof(LidarID));
            typesEnum.Add(typeof(UdpFrameFunction));
            
            foreach (Type type in typesEnum)
            {
                foreach (var value in Enum.GetValues(type))
                {
                    String result = NameFinder.GetNameUnknow((Convert.ChangeType(value, type)));
                    if (result == (Convert.ChangeType(value, type).ToString()) || result == "" || result == "Inconnu")
                    {
                        errors.Append("\t");
                        errors.Append(type.ToString());
                        errors.Append(".");
                        errors.AppendLine(Convert.ChangeType(value, type).ToString());
                        error = true;
                    }
                }
            }

            if (error && Debugger.IsAttached)
            {
                Console.WriteLine(errors.ToString());
                //MessageBox.Show(errors.ToString(), "Attention", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/PolygonWithSegment.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class PolygonWithSegment
    {
        public static bool Contains(Polygon containingPolygon, Segment containedSegement)
        {
            // Il suffit de contenir les deux extrémités du segment et de ne jamais croiser le segment
            // À part si le croisement se fait sur une extremité

            bool result;

            if (Cross(containingPolygon, containedSegement))
            {
                // Si ça se croise : ça peut encore être les extremités qui touchent

                List<RealPoint> crossPoints = GetCrossingPoints(containingPolygon, containedSegement);
                if (crossPoints.Count > 2)
                {
                    // Plus de 2 croisements : le segment n'est pas contenu
                    result = false;
                }
                else
                {
                    // Maximum 2 croisements (= les 2 extremités) : le segment est contenu si les 2 extremités et le milieu sont contenus
                    if (PolygonWithRealPoint.Contains(containingPolygon, containedSegement.StartPoint) && PolygonWithRealPoint.Contains(containingPolygon, containedSegement.EndPoint) && PolygonWithRealPoint.Contains(containingPolygon, containedSegement.Barycenter))
                        result = true;
                    else
                        result = false;
                }
            }
            else
            {
                // Pas de croisement, il suffit de contenir un point du segment
                result = PolygonWithRealPoint.Contains(containingPolygon, containedSegement.StartPoint);
            }

            return result;
        }

        public static bool Cross(Polygon polygon, Segment segment)
        {
            return SegmentWithPolygon.Cross(segment, polygon);
        }

        public static double Distance(Polygon polygon, Segment segment)
        {
            return SegmentWithPolygon.Distance(segment, polygon);
        }

        public static List<RealPoint> GetCrossingPoints(Polygon polygon, Segment segment)
        {
            return SegmentWithPolygon.GetCrossingPoints(segment, polygon);
        }
    }
}
<file_sep>/GoBot/GeometryTester/TestPolygonWithPolygon.cs
﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GeometryTester
{
    [TestClass]
    public class TestPolygonWithPolygon
    {
        #region Contains



        #endregion
    }
}
<file_sep>/GoBot/Geometry/RectangleExtensions.cs
﻿using System.Drawing;

internal static class RectangleExtensions
{
    #region RectangleF

    /// <summary>
    /// Retourne le point central du rectangle
    /// </summary>
    /// <param name="rect">Rectangle dont on cherche le centre</param>
    /// <returns>Point central du rectangle<returns>
    public static PointF Center(this RectangleF rect)
    {
        return new PointF(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
    }

    /// <summary>
    /// Augmente la largeur du rectangle tout en conservant son centre au même endroit
    /// </summary>
    /// <param name="rect">Rectangle à modifier</param>
    /// <param name="newWidth">Nouvelle largeur</param>
    public static RectangleF ExpandWidth(this RectangleF rect, double newWidth)
    {
        rect.X -= (float)(newWidth - rect.Width) / 2;
        rect.Width = (float)newWidth;
        return rect;
    }

    /// <summary>
    /// Augmente la hauteur du rectangle tout en conservant son centre au même endroit
    /// </summary>
    /// <param name="rect">Rectangle à modifier</param>
    /// <param name="newHeight">Nouvelle hauteur</param>
    public static RectangleF ExpandHeight(this RectangleF rect, double newHeight)
    {
        rect.Y -= (float)(newHeight - rect.Height) / 2;
        rect.Height = (float)newHeight;
        return rect;
    }

    /// <summary>
    /// Déplace le rectangle sur les axes X et Y suivant les deltas donnés
    /// </summary>
    /// <param name="rect">Ractangle à déplacer</param>
    /// <param name="deltaX">Déplacement sur l'axe des abscisses</param>
    /// <param name="deltaY">Déplacement sur l'axe des ordonnées</param>
    public static RectangleF Shift(this RectangleF rect, double deltaX, double deltaY)
    {
        rect.X += (float)deltaX;
        rect.Y += (float)deltaY;
        return rect;
    }

    /// <summary>
    /// Déplace le rectangle en modifiant son centre et en conservant sa taille
    /// </summary>
    /// <param name="rect">Rectangle à modifier</param>
    /// <param name="center">Nouveau centre</param>
    public static RectangleF SetCenter(this RectangleF rect, PointF center)
    {
        rect.X = center.X - rect.Width / 2;
        rect.Y = center.Y - rect.Height / 2;
        return rect;
    }

    #endregion

    #region Rectangle

    public static PointF Center(this Rectangle rect)
    {
        return new PointF(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
    }

    #endregion
}
<file_sep>/GoBot/GoBot/PathFinding/Trajectory.cs
﻿using GoBot.Actions;
using Geometry;
using Geometry.Shapes;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;

namespace GoBot.PathFinding
{
    public class Trajectory
    {
        List<RealPoint> _points;
        List<Segment> _lines;

        AnglePosition _startAngle, _endAngle;

        /// <summary>
        /// Liste des points de passage de la trajectoire
        /// </summary>
        public ReadOnlyCollection<RealPoint> Points { get { return _points.AsReadOnly(); } }

        /// <summary>
        /// Liste des segments de la trajectoire
        /// </summary>
        public ReadOnlyCollection<Segment> Lines { get { return _lines.AsReadOnly(); } }

        public AnglePosition StartAngle { get { return _startAngle; } set { _startAngle = value; } }
        public AnglePosition EndAngle { get { return _endAngle; } set { _endAngle = value; } }

        public Trajectory()
        {
            _points = new List<RealPoint>();
            _lines = new List<Segment>();
        }

        public Trajectory(Trajectory other)
        {
            _points = new List<RealPoint>(other.Points);
            _lines = new List<Segment>(other.Lines);
            _startAngle = other.StartAngle;
            _endAngle = other.EndAngle;
        }

        /// <summary>
        /// Ajoute un point de passage à la trajectoire
        /// </summary>
        /// <param name="point">Point à ajouter à la trajectoire</param>
        public void AddPoint(RealPoint point)
        {
            _points.Add(point);

            if (_points.Count > 1)
                _lines.Add(new Segment(Points[Points.Count - 2], Points[Points.Count - 1]));
        }

        /// <summary>
        /// Convertit la trajectoire en suite d'actions de déplacement à effectuer pour suivre la trajectoire
        /// </summary>
        /// <returns>Liste des déplacements correspondant</returns>
        public List<ITimeableAction> ConvertToActions(Robot robot)
        {
            List<ITimeableAction> actions = new List<ITimeableAction>();
            AnglePosition angle = _startAngle;

            for (int i = 0; i < Points.Count - 1; i++)
            {
                RealPoint c1 = new RealPoint(Points[i].X, Points[i].Y);
                RealPoint c2 = new RealPoint(Points[i + 1].X, Points[i + 1].Y);

                Position p = new Position(angle, c1);
                Direction traj = Maths.GetDirection(p, c2);

                // Désactivation 2019 de la possibilité de faire des marches arrière pour conserver le LDIAR d'évitement dans le sens de déplacement du robot
                bool canReverse = false;

                // Teste si il est plus rapide (moins d'angle à tourner) de se déplacer en marche arrière avant la fin
                bool inverse = false;

                if (canReverse)
                {
                    if (i < _points.Count - 2)
                    {
                        inverse = Math.Abs(traj.angle) > 90;
                    }
                    else
                    {
                        // On cherche à minimiser le tout dernier angle quand on fait l'avant dernier
                        AnglePosition finalAngle = angle - traj.angle;
                        inverse = Math.Abs(finalAngle - _endAngle) > 90;
                    }
                    if (inverse)
                        traj.angle = new AngleDelta(traj.angle - 180);
                }

                traj.angle.Modulo();

                if (traj.angle < 0)
                {
                    actions.Add(new ActionPivot(robot, -traj.angle, SensGD.Droite));
                    angle -= traj.angle;
                }
                else if (traj.angle > 0)
                {
                    actions.Add(new ActionPivot(robot, traj.angle, SensGD.Gauche));
                    angle -= traj.angle;
                }

                if (inverse)
                    actions.Add(new ActionRecule(robot, (int)(traj.distance)));
                else
                    actions.Add(new ActionAvance(robot, (int)(traj.distance)));
            }

            AngleDelta diff = angle - _endAngle;
            if (Math.Abs(diff) > 0.2) // Angle minimal en dessous duquel on considère qu'il n'y a pas besoin d'effectuer le pivot
            {
                if (diff < 0)
                    actions.Add(new ActionPivot(robot, -diff, SensGD.Droite));
                else
                    actions.Add(new ActionPivot(robot, diff, SensGD.Gauche));
            }

            return actions;
        }

        public TimeSpan GetDuration(Robot robot)
        {
            return new TimeSpan(ConvertToActions(robot).Sum(o => o.Duration.Ticks));
        }

        public void Paint(Graphics g, WorldScale scale)
        {
            Point previousPoint = Points[0];

            using (Pen redPen = new Pen(Color.Red, 2), whitePen = new Pen(Color.White, 4))
            {
                for (int i = 0; i < Points.Count; i++)
                {
                    Point pointNode = scale.RealToScreenPosition(Points[i]);
                    if (i >= 1)
                    {
                        g.DrawLine(whitePen, pointNode, previousPoint);
                        g.DrawLine(redPen, pointNode, previousPoint);
                    }
                    previousPoint = pointNode;
                }
                for (int i = 0; i < Points.Count; i++)
                {
                    Point point = scale.RealToScreenPosition(Points[i]);
                    g.FillEllipse(Brushes.Red, new Rectangle(point.X - 4, point.Y - 4, 8, 8));
                    g.DrawEllipse(Pens.White, new Rectangle(point.X - 4, point.Y - 4, 8, 8));
                }
            }
        }

        public void RemovePoint(int index)
        {
            if (index == 0)
            {
                _points.RemoveAt(0);
                _lines.RemoveAt(0);
            }
            else if (index == _points.Count - 1)
            {
                _points.RemoveAt(_points.Count - 1);
                _points.RemoveAt(_lines.Count - 1);
            }
            else
            {
                Segment newSeg = new Segment(_points[index - 1], _points[index + 1]);
                _points.RemoveAt(index);
                _lines.RemoveAt(index - 1);
                _lines.RemoveAt(index - 1);
                _lines.Insert(index, newSeg);
            }
        }

        public bool RemoveLine(int index)
        {
            bool ok = true;

            if (index == 0)
            {
                _points.RemoveAt(0);
                _lines.RemoveAt(0);
            }
            else if (index == _lines.Count - 1)
            {
                _points.RemoveAt(_points.Count - 1);
                _points.RemoveAt(_lines.Count - 1);
            }
            else
            {
                Segment s1 = _lines[index - 1];
                Segment s2 = _lines[index + 1];

                if (s1.Line.Cross(s2.Line))
                {
                    RealPoint cross = s1.Line.GetCrossingPoints(s2.Line)[0];
                    Segment newSeg1 = new Segment(s1.EndPoint, cross);
                    Segment newSeg2 = new Segment(cross, s2.StartPoint);

                    _lines[index - 1] = newSeg1;
                    _lines[index + 1] = newSeg2;
                    _lines.RemoveAt(index);
                    _points.RemoveRange(index, 2);
                    _points.Insert(index, cross);
                }
                else
                {
                    ok = false;
                }
            }

            return ok;
        }
    }
}
<file_sep>/GoBot/GoBot/Program.cs
﻿using System;
using System.Linq;
using System.Windows.Forms;
using GoBot.Communications;
using GoBot.IHM;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading;
using System.Diagnostics;
using GoBot.Devices;
using GoBot.Actionneurs;
using GoBot.Threading;
using GoBot.Beacons;
using GoBot.BoardContext;

namespace GoBot
{
    static class Program
    {
        /// <summary>
        /// Point d'entrée principal de l'application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Execution.DesignMode = false;
            Execution.LaunchStart = DateTime.Now;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            SplashScreen.ShowSplash(Properties.Resources.Splash1, new Rectangle(495, 115, 275, 80));
            
            CheckAlreadyLaunched();
            DebugChecks();
            Update();
            CheckIP();

            SplashScreen.SetMessage("Initialisation\nConnexions...", Color.Black);
            SplashScreen.SetImage(Properties.Resources.Splash1);
            ThreadManager.Init();
            Connections.Init();

            SplashScreen.SetMessage("Initialisation\nConfig...", Color.Black);
            SplashScreen.SetImage(Properties.Resources.Splash2);
            Config.Load();

            SplashScreen.SetMessage("Initialisation\nDevices...", Color.Black);
            SplashScreen.SetImage(Properties.Resources.Splash3);
            AllDevices.Init();
            Actionneur.Init();
            SuiviBalise.Init();

            SplashScreen.SetMessage("Initialisation\nRobot...", Color.Black);
            Robots.Init();
            Recalibration.Init();

            SplashScreen.SetMessage("Initialisation\nPlateau...", Color.Black);
            Dessinateur.Init();
            GameBoard.Init();

            SplashScreen.SetMessage("Initialisation\nLogs...", Color.Black);
            SplashScreen.SetImage(Properties.Resources.Splash4);
            Logs.Logs.Init();

            SplashScreen.SetMessage("Initialisation\nInterface...", Color.Black);
            SplashScreen.SetImage(Properties.Resources.Splash5);

            Application.Run(new FenGoBot(args));
        }

        static void DebugChecks()
        {
            if (Debugger.IsAttached)
            {
                TestCode.TestEnums();
            }
        }

        static void CheckIP()
        {
            if(!Dns.GetHostAddresses(Dns.GetHostName()).ToList().Exists(ip => ip.ToString().StartsWith("10.1.0.")))
            {
                SplashScreen.SetMessage("Attention !\nIP non configurée...", Color.Red);

                Thread.Sleep(1000);
            }
        }

        static void CheckAlreadyLaunched()
        {
            int tryCount = 0;
            int tryMax = 5;
            bool ok = false;
            String messageWait = "Execution de GoBot\nen attente";
            
            Process[] proc;
            do
            {
                proc = Process.GetProcessesByName("GoBot");
                ok = (proc.Length <= 1);

                if(!ok)
                {
                    messageWait += ".";
                    SplashScreen.SetMessage(messageWait, Color.Orange);
                    Thread.Sleep(1000);
                }
                    
                tryCount++;

            } while (!ok && tryCount < tryMax);

            if (!ok)
            {
                SplashScreen.SetMessage("GoBot est déjà lancé...", Color.Red);
                Thread.Sleep(1000);
                Environment.Exit(0);
            }
        }

        static void Update()
        {
            // Pas d'update automatique si on est dans l'IDE ou si le fichier NoUpdate existe à coté de l'exe
            if (!Debugger.IsAttached)
            {
                String noUpdateFile = Application.StartupPath + "\\NoUpdate";
                if (!File.Exists(noUpdateFile))
                {
                    SplashScreen.SetMessage("GoBot recherche\ndes mises à jour...", Color.Black);
                    String versionCourante = Application.ProductVersion.Substring(0, Application.ProductVersion.LastIndexOf('.'));
                    String derniereVersion;

                    try
                    {
                        HttpWebRequest r = (HttpWebRequest)WebRequest.Create("http://www.omybot.com/GoBot/version.txt");
                        HttpWebResponse rep = (HttpWebResponse)r.GetResponse();
                        StreamReader sr = new StreamReader(rep.GetResponseStream());
                        derniereVersion = sr.ReadLine();
                        sr.Close();
                    }
                    catch (Exception)
                    {
                        derniereVersion = versionCourante;
                        SplashScreen.SetMessage("Impossible de mettre\nà jour...", Color.Red);
                        Thread.Sleep(1000);
                    }

                    if (versionCourante != derniereVersion)
                    {
                        SplashScreen.SetMessage("Une nouvelle version\n est disponible.", Color.Green);
                        Thread.Sleep(1000);
                        SplashScreen.SetMessage("Téléchargement de la\n dernière version...", Color.FromArgb(50, 50, 50));

                        WebClient wc = new WebClient();
                        String setup = System.IO.Path.GetTempPath() + "SetupGoBot.exe";

                        try
                        {
                            wc.DownloadFile("http://www.omybot.com/GoBot/SetupGoBot.exe", System.IO.Path.GetTempPath() + "SetupGoBot.exe");

                            SplashScreen.SetMessage("GoBot va se relancer...", Color.FromArgb(50, 50, 50));
                            Thread.Sleep(1000);
                            System.Diagnostics.ProcessStartInfo myInfo = new System.Diagnostics.ProcessStartInfo();
                            myInfo.FileName = setup;
                            myInfo.Arguments = "/SP- /VERYSILENT";
                            System.Diagnostics.Process.Start(myInfo);
                            Application.Exit();
                        }
                        catch (Exception)
                        {
                            SplashScreen.SetMessage("Erreur !" + Environment.NewLine + " Mise à jour échouée", Color.Red);
                            Thread.Sleep(2000);
                        }
                    }
                }
            }
        }
    }
}
<file_sep>/GoBot/Geometry/Shapes/ShapesInteractions/SegmentWithCircle.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Geometry.Shapes.ShapesInteractions
{
    internal static class SegmentWithCircle
    {
        public static bool Contains(Segment containingSegment, Circle containedCircle)
        {
            // Contenir un cercle revient à avoir un cercle de rayon 0 dont le centre se trouve sur le segment
            return SegmentWithRealPoint.Contains(containingSegment, containedCircle.Center) && containedCircle.Radius == 0;
        }

        public static bool Cross(Segment segment, Circle circle)
        {
            return CircleWithSegment.Cross(circle, segment);
        }

        public static double Distance(Segment segment, Circle circle)
        {
            return CircleWithSegment.Distance(circle, segment);
        }

        public static List<RealPoint> GetCrossingPoints(Segment segment, Circle circle)
        {
            return CircleWithSegment.GetCrossingPoints(circle, segment);
        }
    }
}
<file_sep>/GoBot/GoBot/Execution.cs
﻿using System;

namespace GoBot
{
    public class Execution
    {
        /// <summary>
        /// Permet de savoir si l'application est mode Design (concepteur graphique) ou en cours d'execution
        /// Le DesignMode de base :
        ///     - Ne fonctionne pas dans les contructeurs
        ///     - Ne fonctionne pas pour les contrôles imbriqués
        /// </summary>
        public static bool DesignMode { get; set; } = true;

        /// <summary>
        /// Obtient ou défini si l'application est en train de se couper
        /// </summary>
        public static bool Shutdown { get; set; } = false;

        /// <summary>
        /// Date de lancement de l'application
        /// </summary>
        public static DateTime LaunchStart { get; set; }

        /// <summary>
        /// Date de lancement de l'application sous format texte triable alphabétiquement
        /// </summary>
        public static String LaunchStartString { get { return Execution.LaunchStart.ToString("yyyy.MM.dd HH\\hmm\\mss\\s"); } }

    }
}
<file_sep>/GoBot/GoBot/Devices/Pepperl/PepperlComm.cs
﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace GoBot.Devices
{
    class PepperlComm
    {
        IPAddress _ip;

        public PepperlComm(IPAddress ip)
        {
            _ip = ip;
        }

        public Dictionary<String, String> SetParameter(String parameter, String value)
        {
            return SendCommand(PepperlConst.CmdSetParameter, parameter, value);
        }

        public Dictionary<String, String> SetParameters(IEnumerable<String> parameters, IEnumerable<String> values)
        {
            return SendCommand(PepperlConst.CmdSetParameter, parameters, values);
        }

        public Dictionary<String, String> SetParameters(params String[] paramVals)
        {
            return SendCommand(PepperlConst.CmdSetParameter, paramVals);
        }

        public Dictionary<String, String> SendCommand(String command)
        {
            return JsonDumbParser.Parse(SendMessage(command));
        }

        public Dictionary<String, String> SendCommand(PepperlCmd command)
        {
            return JsonDumbParser.Parse(SendMessage(command.GetText()));
        }

        public Dictionary<String, String> SendCommand(PepperlCmd command, IEnumerable<String> parameters, IEnumerable<String> values)
        {
            return SendCommand(command.GetText(), parameters, values);
        }

        public Dictionary<String, String> SendCommand(String command, IEnumerable<String> parameters, IEnumerable<String> values)
        {
            if (parameters.Count() != values.Count())
                return new Dictionary<String, String>();

            StringBuilder message = new StringBuilder(command + "?");

            for (int i = 0; i < parameters.Count(); i++)
                message = message.Append(parameters.ElementAt(i) + "=" + values.ElementAt(i) + "&");

            message.Remove(message.Length - 1, 1); // Le dernier & inutile

            String response = SendMessage(message.ToString());

            //Console.WriteLine(response);

            return JsonDumbParser.Parse(response);
        }

        public Dictionary<String, String> SendCommand(PepperlCmd command, params String[] paramVals)
        {
            return SendCommand(command.GetText(), paramVals);
        }

        public Dictionary<String, String> SendCommand(String command, params String[] paramVals)
        {
            if (paramVals.Length % 2 != 0)
                return new Dictionary<String, String>();

            List<String> parameters = new List<String>();
            List<String> values = new List<String>();

            for (int i = 0; i < paramVals.Length; i += 2)
            {
                parameters.Add(paramVals[i]);
                values.Add(paramVals[i + 1]);
            }

            return SendCommand(command, parameters, values);
        }

        private String SendMessage(String message)
        {
            String rep = "";

            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://" + _ip.ToString() + "/cmd/" + message);
                request.Timeout = 500;

                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                using (Stream stream = response.GetResponseStream())
                using (StreamReader reader = new StreamReader(stream))
                {
                    rep = reader.ReadToEnd();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR Lidar Pepperl envoi " + message + " - " + e.Message);
                rep = "";
            }

            return rep;
        }
    }
}
<file_sep>/GoBot/GoBot/Strategies/StrategyMinimumScore.cs
﻿using System.Collections.Generic;
using Geometry;
using Geometry.Shapes;
using GoBot.Movements;
using GoBot.BoardContext;

namespace GoBot.Strategies
{
    /// <summary>
    /// Stratégie qui consiste à marquer juste quelques points (pour homologuation de capacité à marquer des points par exemple)
    /// </summary>
    class StrategyMinimumScore : Strategy
    {
        private bool _avoidElements = true;

        public override bool AvoidElements => _avoidElements;

        List<Movement> mouvements = new List<Movement>();

        protected override void SequenceBegin()
        {
            // Sortir ICI de la zonde de départ

            Robots.MainRobot.UpdateGraph(GameBoard.ObstaclesAll);
            //Robots.MainRobot.MoveForward(500);
            
            if (GameBoard.MyColor == GameBoard.ColorLeftBlue)
            {
                //*mouvements.Add(new MoveAccelerator(Plateau.Elements.AcceleratorYellow));
            }
            else
            {
                //*mouvements.Add(new MoveAccelerator(Plateau.Elements.AcceleratorViolet));
            }
        }
        
        protected override void SequenceCore()
        {
            // TODOYEACHYEAR Ajouter ICI l'ordre de la strat fixe avant détection d'adversaire

            _avoidElements = true;

            foreach (Movement move in mouvements)
            {
                bool ok = false;
                while (!ok)
                {
                    ok = move.Execute();
                }
            }
            
            while (IsRunning)
            {
                while (!Robots.MainRobot.GoToPosition(new Position(0, new RealPoint(700, 1250)))) ;
                while (!Robots.MainRobot.GoToPosition(new Position(180, new RealPoint(3000 - 700, 1250)))) ;
            }
        }
    }
}
<file_sep>/GoBot/GoBot/IHM/Panels/PotarControl.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GoBot.Actionneurs;
using System.Threading;
using GoBot.Threading;
using System.Reflection;
using GoBot.Devices;

namespace GoBot.IHM
{
    public partial class PotarControl : UserControl
    {
        private ThreadLink _linkPolling;
        private Positionable _currentPositionnable;
        private int _currentPosition;

        public Dictionary<String, PropertyInfo> _positionsProp;


        public PotarControl()
        {
            InitializeComponent();
            _linkPolling = null;
        }

        private void PotarControl_Load(object sender, EventArgs e)
        {
            if (!Execution.DesignMode)
            {
                cboPositionnable.Items.AddRange(Config.Positionnables.ToArray());
            }
        }

        private void cboPositionnable_SelectedIndexChanged(object sender, EventArgs e)
        {
            lock (this)
            {
                _currentPositionnable = (Positionable)cboPositionnable.SelectedItem;

                SetPositions(_currentPositionnable);
            }
        }

        private void switchBouton_ValueChanged(object sender, bool value)
        {
            if (value)
            {
                trackBar.Min = _currentPositionnable.Minimum;
                trackBar.Max = _currentPositionnable.Maximum;
                _linkPolling = ThreadManager.CreateThread(link => PollingLoop());
                _linkPolling.StartThread();
            }
            else
            {
                _linkPolling.Cancel();
            }
        }

        private void PollingLoop()
        {
            double posValue;
            double ticksCurrent, ticksMin, ticksRange;
            int pointsParTour = 4096;
            double toursRange = 5;

            _linkPolling.RegisterName();

            ticksCurrent = 0;//TODO2020 AllDevices.RecGoBot.GetCodeurPosition();
            ticksMin = ticksCurrent;
            ticksRange = pointsParTour * toursRange;

            posValue = _currentPositionnable.Minimum;

            while (!_linkPolling.Cancelled)
            {
                lock (this)
                {
                    _linkPolling.LoopsCount++;

                    toursRange = trackBarSpeed.Value;
                    ticksRange = pointsParTour * toursRange;
                    Thread.Sleep(50);
                    ticksCurrent = 0;//TODO2020 AllDevices.RecGoBot.GetCodeurPosition();

                    if (ticksCurrent > ticksMin + ticksRange)
                        ticksMin = ticksCurrent - ticksRange;
                    else if (ticksCurrent < ticksMin)
                        ticksMin = ticksCurrent;

                    posValue = (ticksCurrent - ticksMin) / ticksRange * (_currentPositionnable.Maximum - _currentPositionnable.Minimum) + _currentPositionnable.Minimum;

                    posValue = Math.Min(posValue, _currentPositionnable.Maximum);
                    posValue = Math.Max(posValue, _currentPositionnable.Minimum);

                }

                SetPosition((int)posValue);
            }

            _linkPolling = null;
        }

        private void SetPosition(int position)
        {
            if (position != _currentPosition)
            {
                _currentPosition = position;
                _currentPositionnable.SendPosition((int)_currentPosition);
                this.InvokeAuto(() =>
                {
                    trackBar.SetValue(_currentPosition);
                    lblValue.Text = _currentPosition.ToString();
                });
            }
        }

        private void trackBarSpeed_ValueChanged(object sender, double value)
        {
            lblSpeed.Text = "Rapport " + value.ToString();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            String[] tab = ((String)(cboPositions.SelectedItem)).Split(new char[] { ':' });

            String position = tab[0].Trim().ToLower();
            int valeur = Convert.ToInt32(tab[1].Trim());

            int index = cboPositions.SelectedIndex;

            _positionsProp[(String)cboPositions.SelectedItem].SetValue((Positionable)cboPositionnable.SelectedItem, _currentPosition, null);
            trackBar.Min = _currentPositionnable.Minimum;
            trackBar.Max = _currentPositionnable.Maximum;

            SetPositions(_currentPositionnable);
            cboPositions.SelectedIndex = index;
            
            Config.Save();
        }

        private void SetPositions(Positionable pos)
        {
            PropertyInfo[] properties = _currentPositionnable.GetType().GetProperties();

            List<String> noms = new List<string>();
            _positionsProp = new Dictionary<string, PropertyInfo>();

            foreach (PropertyInfo property in properties)
            {
                if (property.Name != "ID")
                {
                    noms.Add(Config.PropertyNameToScreen(property) + " : " + property.GetValue(_currentPositionnable, null));
                    _positionsProp.Add(noms[noms.Count - 1], property);
                }
            }

            cboPositions.Items.Clear();
            cboPositions.Items.AddRange(noms.ToArray());
            btnSave.Enabled = false;
        }

        private void cboPositions_SelectedIndexChanged(object sender, EventArgs e)
        {
            btnSave.Enabled = true;
            _currentPosition = (int)_positionsProp[(String)cboPositions.SelectedItem].GetValue(cboPositionnable.SelectedItem, null);
        }
    }
}
<file_sep>/GoBot/GoBot/Devices/Pepperl/JsonDumbParser.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GoBot.Devices
{
    public class JsonDumbParser
    {
        public static Dictionary<String, String> Parse(String json)
        {
            Dictionary<String, String> values = new Dictionary<string, string>();

            if (json == "") return null;

            json = json.Replace("{", "").Replace("}", "").Replace(",\r\n", ":").Replace("\r\n", "");

            List<String> splits = json.Split(new String[] { ":" }, StringSplitOptions.None).ToList();

            for (int i = 0; i < splits.Count; i += 2)
            {
                values.Add(splits[i].Replace("\"", ""), splits[i + 1].Replace("\"", ""));
            }

            return values;
        }
    }
}
