Comment pouvons-nous aider ?

Print

Comment gérer automatiquement l’heure d’été dans l’IHM à l’aide d’une macro ?

 

Dans ce post, nous allons vous montrer comment gérer automatiquement l’heure d’été dans l’IHM à l’aide d’une macro. Le changement d’heure a été appliqué pour la première fois en 1916. Depuis 2002, le changement d’heure s’effectue de manière uniforme dans tous les États membres de l’Union européenne :

  • le passage à l’heure d’été a lieu dans la nuit du dernier samedi de mars
  • le passage à l’ heure d ‘hiver a lieu dans la nuit du dernier samedi d’octobre

Pour rappel, les pays de l’UE sont actuellement divisés en trois fuseaux horaires :

  • Europe occidentale (GMT) : Irlande et Portugal
  • Europe centrale (GMT 1) : Allemagne, Autriche, Belgique, Croatie, Danemark, Espagne, France, Hongrie, Italie, Luxembourg, Malte, Pays-Bas, Pologne, République tchèque, Slovaquie, Slovénie et Suède
  • Europe de l’Est (GMT 2) : Bulgarie, Chypre, Estonie, Finlande, Grèce, Lettonie, Lituanie et Roumanie.

Avec l’outil HMI

Dans l’éditeur de macros, ajoutez une nouvelle macro et copiez-la.

#include "MacroInit.h"
// Returns the day-of-month of the last Sunday of a given month/year
// Uses the Tomohiko Sakamoto-inspired method (no division by 7 needed)
int LastSundayOfMonth(int month, int year)
{
// Day of week of the last day of the month (0=Sunday ... 6=Saturday)
// Zeller-like formula for day-of-week of day 1, then offset to last day
int y = year;
int m = month;
int daysInMonth;
switch(m)
{
case 1:  daysInMonth = 31; break;
case 2:
if ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0))
daysInMonth = 29;
else
daysInMonth = 28;
break;
case 3:  daysInMonth = 31; break;
case 4:  daysInMonth = 30; break;
case 5:  daysInMonth = 31; break;
case 6:  daysInMonth = 30; break;
case 7:  daysInMonth = 31; break;
case 8:  daysInMonth = 31; break;
case 9:  daysInMonth = 30; break;
case 10: daysInMonth = 31; break;
case 11: daysInMonth = 30; break;
case 12: daysInMonth = 31; break;
default: daysInMonth = 30; break;
}
// Compute day-of-week of the last day of the month
// Using Zeller's formula (0=Sunday, 1=Monday ... 6=Saturday)
if (m < 3) { m += 12; y -= 1; }
int k = y % 100;
int j = y / 100;
int dow = (daysInMonth + (13*(m+1))/5 + k + k/4 + j/4 - 2*j) % 7;
// Zeller returns: 0=Saturday, 1=Sunday, 2=Monday ... 6=Friday
// Normalize to: 0=Sunday, 1=Monday ... 6=Saturday
dow = (dow + 6) % 7;
// Subtract offset to reach last Sunday (dow==0)
return daysInMonth - dow;
}
void Macro_main(IN *p)
{
MarcoInit
// Automatic Daylight Saving Time (DST) management
// <!> This script must be called every minute
// DST start : last Sunday of March   - 02:00 -> 03:00 (clock forward +1h)
// DST end   : last Sunday of October - 03:00 -> 02:00 (clock back   -1h)
// LocalBit[50000]: 0 = summer time, 1 = winter time (backed up on power failure)
// Catchup window: up to 6 days after the DST Sunday (HMI was powered off)
int minute    = BCD2BIN(LocalWord[60001]); // Current minute
int hour      = BCD2BIN(LocalWord[60002]); // Current hour
int day       = BCD2BIN(LocalWord[60003]); // Day of month
int month     = BCD2BIN(LocalWord[60004]); // Month
int year      = BCD2BIN(LocalWord[60005]); // Year (needed for LastSundayOfMonth)
int dayOfWeek = BCD2BIN(LocalWord[60006]); // Monday=1 ... Sunday=7
// --- DST START : last Sunday of March at 02:00 -> 03:00 ---
// Window: from Sunday at 02:00 until the following Saturday at 23:59 (6 days catchup)
int lastSunMarch   = LastSundayOfMonth(3, year);
int daysSinceDSTStart = day - lastSunMarch; // Positive if we are past the last Sunday
int dstStartWindow = (daysSinceDSTStart == 0 && hour >= 2) || // Sunday from 02:00
(daysSinceDSTStart >= 1 && daysSinceDSTStart <= 6); // Mon to Sat catchup
if ((LocalBit[50000] == 1) &&
(month == 3)           &&
dstStartWindow)
{
LocalWord[60002] = BIN2BCD(hour + 1); // Shift clock forward by 1 hour
LocalBit[50000]  = 0;                 // Flag: now summer time
}
// --- DST END : last Sunday of October at 03:00 -> 02:00 ---
// Window: from Sunday at 03:00 until the following Saturday at 23:59 (6 days catchup)
int lastSunOctober = LastSundayOfMonth(10, year);
int daysSinceDSTEnd = day - lastSunOctober;
int dstEndWindow = (daysSinceDSTEnd == 0 && hour >= 3) || // Sunday from 03:00
(daysSinceDSTEnd >= 1 && daysSinceDSTEnd <= 6); // Mon to Sat catchup
if ((LocalBit[50000] == 0) &&
(month == 10)          &&
dstEndWindow)
{
LocalWord[60002] = BIN2BCD(hour - 1); // Shift clock back by 1 hour
LocalBit[50000]  = 1;                 // Flag: now winter time
}
}

Ajoutez la macro dans la liste « Macro globale » et définissez sa fréquence d’exécution à 60000 ms (1 minute) pour mettre à jour l’horloge toutes les minutes.

Comment cela fonctionne-t-il ?

Le programme s’exécute toutes les minutes et lit l’heure actuelle (minute, heure, jour, mois, année, jour de la semaine) dans les registres.
Toutes les valeurs de l’heure sont stockées au format BCD, elles sont donc d’abord converties en décimales.
Une fonction d’aide calcule le nombre de jours du mois en cours, y compris les années bissextiles.
Pour détecter le dernier dimanche du mois, le code vérifie : dayOfWeek == 7 (dimanche) et day 7 > daysInMonth.
S’il s’agit du dernier dimanche de mars à exactement 03:00, l’horloge est avancée à 04:00 (heure d’été).
Un bit indicateur (LocalBit[65535]) est activé pour indiquer l’heure d’été.
S’il s’agit du dernier dimanche d’octobre à exactement 03:00, l’horloge est ramenée à 02:00 (heure d’hiver).
Le bit de drapeau est commuté pour indiquer l’heure d’hiver, ce qui évite des corrections répétées.
Cela permet d’éviter les boucles infinies où la même heure serait traitée plusieurs fois.
Le système maintient donc automatiquement l’heure locale synchronisée avec les règles officielles de l’heure d’été.

Vous pouvez remplacer LB65535 par LB51999 pour sauvegarder la valeur(types de registre IHM : LB, LW, RWI et registre de rétention/sauvegarde)

Voir aussi Comment régler et synchroniser l’horloge de l’automate ACE avec l’IHM à l’aide d’une macro