commit e837cdf1d2f8ec86d487318c1016aa5120197403
Author: lbihannic <lucas.bihannic@inrae.fr>
Date:   Mon Apr 14 15:43:59 2025 +0200

    add timers on chambers view

diff --git a/GES_PAC/GES_PAC.csproj.user b/GES_PAC/GES_PAC.csproj.user
index 0cc333a..06228c2 100644
--- a/GES_PAC/GES_PAC.csproj.user
+++ b/GES_PAC/GES_PAC.csproj.user
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <IsFirstTimeProjectOpen>False</IsFirstTimeProjectOpen>
     <ActiveDebugFramework>net9.0-android</ActiveDebugFramework>
-    <ActiveDebugProfile>Xiaomi 23090RA98G (Android 15.0 - API 35)</ActiveDebugProfile>
+    <ActiveDebugProfile>Samsung SM-G556B (Android 14.0 - API 34)</ActiveDebugProfile>
     <SelectedPlatformGroup>PhysicalDevice</SelectedPlatformGroup>
     <DefaultDevice>pixel_7_-_api_35</DefaultDevice>
     <SelectedDevice>Xiaomi 23090RA98G</SelectedDevice>
@@ -11,6 +11,10 @@
   <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net9.0-android|AnyCPU'">
     <DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
   </PropertyGroup>
+  <PropertyGroup Condition="'$(TargetPlatformIdentifier)'=='iOS'">
+    <RuntimeIdentifier>ios-arm64</RuntimeIdentifier>
+    <PlatformTarget>arm64</PlatformTarget>
+  </PropertyGroup>
   <ItemGroup>
     <None Update="App.xaml">
       <SubType>Designer</SubType>
diff --git a/GES_PAC/Model/Serie.cs b/GES_PAC/Model/Serie.cs
index 9625409..cb83912 100644
--- a/GES_PAC/Model/Serie.cs
+++ b/GES_PAC/Model/Serie.cs
@@ -95,6 +95,10 @@ namespace GES_PAC.Model
         {
             return SeriesAnimales.Sum(sa => sa.Comportements.Count());
         }
+        public SerieAnimal? GetSerieAnimalByNumBoite(int numBoite)
+        {
+            return SeriesAnimales.FirstOrDefault(sa => sa.NumeroBoite == numBoite);
+        }
         #endregion
     }
 }
diff --git a/GES_PAC/Model/SerieAnimal.cs b/GES_PAC/Model/SerieAnimal.cs
index 22e78e1..65f1c62 100644
--- a/GES_PAC/Model/SerieAnimal.cs
+++ b/GES_PAC/Model/SerieAnimal.cs
@@ -38,6 +38,10 @@ namespace GES_PAC.Model
         {
             return Mesures.Last();
         }
+        public Mesure GetFirstMeasure()
+        {
+            return Mesures.First();
+        }
 
         public void AddBehaviour(Comportement newBehaviour)
         {
@@ -46,7 +50,20 @@ namespace GES_PAC.Model
 
         public bool HasBehaviour()
         {
-            return Comportements.Any();
+            return Comportements.Count != 0;
+        }
+        public string GetTimeInChamber()
+        {
+            if (Mesures.Count == 0)
+                return "";
+
+            TimeSpan timeInChamber;
+            if (IsOut)
+                timeInChamber = GetLastMeasure().Time - GetFirstMeasure().Time;
+            else
+                timeInChamber = DateTime.Now - GetFirstMeasure().Time;
+
+            return $"{timeInChamber.Hours:00}:{timeInChamber.Minutes:00}";
         }
     }
 }
diff --git a/GES_PAC/View/ChambersView.xaml b/GES_PAC/View/ChambersView.xaml
index 90d5a67..749243f 100644
--- a/GES_PAC/View/ChambersView.xaml
+++ b/GES_PAC/View/ChambersView.xaml
@@ -48,20 +48,20 @@
                                 </Grid.RowDefinitions>
 
                                 <!-- Boutons Gauche -->
-                                <controls:ChamberButtonView Grid.Row="5" Grid.Column="0" ChamberId="1" />
-                                <controls:ChamberButtonView Grid.Row="4" Grid.Column="0" ChamberId="3" />
-                                <controls:ChamberButtonView Grid.Row="3" Grid.Column="0" ChamberId="5" />
-                                <controls:ChamberButtonView Grid.Row="2" Grid.Column="0" ChamberId="7" />
-                                <controls:ChamberButtonView Grid.Row="1" Grid.Column="0" ChamberId="9" />
-                                <controls:ChamberButtonView Grid.Row="0" Grid.Column="0" ChamberId="11" />
+                                <controls:ChamberButtonView Grid.Row="5" Grid.Column="0" ChamberId="1" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="4" Grid.Column="0" ChamberId="3" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="3" Grid.Column="0" ChamberId="5" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="2" Grid.Column="0" ChamberId="7" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="1" Grid.Column="0" ChamberId="9" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="0" Grid.Column="0" ChamberId="11" Timer="{Binding Timer}"/>
 
                                 <!-- Boutons Droite -->
-                                <controls:ChamberButtonView Grid.Row="5" Grid.Column="2" ChamberId="2" />
-                                <controls:ChamberButtonView Grid.Row="4" Grid.Column="2" ChamberId="4" />
-                                <controls:ChamberButtonView Grid.Row="3" Grid.Column="2" ChamberId="6" />
-                                <controls:ChamberButtonView Grid.Row="2" Grid.Column="2" ChamberId="8" />
-                                <controls:ChamberButtonView Grid.Row="1" Grid.Column="2" ChamberId="10" />
-                                <controls:ChamberButtonView Grid.Row="0" Grid.Column="2" ChamberId="12" />
+                                <controls:ChamberButtonView Grid.Row="5" Grid.Column="2" ChamberId="2" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="4" Grid.Column="2" ChamberId="4" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="3" Grid.Column="2" ChamberId="6" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="2" Grid.Column="2" ChamberId="8" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="1" Grid.Column="2" ChamberId="10" Timer="{Binding Timer}"/>
+                                <controls:ChamberButtonView Grid.Row="0" Grid.Column="2" ChamberId="12" Timer="{Binding Timer}"/>
 
                                 <!-- Couloir central -->
                                 <BoxView Grid.Column="1" Grid.RowSpan="6" HeightRequest="500" WidthRequest="75" Color="Gray"/>
@@ -83,10 +83,10 @@
                 </VerticalStackLayout>
             </ScrollView>
             <StackLayout
-                Margin="-40"
+                Margin="-90"
                 HorizontalOptions="Center"
                 Orientation="Horizontal"
-                VerticalOptions="Fill">
+                VerticalOptions="End">
 
                 <Button 
                     Text="Terminer les mesures"
diff --git a/GES_PAC/View/Controls/ChamberButtonView.xaml b/GES_PAC/View/Controls/ChamberButtonView.xaml
index 1291a5e..419a1b7 100644
--- a/GES_PAC/View/Controls/ChamberButtonView.xaml
+++ b/GES_PAC/View/Controls/ChamberButtonView.xaml
@@ -1,22 +1,29 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
-             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
-             xmlns:vm="clr-namespace:GES_PAC.ViewModel.Controls"
-             x:Class="GES_PAC.View.Controls.ChamberButtonView"
-             x:Name="ChamberButtonControl"
-             x:DataType="vm:ChamberButtonViewModel">
+<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"  
+            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
+            xmlns:vm="clr-namespace:GES_PAC.ViewModel.Controls"  
+            x:Class="GES_PAC.View.Controls.ChamberButtonView"  
+            x:Name="ChamberButtonControl"  
+            x:DataType="vm:ChamberButtonViewModel">  
 
-    <Grid>
-        <Button Text="{Binding ButtonText}"
-                Style="{StaticResource ChambreButtonStyle}"
-                Command="{Binding OnClickChamberCommand}"
-                CommandParameter="{Binding ButtonCommandParameter}"
-                BackgroundColor="{Binding ButtonBackgroundColor}"
-                IsEnabled="{Binding ButtonIsEnabled}" />
-        <Label Text="{Binding ButtonLabel}"
-               FontSize="10"
-               Margin="3"
-               HorizontalOptions="Start"
-               VerticalOptions="Start" />
-    </Grid>
+   <Grid>  
+       <Button Text="{Binding ButtonText}"  
+           Style="{StaticResource ChambreButtonStyle}"  
+           Command="{Binding OnClickChamberCommand}"  
+           CommandParameter="{Binding ChamberId}"  
+           BackgroundColor="{Binding ButtonBackgroundColor}"  
+           IsEnabled="{Binding ButtonIsEnabled}" />  
+
+       <Label Text="{Binding ButtonLabel}"  
+           FontSize="12"  
+           Margin="3"  
+           HorizontalOptions="Start"  
+           VerticalOptions="Start" />  
+
+       <Label Text="{Binding TimeIn}"  
+           FontSize="12"  
+           Margin="3"  
+           HorizontalOptions="End"  
+           VerticalOptions="Start" />  
+   </Grid>  
 </ContentView>
diff --git a/GES_PAC/View/Controls/ChamberButtonView.xaml.cs b/GES_PAC/View/Controls/ChamberButtonView.xaml.cs
index d2a86c5..084887d 100644
--- a/GES_PAC/View/Controls/ChamberButtonView.xaml.cs
+++ b/GES_PAC/View/Controls/ChamberButtonView.xaml.cs
@@ -1,32 +1,58 @@
-﻿using GES_PAC.ViewModel.Controls;
+﻿using GES_PAC.ViewModel;
+using GES_PAC.ViewModel.Controls;
 
-namespace GES_PAC.View.Controls
+namespace GES_PAC.View.Controls;
+
+public partial class ChamberButtonView : ContentView
 {
-    public partial class ChamberButtonView : ContentView
+    public static readonly BindableProperty ChamberIdProperty =
+        BindableProperty.Create(nameof(ChamberId), typeof(int), typeof(ChamberButtonView), default(int), propertyChanged: OnChamberIdChanged);
+
+    public static readonly BindableProperty TimerProperty =
+        BindableProperty.Create(nameof(Timer), typeof(TimerPublisher), typeof(ChamberButtonView), propertyChanged: OnTimerChanged);
+
+    public TimerPublisher? Timer
+    {
+        get => (TimerPublisher?)GetValue(TimerProperty);
+        set => SetValue(TimerProperty, value);
+    }
+
+    public int ChamberId
+    {
+        get => (int)GetValue(ChamberIdProperty);
+        set => SetValue(ChamberIdProperty, value);
+    }
+    public ChamberButtonView()
+    {
+        InitializeComponent();
+    }
+
+    protected override void OnBindingContextChanged()
     {
-        public static readonly BindableProperty ChamberIdProperty =
-            BindableProperty.Create(nameof(ChamberId), typeof(int), typeof(ChamberButtonView), default(int), propertyChanged: OnChamberIdChanged);
+        base.OnBindingContextChanged();
 
-        public int ChamberId
+        if (BindingContext is not ChamberButtonViewModel && ChamberId != 0 && Timer != null)
         {
-            get => (int)GetValue(ChamberIdProperty);
-            set => SetValue(ChamberIdProperty, value);
+            var vm = new ChamberButtonViewModel(ChamberId, Timer);
+            BindingContext = vm;
         }
+    }
+    private static void OnChamberIdChanged(BindableObject bindable, object oldValue, object newValue)
+    {
+        var control = (ChamberButtonView)bindable;
+        int id = (int)newValue;
 
-        public ChamberButtonView()
+        if (control.BindingContext is ChamberButtonViewModel vm)
         {
-            BindingContext = new ChamberButtonViewModel();
-            InitializeComponent();
+            vm.UpdateChamberId(id);
         }
-
-        private static void OnChamberIdChanged(BindableObject bindable, object oldValue, object newValue)
+    }
+    private static void OnTimerChanged(BindableObject bindable, object oldValue, object newValue)
+    {
+        if (bindable is ChamberButtonView view)
         {
-            var control = (ChamberButtonView)bindable;
-
-            if (control.BindingContext is ChamberButtonViewModel viewModel)
-            {
-                viewModel.UpdateProperties((int)newValue);
-            }
+            view.OnBindingContextChanged();
         }
     }
+
 }
diff --git a/GES_PAC/ViewModel/ChambersViewModel.cs b/GES_PAC/ViewModel/ChambersViewModel.cs
index bca48a5..1541c69 100644
--- a/GES_PAC/ViewModel/ChambersViewModel.cs
+++ b/GES_PAC/ViewModel/ChambersViewModel.cs
@@ -1,14 +1,13 @@
-using GES_PAC.Model;
 using GES_PAC.View;
+using GES_PAC.ViewModel.Controls;
+using System.Collections.ObjectModel;
 using System.Windows.Input;
 
 namespace GES_PAC.ViewModel
 {
     public class ChambersViewModel : BaseViewModel
     {
-
         #region Attributs
-        
         #endregion
 
         #region Commandes
@@ -16,13 +15,17 @@ namespace GES_PAC.ViewModel
         #endregion
 
         #region Propriétés
-
+        public ObservableCollection<ChamberButtonViewModel> Chambers { get; } = new();
+        public TimerPublisher Timer { get; }
         #endregion
 
         #region Constructeurs
         public ChambersViewModel()
         {
             TerminateSetCommand = new Command(async () => await TerminateSet());
+
+            Timer = new TimerPublisher();
+            Timer.Start();
         }
         #endregion
 
diff --git a/GES_PAC/ViewModel/Controls/ChamberButtonViewModel.cs b/GES_PAC/ViewModel/Controls/ChamberButtonViewModel.cs
index 5d7aea4..1ee6455 100644
--- a/GES_PAC/ViewModel/Controls/ChamberButtonViewModel.cs
+++ b/GES_PAC/ViewModel/Controls/ChamberButtonViewModel.cs
@@ -7,16 +7,28 @@ namespace GES_PAC.ViewModel.Controls
     public class ChamberButtonViewModel : BaseViewModel
     {
         #region Attributs
+        private Serie _currentSet;
         private string _buttonText;
         private int _chamberId;
         private Color _buttonBackgroundColor;
         private bool _buttonIsEnabled;
         private string _buttonLabel;
+        private string _timeIn;
         private ICommand _onClickChamberCommand;
+        private readonly TimerPublisher _timer;
+
         #endregion
 
         #region Propriétés
-
+        public Serie CurrentSet
+        {
+            get => _currentSet;
+            set
+            {
+                _currentSet = value;
+                OnPropertyChanged();
+            }
+        }
         public string ButtonText {
             get => _buttonText;
             set
@@ -61,7 +73,18 @@ namespace GES_PAC.ViewModel.Controls
                 OnPropertyChanged();
             }
         }
-        public Serie CurrentSet { get; set; }
+        public string TimeIn
+        {
+            get => _timeIn;
+            set
+            {
+                _timeIn = value;
+                OnPropertyChanged();
+            }
+        }
+        public SerieAnimal AnimalSet;
+        private bool _isSubscribed = false;
+
         #endregion
 
         #region Commandes
@@ -77,38 +100,74 @@ namespace GES_PAC.ViewModel.Controls
         #endregion
 
         #region Constructeurs
-        #endregion
 
-        #region Méthodes
-        public void UpdateProperties(int chamberId)
+        public ChamberButtonViewModel(int chamberId, TimerPublisher timerPublisher)
         {
-            OnClickChamberCommand = new Command(async () => await GoToEnterAnimal());
+            ChamberId = chamberId;
+            _timer = timerPublisher;
 
+            OnClickChamberCommand = new Command(async () => await GoToChamber());
+            ButtonBackgroundColor = Colors.White;
+            ButtonIsEnabled = true;
+            ButtonLabel = "0";
+            TimeIn = "";
             CurrentSet = JourneeViewModel.Instance.GetCurrentSet();
 
-            ButtonText = chamberId.ToString();
+            UpdateChamberId(chamberId);
+        }
+
+        #endregion
+
+        #region Méthodes
+        public void UpdateChamberId(int chamberId)
+        {
             ChamberId = chamberId;
+            UpdateProperties();
+        }
+        public void UpdateProperties()
+        {
+            AnimalSet = CurrentSet.GetSerieAnimalByNumBoite(ChamberId);
+            ButtonText = ChamberId.ToString();
+
+            int measureCount = AnimalSet?.GetMeasureCount() ?? 0;
 
-            ButtonIsEnabled = JourneeViewModel.Instance.GetCurrentSet().GetIsInByNumeroBoite(chamberId);
-            bool hasBehaviour = CurrentSet.GetHasBehaviourByNumeroBoite(chamberId);
-            int count = CurrentSet.GetMeasureNumberByNumeroBoite(chamberId);
-            if (hasBehaviour)
-                ButtonBackgroundColor = Colors.Yellow;
-            else
-                ButtonBackgroundColor = count > 0 ? Colors.LightGreen : Colors.White;
-            ButtonLabel = count.ToString();
+            if (measureCount == 0)
+                return;
+
+            if (!_isSubscribed && !AnimalSet.IsOut)
+            {
+                _timer.Register(OnTimerTick);
+                _isSubscribed = true;
+            }
+
+            if (_isSubscribed && AnimalSet.IsOut)
+            {
+                _timer.Unregister(OnTimerTick);
+                _isSubscribed = false;
+            }
+
+            ButtonIsEnabled = !AnimalSet.IsOut;
+            ButtonBackgroundColor = AnimalSet.HasBehaviour() ? Colors.Yellow : Colors.LightGreen;
+            ButtonLabel = measureCount.ToString();
+            TimeIn = AnimalSet.GetTimeInChamber();
         }
-        private async Task GoToEnterAnimal()
+
+        private async Task GoToChamber()
         {
             if (IsBusy) return;
             IsBusy = true;
 
-            var targetView = (CurrentSet.GetMeasureNumberByNumeroBoite(ChamberId) == 0) ? nameof(EnterAnimalView) : nameof(CreateMeasureView);
+            var targetView = (CurrentSet.GetSerieAnimalByNumBoite(ChamberId) == null) ? nameof(EnterAnimalView) : nameof(CreateMeasureView);
 
             await Shell.Current.GoToAsync($"{targetView}?chamberId={ChamberId}");
 
             IsBusy = false;
         }
+        public void OnTimerTick(object? sender, EventArgs e)
+        {
+            TimeIn = AnimalSet?.GetTimeInChamber() ?? "";
+        }
+
         #endregion
     }
 }
diff --git a/GES_PAC/ViewModel/MainViewModel.cs b/GES_PAC/ViewModel/MainViewModel.cs
index 2f03710..f51f572 100644
--- a/GES_PAC/ViewModel/MainViewModel.cs
+++ b/GES_PAC/ViewModel/MainViewModel.cs
@@ -56,15 +56,15 @@ namespace GES_PAC.ViewModel
         public MainViewModel(ConnexionService connService)
         {
             //ONLY FOR TESTS
-            //var date = DateTime.Now;
-            //var newDay = new Journee(date, PersonViewModel.Instance.Persons[0], PlaceViewModel.Instance.Places[0], "");
-            //newDay.AddSet(new Serie(date, date, 25, 25, 25));
-            //newDay.AddCalibration(new MesureCalibration(date, 50, 40, 500, TypeCalibration.Air, ""), PhaseCalibration.Debut);
-            //newDay.AddCalibration(new MesureCalibration(date, 0, 0, 1000000, TypeCalibration.Methane, "dazdazd"), PhaseCalibration.Debut);
-            //newDay.AddCalibration(new MesureCalibration(date, 30, 30, 3000, TypeCalibration.Melange, "deqsfsdf"), PhaseCalibration.Debut);
-            //newDay.GetCurrentSet().AddSerieAnimal(new SerieAnimal(1, 500, date, ""));
-            //newDay.GetCurrentSet().AddMeasure(new Mesure(date, 50, 50, 50), 1, false);
-            //JourneeViewModel.Instance.Journees.Add(newDay);
+            var date = DateTime.Now;
+            var newDay = new Journee(date, PersonViewModel.Instance.Persons[0], PlaceViewModel.Instance.Places[0], "");
+            newDay.AddSet(new Serie(date, date, 25, 25, 25));
+            newDay.AddCalibration(new MesureCalibration(date, 50, 40, 500, TypeCalibration.Air, ""), PhaseCalibration.Debut);
+            newDay.AddCalibration(new MesureCalibration(date, 0, 0, 1000000, TypeCalibration.Methane, "dazdazd"), PhaseCalibration.Debut);
+            newDay.AddCalibration(new MesureCalibration(date, 30, 30, 3000, TypeCalibration.Melange, "deqsfsdf"), PhaseCalibration.Debut);
+            newDay.GetCurrentSet().AddSerieAnimal(new SerieAnimal(1, 500, date, ""));
+            newDay.GetCurrentSet().AddMeasure(new Mesure(date, 50, 50, 50), 1, false);
+            JourneeViewModel.Instance.Journees.Add(newDay);
             //ONLY FOR TESTS
 
             GoToCreateDayCommand = new Command(async () => await GoToCreateDayPage());
diff --git a/GES_PAC/ViewModel/TimerPublisher.cs b/GES_PAC/ViewModel/TimerPublisher.cs
new file mode 100644
index 0000000..4d2bcac
--- /dev/null
+++ b/GES_PAC/ViewModel/TimerPublisher.cs
@@ -0,0 +1,22 @@
+﻿namespace GES_PAC.ViewModel
+{
+    public class TimerPublisher
+    {
+        private readonly IDispatcherTimer _timer;
+        public event EventHandler? Tick;
+
+        public TimerPublisher()
+        {
+            _timer = Dispatcher.GetForCurrentThread().CreateTimer();
+            _timer.Interval = TimeSpan.FromSeconds(15);
+            _timer.Tick += (s, e) => Tick?.Invoke(this, EventArgs.Empty);
+        }
+
+        public void Start() => _timer.Start();
+        public void Stop() => _timer.Stop();
+
+        public void Register(EventHandler handler) => Tick += handler;
+        public void Unregister(EventHandler handler) => Tick -= handler;
+    }
+
+}
