Simulateur de process industriel en temps réel

Denis Hulo

Objectif : réaliser une simulation de process industriel dans un formulaire Access en utilisant le timer.

Niveau requis : avancé.

Commentez cet article : Commentez

- Titre : Simulateur de process industriel en temps réel

- Auteur : Denis Hulo

- Parution : 29 mai 2018

- Mise à jour : 3 octobre 2019

- Public visé :

- Licence : Copyright ® 2018 Denis Hulo. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Rappel du contexte

- III. Description du processus

- IV. État d'avancement du process

- IV-A. Formules mathématiques de base

- IV-B. Formules employées

- V. Rendu final du simulateur de process

- VI. Tables nécessaires

- VI-A. T_Gamme

- VI-B. T_MachineGamme

- VII. Formulaire F_SimulateurProcess

- VII-A. Contrôles constituant le tableau des machines

- VII-A-1. Note sur les objets barres de progression

- VII-B. Zones de texte pour paramétrer le process

- VII-B-1. Zone de texte txtIntervalleTimer

- VII-B-2. Zone de texte txtEtatProcess

- VII-B-3. Zone de texte txtNbreMachines

- VII-C. Contrôles Indicateurs d'avancement du process

- VII-D. Boutons de commande pour le contrôle du process

- VII-D-1. Bouton de commande cmdSauvegarder

- VII-D-2. Bouton de commande cmdDemarrer

- VII-D-3. Bouton de commande cmdArreter

- VII-D-4. Bouton de commande cmdInitialiser

- VII-E. Événement sur Activation

- VII-E-1. Procédure événementielle sur Activation

- VII-E-2. Procédure de mise à jour des indicateurs

- VII-F. Événement sur Timer

- VII-F-1. Procédure événementielle sur Timer

- VIII. Téléchargement

- IX. Remerciements

I - Introduction

Le sujet a déjà été abordé dans simulation de process industriel. J'avais utilisé à l'époque des requêtes complexes pour réaliser l'application et on ne pouvait pas vraiment suivre le process en temps réel.

Dans ce nouvel article, je décris toujours ce type de simulateur pour une gamme de fabrication, mais cette fois-ci j'utilise le timer du formulaire, pour suivre en temps réel le processus.

II - Rappel du contexte

Dans l'industrie, les machines peuvent être regroupées en ensembles autonomes de production spécialisés par type de produits qui utilisent les mêmes machines : ces ensembles s'appellent des îlots. Les flux de produits (matières) peuvent utiliser les postes de travail de l'îlot dans un ordre différent suivant leur gamme de fabrication, alors que dans une ligne de fabrication l'ordre est impératif.

Description d'un îlot de machines

Pour simplifier, on va ici considérer une gamme de fabrication composée de m machines placées en série.

III - Description du processus

Détaillons les différentes étapes du processus se déroulant sur une machine :

Étapes du processus

- Si la machine est prête et s'il y a des pièces en attente, alors on lance l'opération sur la machine.

- Sinon on attend, jusqu'à ce que ces deux conditions soient réunies.

- Une fois l'opération terminée, la pièce est transférée vers la machine suivante.

Processus d'usinage sur une machine

IV - État d'avancement du process

L'objectif est d'obtenir, à partir de formules mathématiques, une estimation du nombre total de pièces déjà usinées à un instant donné, de la date et l'heure de fin du process, et de l'avancement du process. Nous utiliserons ces fonctions traduites en VBA pour afficher ces indicateurs sur le formulaire principal.

IV-A - Formules mathématiques de base

La durée de fabrication de n pièces

duree_fabrication = (n-1)*duree_max + duree_total

- duree_max : durée de l'opération la plus longue de la gamme de fabrication ;

- duree_total : somme totale des durées de toutes les opérations de la gamme de fabrication.

On en déduit :

Nombre de pièces fabriquées après une durée d

nbre_fabrique = ((d - duree_total) \ duree_max) + 1

La durée de fabrication doit toujours être supérieure ou égale à la somme totale des durées de toutes les opérations.

IV-B - Formules employées

Date de fin du process

date_fin = DateAdd("s",duree_fabrication, date_debut)

- duree_fabrication : durée de fabrication des n pièces suivant la 1re formule ;

- date_debut : date de début du process.

Quantité de pièces fabriquées après une durée d

nbre_fabrique = ((d - duree_total) \ duree_max) + 1

- duree_max : durée de l'opération la plus longue de la gamme de fabrication ;

- duree_total : somme totale des durées de toutes les opérations de la gamme de fabrication.

Cette dernière formule va nous permettre de connaître, à un instant donné, le nombre total de pièces déjà terminées en bout de gamme.

Avancement réel du process

avancement_reel = nbre_fabrique/nbre_total

- nbre_fabrique : quantité de pièces fabriquées après une durée d ;

- nbre_total : nombre total de pièces à fabriquer.

Avancement estimé du process

avancement_estime = nbre_estime/nbre_total

- nbre_estime : quantité estimée de pièces fabriquées après une durée d ;

- nbre_total : nombre total de pièces à fabriquer.

V - Rendu final du simulateur de process

Voici le rendu du formulaire qui présente, pour chaque machine, le nombre de pièces en attente et la progression de l'opération.

Aperçu du formulaire F_SimulateurProcess

On constate que le simulateur permet de visualiser l'avancement des opérations sur les différentes machines grâce à des barres de progression.

Démonstration du simulateur en vidéo

Voici les objets nécessaires pour aboutir à ce résultat.

VI - Tables nécessaires

Pour sauvegarder les informations concernant la gamme de fabrication et ses machines on a besoin de deux tables.

VI-A - T_Gamme

Cette table contient les informations concernant la gamme de fabrication.

Nom du champ

Type de données

Description

IdGamme

Entier long

Identifiant de la gamme de fabrication

NomGamme

Texte

Nom de la gamme de fabrication

EtatProcess

Texte

État du process en cours sur la gamme de fabrication (Arrêt ; Progression)

NbreMachines

Entier long

Nombre de machines composant la gamme de fabrication

IntervalleTimer

Entier long

Intervalle de temps en millisecondes entre deux exécutions de la procédure sur Timer

DebutProcess

Date/Heure

Début du process en cours sur la gamme de fabrication

QtePieces

Entier long

Quantité totale de pièces à fabriquer

QteFab

Entier long

Quantité de pièces déjà fabriquées

VI-B - T_MachineGamme

Cette table permet de sauvegarder les informations relatives aux machines de la gamme de fabrication, telles que l'opération, l'état de la machine, la durée de l'opération et du transfert de la pièce.

Nom du champ

Type de données

Description

IdMachine

Texte

Identifiant de la machine

IdGamme

Entier long

Identifiant de la gamme contenant la machine

Operation

Texte

Nom de l'opération réalisée sur la machine

DureeOperation

Réel simple

Durée en secondes de l'opération sur la machine

DureeTransfert

Réel simple

Durée de transfert de la pièce vers la machine suivante

QteMachine

Entier long

Quantité de pièces en attente sur la machine

EtatMachine

Texte

État de la machine : (R : Ready ; P : Progression)

ProgressionOperation

Réel simple

Valeur indiquant la progression de la pièce sur la machine

ProgressionTransfert

Réel simple

Valeur indiquant la progression du transfert de la pièce vers la machine suivante.

VII - Formulaire F_SimulateurProcess

Il est relié à la table T_Gamme. Nous allons décrire les différents éléments du formulaire.

Aperçu du formulaire F_SimulateurProcess

Le simulateur permet de visualiser la progression des pièces sur les différentes machines.

VII-A - Contrôles constituant le tableau des machines

Tableau des machines

Contrôles constituant le tableau

- Zones de texte txtIdMachine1..14 : affichent les indices ou numéros d'ordre des machines ;

- Zones de texte txtOperation1..14 : affichent les noms des opérations ;

- Zones de texte txtDureeOperation1..14 : les durées en secondes des opérations effectuées sur les différentes machines ;

- Zones de texte txtDureeTransfert1..14 : les durées en secondes de transfert sur les machines suivantes ;

- Zones de texte txtQteMachine1..14 : le nombre de pièces en attente sur les différentes machines ;

- Zones de texte txtEtatMachine1..14 : affichent l'état des machines (R : Ready ; P : Progression) ;

- Etiquettes lnlProgressionOperationMax1..14 : utilisées pour afficher les barres de largeur maximale ;

- Etiquettes lnlProgressionOperationPrg1..14 : utilisées pour afficher les barres de progression de largeur proportionnelle à l'avancement des opérations ;

- Etiquettes lnlProgressionOperationPct1..14 : utilisées pour afficher les pourcentages d'avancement des opérations ;

- Etiquettes lnlProgressionTransfertMax1..14 : utilisées pour afficher les barres de largeur maximale ;

- Etiquettes lnlProgressionTransfertPrg1..14 : utilisées pour afficher les barres de progression de largeur proportionnelle à l'avancement des transferts ;

- Etiquettes lnlProgressionTransfertPct1..14 : utilisées pour afficher les pourcentages d'avancement des transferts.

VII-A-1 - Note sur les objets barres de progression

Ils indiquent la progression de l'usinage et du transfert des pièces grâce aux propriétés max, donnant la durée totale de l'opération, et value indiquant l'avancement de l'opération.

Il s'agit d'objets créés à partir du module de classe clsLblProg, réalisé par Adam Waller, et nécessitant d'être inséré dans votre projet Access.

Les deux tableaux des objets Barre de progression sont définis en haut du module du formulaire :

Déclaration des tableaux des barres de progression

Option Compare Database
Option Explicit

' Tableaux des objets Barre de progression basés sur la classe clsLblProg
Dim prgProgressionOperation(1 To 14) As clsLblProg
Dim prgProgressionTransfert(1 To 14) As clsLblProg
...

On initialise les deux tableaux des objets Barre de progression sur ouverture du formulaire :

Procédure événementielle sur Ouverture

Private Sub Form_Open(Cancel As Integer)
' On crée les objets Barre de progression à partir du module de classe clsLblProg
CreerBarresProgression

End Sub

Procédure CreerBarresProgression

Public Sub CreerBarresProgression()
' Création des objets Barre de progression
Dim i As Long ' Indice des barres de progression dans les tableaux

For i = 1 To 14 ' Parcours des indices des barres de progression
Set prgProgressionOperation(i) = New clsLblProg ' Création de l'instance de l'objet Barre de progression pour l'opération
prgProgressionOperation(i).Initialize Me("lblProgressionOperationMax" & i), _
Me("lblProgressionOperationPrg" & i), _
Me("lblProgressionOperationPct" & i)

Set prgProgressionTransfert(i) = New clsLblProg ' Création de l'instance de l'objet Barre de progression pour le transfert
prgProgressionTransfert(i).Initialize Me("lblProgressionTransfertMax" & i), _
Me("lblProgressionTransfertPrg" & i), _
Me("lblProgressionTransfertPct" & i)
Next i

End Sub

On libère les objets Barre de progression des deux tableaux sur fermeture du formulaire :

Déclaration des tableaux des barres de progression

Private Sub Form_Close()
' On libère les objets Barre de progression
LibererBarresProgression

End Sub

Procédure LibererBarresProgression

Public Sub LibererBarresProgression()
' Libération des objets Barre de progression
Dim i As Long ' Indice des barres de progression dans les tableaux

For i = 1 To 14 ' Parcours des indices des barres de progression
' libération des objets liés aux barres de progression de l'opération et du transfert
Set prgProgressionOperation(i) = Nothing
Set prgProgressionTransfert(i) = Nothing
Next i

End Sub

VII-B - Zones de texte pour paramétrer le process

Paramètres du process

VII-B-1 - Zone de texte txtIntervalleTimer

On y renseigne l'intervalle du timer, c'est-à-dire le temps qui s'écoule en millisecondes entre deux exécutions de la procédure sur Timer.

VII-B-2 - Zone de texte txtEtatProcess

Indique l'état du processus : Arrêt ; Progression. Elle est simplement mise à jour en cliquant respectivement sur les boutons cmdArreter et cmdDemarrer.

VII-B-3 - Zone de texte txtNbreMachines

Affiche le nombre de machines composant la gamme de fabrication.

Procédure événementielle sur après-maj

Private Sub txtNbreMachines_AfterUpdate()
' Procédure d'affichage des lignes du tableau en fonction de la valeur saisie dans la zone de texte
Dim i As Long, n As Long ' Indice et nombre de machines

i = 1

n = Nz(Me.txtNbreMachines.Value, 0) ' On copie dans la variable n le nombre maximum de machines.

If n > 14 Then
Me.txtNbreMachines.Value = 14 ' On définit la valeur maximale de saisie à 14.
n = 14
End If

For i = 1 To n ' On parcourt les lignes du tableau pour les afficher

' On affiche les zones de texte du tableau
Me("txtIdMachine" & i).Visible = True
Me("txtOperation" & i).Visible = True
Me("txtDureeOperation" & i).Visible = True
Me("txtDureeTransfert" & i).Visible = True
Me("txtQteMachine" & i).Visible = True

Me("txtEtatMachine" & i).Visible = True

' On affiche les barres de progression
Me("prgProgressionOperation" & i).Visible = True
Me("prgTransfertMachine" & i).Visible = True

Next i

While i <= 14 ' On parcourt les lignes restantes pour les masquer

' On masque les zones de texte du tableau
Me("txtidMachine" & i).Visible = False
Me("txtOperation" & i).Visible = False
Me("txtDureeOperation" & i).Visible = False
Me("txtDureeTransfert" & i).Visible = False
Me("txtQteMachine" & i).Visible = False

Me("txtEtatMachine" & i).Visible = False

' On masque les barres de progression
Me("prgProgressionOperation" & i).Visible = False
Me("prgTransfertMachine" & i).Visible = False

i = i + 1 ' Indice suivant

Wend

End Sub

VII-C - Contrôles Indicateurs d'avancement du process

Indicateurs d'avancement du process

Zones de texte indicateurs d'avancement

- txtDebutProcess : date et heure de début du process ;

- txtFinProcess : date et heure estimées de la fin du process ;

- txtQteFab : quantité de pièces déjà fabriquées ;

- txtQteEstimee : quantité estimée de pièces déjà fabriquées ;

- txtAvancementReel : indicateur d'avancement réel du process ;

- txtAvancementEstime : indicateur de l'avancement théorique du process.

VII-D - Boutons de commande pour le contrôle du process

Boutons de commande pour le contrôle du process

VII-D-1 - Bouton de commande cmdSauvegarder

Bouton permettant de sauvegarder l'état des machines dans la table T_MachineGamme.

Déroulé de la procédure

- Vidage de la table T_MachineGamme.

- Ouverture du Recordset lié à la table T_MachineGamme.

- Parcours ligne par ligne du tableau de contrôles représentant les machines, et création pour chaque ligne d'un enregistrement dans la table T_MachineGamme.

- Fermeture et libération des variables.

Procédure événementielle sur clic

Private Sub CmdSauvegarder_Click()
' Procédure de sauvegarde de l'état des machines
On Error GoTo err_CmdSauvegarder_Click
Dim i As Long ' Indice de la machine
Dim db As DAO.Database ' Référence à la base courante
Dim rs As DAO.Recordset ' Réfèrence au jeu d'enregistrements lié à la table des machines

If Not LignesOK Then ' Si une ligne est incomplète
MsgBox ("Une ligne comporte une case vide !")
Exit Sub
End If

Set db = CurrentDb ' Référence à la base courante

DoCmd.SetWarnings False ' Déactivation des alertes
DoCmd.RunSQL ("delete from T_MachineGamme where IdGamme=" & Nz(Me.IdGamme, 0))  ' Suppression du contenu de la table T_MachineGamme
DoCmd.SetWarnings True ' Activation des alertes

Set rs = db.OpenRecordset("T_MachineGamme", dbOpenDynaset) ' Ouverture du jeu d'enregistrements lié à la table T_MachineGamme

i = 1 ' Initialisation de la variable d'indice

Do While Nz(Me("txtIdMachine" & i).Value) <> "" ' On parcourt les machines tant que la ligne n'est pas vide

rs.AddNew

' Création de l'enregistrement avec les informations contenus dans les zones de texte et barres de progression d'indice i
rs!IdMachine = Me("txtIdMachine" & i).Value
rs!IdGamme = Me.IdGamme.Value
rs!Operation = Me("txtOperation" & i).Value
rs!DureeOperation = Me("txtDureeOperation" & i).Value
rs!dureeTransfert = Me("txtDureeTransfert" & i).Value
rs!QteMachine = Me("txtQteMachine" & i).Value
rs!EtatMachine = Me("txtEtatMachine" & i).Value
rs!ProgressionOperation = prgProgressionOperation(i).Value
rs!TransfertMachine = prgProgressionTransfert(i).Value

rs.Update

i = i + 1 ' Indice suivant

If i > 14 Then ' Si on dépasse l'indice maximum des machines
Exit Do ' On sort de la boucle
End If

Loop

err_CmdSauvegarder_Click:

If Err.Number <> 0 Then ' Gestion d'erreur
DoCmd.SetWarnings True ' Activation des alertes au cas où elles soient déactivées
MsgBox (Err.Description) ' Affichage du message d'erreur
End If

On Error Resume Next

' Libération des variables
rs.Close
Set rs = Nothing

Set db = Nothing

End Sub

VII-D-2 - Bouton de commande cmdDemarrer

Bouton permettant de démarrer le process.

Procédure événementielle sur clic

Private Sub CmdDemarrer_Click()
' Procédure de lancement du process

If Not LignesOK Then ' Si une ligne est incomplète
MsgBox ("Une ligne comporte une case vide !")
Exit Sub ' Sortie
End If

If Me.EtatProcess = "Progression" Then ' Si le process est déjà en cours
MsgBox ("Process déjà en cours !")
Exit Sub ' Sortie
End If

If MsgBox("Souhaitez-vous démarrer le process ?", vbYesNo) = vbYes Then ' Si on accepte
Me.txtEtatProcess.Value = "Progression" ' On met l'état du process sur "Progression"
If Nz(Me.txtDebutProcess) = "" Then
' Mise à jour de la date de début du process
Me.txtDebutProcess = Now()
End If
Me.TimerInterval = Me.txtIntervalleTimer  ' On active le timer avec l'intervalle d'exécution défini sur le formulaire
VerrouillageTableau (True) ' Verrouillage du tableau des machines
End If

End Sub

VII-D-3 - Bouton de commande cmdArreter

Bouton permettant d'arrêter le processus.

Procédure événementielle sur clic

Private Sub CmdArreter_Click()
' Procédure d'arrêt du process

If Me.EtatProcess = "Arrêt" Then ' Si le process est déjà arrêté
MsgBox ("Process déjà arrêté !")
Exit Sub ' Sortie
End If

If MsgBox("Souhaitez-vous arrêter le process ?", vbYesNo) = vbYes Then ' Si on accepte
Me.txtEtatProcess.Value = "Arrêt en cours" ' On met l'état du process à "Arrêt en cours"
VerrouillageTableau (False) ' déverrouillage du tableau des machines
End If

End Sub

VII-D-4 - Bouton de commande cmdInitialiser

Bouton permettant d'initialiser le process en repartant avec le nombre total de pièces en attente sur la 1re machine.

Déroulé de la procédure

- Détermination du nombre total de pièces en attente, en fabrication et fabriquées.

- Parcours du tableau des machines.

- Pour chaque machine, initialisation des quantités en attente, de l'état de la machine et des barres de progression.

Procédure événementielle sur clic

Private Sub CmdInitialiser_Click()
' Procédure d'initialisation du process
On Error GoTo err_CmdInitialiser_Click
Dim i As Long, nbPieces As Long ' Indice des machines et nombre total de pièces
Dim lngProgress As Long, lngTransfert As Long
Dim dureemaxProgress As Long, dureemaxTransfert As Long ' Durées maximales des opérations et des transferts

If MsgBox("Souhaitez-vous initialiser le process ?", vbYesNo) = vbNo Then ' Si on refuse
Exit Sub
End If

If Me.txtEtatProcess.Value = "Progression" Then ' Si un process est en cours
MsgBox ("Interrompre le process avant d'initialiser !")
Exit Sub
End If

If Me.txtEtatProcess.Value = "Arrêt en cours" Then ' Si le processus est en cours d'arrêt
MsgBox ("Arrêt en cours !")
Exit Sub
End If

If Not LignesOK Then 'Si une ligne est incomplète
MsgBox ("Une ligne comporte une case vide !")
Exit Sub
End If

i = 1 ' Initialisation de l'indice des machines
nbPieces = Nz(Me.txtQteFab.Value, 0)

' On initialise la largeur des barres de progression
lngProgress = Nz(Me.lblProgressionOperation.Width, 0)
lngTransfert = Nz(Me.lblProgressionTransfert.Width, 0)

' On initialise la durée maximum des barres de progression
dureemaxProgress = Nz(Me("txtDureeOperation" & i).Value, 0)
dureemaxTransfert = Nz(Me("txtDureeTransfert" & i).Value, 0)

' On détermine le nombre total de pièces à usiner
Do While (Nz(Me("txtIdMachine" & i).Value) <> "") ' On parcourt les machines

' On met à jour le compteur du nombre total de pièces
nbPieces = nbPieces + Nz(Me("txtQteMachine" & i).Value, 0) - (Me("txtEtatMachine" & i).Value = "P") - (prgProgressionTransfert(i).Tag = "P")

If (Nz(Me("txtDureeOperation" & i).Value, 0) > dureemaxProgress) Then ' on retient la durée maximum pour la machine
dureemaxProgress = Nz(Me("txtDureeOperation" & i).Value, 0)
End If

If (Nz(Me("txtDureeTransfert" & i).Value, 0) > dureemaxTransfert) Then ' on retient la durée maximum pour le transfert
dureemaxTransfert = Nz(Me("txtDureeTransfert" & i).Value, 0)
End If

i = i + 1 ' Indice suivant

If i > 14 Then Exit Do ' Sortie si l'indice est supérieur à 14

Loop

i = 1

Do While (Nz(Me("txtIdMachine" & i).Value) <> "") ' Tant que la ligne n'est pas vide

If i = 1 Then ' Si on est sur la 1re ligne
' On copie le nombre total de pièces pour la 1re machine
Me("txtQteMachine" & i).Value = nbPieces
Else
' On initialise les quantités en attente sur les machines
Me("txtQteMachine" & i).Value = 0
End If

Me("txtEtatMachine" & i).Value = "R" ' On positionne l'état de la machine sur "R" pour "Ready"

' On définit largeur des barres de progression
prgProgressionOperation(i).Width = (Me("txtDureeOperation" & i).Value / dureemaxProgress) * lngProgress
prgProgressionTransfert(i).Width = (Me("txtDureeTransfert" & i).Value / dureemaxProgress) * lngProgress

' On initialise les barres de progression
prgProgressionOperation(i).Max = Me("txtDureeOperation" & i).Value
prgProgressionOperation(i).Value = 0

prgProgressionTransfert(i).Max = Me("txtDureeTransfert" & i).Value
prgProgressionTransfert(i).Value = 0
prgProgressionTransfert(i).Tag = ""

i = i + 1 ' Indice suivant

If i > 14 Then Exit Do ' Si on dépasse l'indice maximum des machines on sort

Loop

' Me.txtQtePieces.Value = nbPieces ' On met à jour le champ QtePieces de la table T_Gamme avec le nombre total de pièces
Me.txtQteFab.Value = Null ' On initialise le nombre de pièces fabriquées
Me.txtQteEstimee.Value = Null

' Effacement des indicateurs d'avancement
Me.txtAvancementReel.Value = Null
Me.txtAvancementEstime.Value = Null

' Effacement de la date de début et de fin du process
Me.txtDebutProcess.Value = Null
Me.txtFinProcess.Value = Null

err_CmdInitialiser_Click:

If Err.Number <> 0 Then ' Gestion d'erreur
MsgBox (Err.Description) ' Affichage du message d'erreur
End If

End Sub

VII-E - Événement sur Activation

Procédure événementielle s'exécutant sur l'activation d'un enregistrement.

VII-E-1 - Procédure événementielle sur Activation

Déroulé de la procédure

- Ouverture du Recordset lié à la table T_MachineGamme.

- Parcours du jeu enregistrements avec mise à jour des contrôles du tableau des machines situés sur le formulaire.

- Masquage des lignes en trop dans le tableau des machines.

- Mise à jour des indicateurs.

Procédure événementielle sur activation

Private Sub Form_Current()
On Error GoTo err_Form_Current
Dim i As Long ' Indice de la machine
Dim db As DAO.Database ' Référence à la base courante
Dim rs As DAO.Recordset ' Référence au jeu d'enregistrements des machines
Dim lngProgress As Long, lngTransfert As Long
Dim dureemaxProgress As Long, dureemaxTransfert As Long
Dim sSQL As String

Me.cmbGamme = Nz(Me.IdGamme, Null)

If Me.txtEtatProcess = "Progression" Then ' Si il y a un process en cours
Me.TimerInterval = Me.txtIntervalleTimer ' Alors on active le timer
End If

' Constitution de la chaîne SQL
sSQL = "select * from T_MachineGamme where IdGamme=" & Nz(Me.IdGamme, 0) & " order by IdMachine;"

Set db = CurrentDb ' Référence à la base courante

' Ouverture du jeu d'enregistrements contenant les données concernant les machines de la gamme en cours
Set rs = db.OpenRecordset(sSQL, dbOpenSnapshot)

lngProgress = Me.lblProgressionOperation.Width ' Définit la largeur par défaut de la barre de progression
lngTransfert = Me.lblProgressionTransfert.Width ' Définit la largeur par défaut de la barre de Transfert

dureemaxProgress = Nz(DMax("DureeOperation", "T_MachineGamme", "IdGamme=" & Nz(Me.IdGamme, 0)), 0) ' Durée de l'opération la plus longue sur les machines de la gamme
dureemaxTransfert = Nz(DMax("DureeTransfert", "T_MachineGamme", "IdGamme=" & Nz(Me.IdGamme, 0)), 0) ' Durée du transfert la plus longue entre les machines de la gamme

i = 1 ' Initialisation de la variable d'indice

Do While Not rs.EOF ' On parcourt les machines

' On affiche les zones de texte du tableau des machines
Me("txtIdMachine" & i).Visible = True
Me("txtOperation" & i).Visible = True
Me("txtDureeOperation" & i).Visible = True
Me("txtDureeTransfert" & i).Visible = True
Me("txtQteMachine" & i).Visible = True

Me("txtEtatMachine" & i).Visible = True

' On affiche les barres de progression
prgProgressionOperation(i).Visible = True
prgProgressionTransfert(i).Visible = True

' Mise à jour des contrôles du formulaire associé aux différentes machines
Me("txtIdMachine" & i).Value = rs!IdMachine
Me("txtOperation" & i).Value = rs!Operation
Me("txtDureeOperation" & i).Value = rs!DureeOperation
Me("txtDureeTransfert" & i).Value = rs!dureeTransfert
Me("txtQteMachine" & i).Value = rs!QteMachine

Me("txtEtatMachine" & i).Value = rs!EtatMachine

prgProgressionOperation(i).Width = (rs!DureeOperation / dureemaxProgress) * lngProgress
prgProgressionOperation(i).Max = rs!DureeOperation
prgProgressionOperation(i).Value = Nz(rs!ProgressionOperation, 0)

prgProgressionTransfert(i).Width = (rs!dureeTransfert / dureemaxProgress) * lngProgress
prgProgressionTransfert(i).Max = rs!dureeTransfert
prgProgressionTransfert(i).Value = Nz(rs!TransfertMachine, 0)

rs.MoveNext ' Enregistrement suivant
i = i + 1 ' Indice suivant

If i > 14 Then Exit Do
Loop

Me.txtNbreMachines.Value = (i - 1)

While i <= 14

' On masque les zones de texte du tableau des machines
Me("txtIdMachine" & i).Visible = False
Me("txtOperation" & i).Visible = False
Me("txtDureeOperation" & i).Visible = False
Me("txtDureeTransfert" & i).Visible = False
Me("txtQteMachine" & i).Visible = False

Me("txtEtatMachine" & i).Visible = False

' On masque les barres de progression
prgProgressionOperation(i).Visible = False
prgProgressionTransfert(i).Visible = False

' Mise à jour des contrôles du formulaire associé aux différentes machines
Me("txtIdMachine" & i).Value = ""
Me("txtOperation" & i).Value = ""
Me("txtDureeOperation" & i).Value = ""
Me("txtDureeTransfert" & i).Value = ""
Me("txtQteMachine" & i).Value = ""

Me("txtEtatMachine" & i).Value = ""

prgProgressionOperation(i).Width = lngProgress
prgProgressionOperation(i).Max = 1
prgProgressionOperation(i).Value = 0

prgProgressionTransfert(i).Width = lngTransfert
prgProgressionTransfert(i).Max = 1
prgProgressionTransfert(i).Value = 0

i = i + 1 ' Indice suivant

Wend

' On met à jour les indicateurs d'avancement
MajIndicateursAvancement

VerrouillageTableau (Me.txtEtatProcess = "Progression") ' Verrouillage/déverrouillage du tableau des machines

err_Form_Current:

If Err.Number <> 0 Then ' Gestion d'erreur
MsgBox (Err.Description) ' Affichage du message d'erreur
End If

On Error Resume Next

' Libération des variables

rs.Close
Set rs = Nothing

Set db = Nothing

End Sub

VII-E-2 - Procédure de mise à jour des indicateurs

À la fin de la procédure principale, on met à jour les indicateurs de date de fin du process, de quantité fabriquée et estimée, d'avancement réel et estimé, en utilisant les formules mathématiques vues en section IV.

Procédure MajIndicateursAvancement

Public Sub MajIndicateursAvancement()
' On met à jour les indicateurs d'avancement
Dim dm As Long, dt As Long ' Variables indiquant la durée maximum et total des opérations sur les machines
Dim dp As Long, dr As Long ' Variables indiquant la durée estimée et restante du process

dm = Nz(DMax("[DureeOperation]", "T_MachineGamme", "IdGamme=" & Nz(Me.IdGamme, 0)), 0) ' Durée maximum entre les opérations de la gamme
dt = Nz(DSum("[DureeOperation] + [DureeTransfert]", "T_MachineGamme", "IdGamme=" & Nz(Me.IdGamme, 0)), 0) ' Somme des durées des opérations transfert compris
dr = (Nz([QtePieces], 0) - Nz([QteFab], 0) - 1) * dm + dt ' Durée restante selon la formule duree_restante = (nbre_restant - 1)*duree_max + duree_totale

If Not IsNull([DebutProcess]) Then ' Si date de début du process non nulle
' Durée écoulée depuis le lancement du process
dp = DateDiff("s", CDate([DebutProcess]), Now())
End If

If Not IsNull([DebutProcess]) Then ' Si date de début du process non nulle
' Date et heure estimées de fin du process
Me.txtFinProcess = DateAdd("s", dr, [DebutProcess])
Else
Me.txtFinProcess = Null
End If

If (dm <> 0) And (dp >= dt) Then ' On évite la division par 0 et le cas où aucune pièce n'a encore été fabriquée
' Qté estimée de pièces déjà fabriquées
Me.txtQteEstimee = ((dp - dt) \ dm) + 1
Else
Me.txtQteEstimee = Null
End If

If (Nz([QtePieces], 0) <> 0) And (Not IsNull([QteFab])) Then ' On évite la division par 0
' Indicateur d'avancement réel du process
Me.txtAvancementReel = Nz([QteFab], 0) / [QtePieces]
Else
Me.txtAvancementReel = Null
End If

If (Nz([QtePieces], 0) <> 0) And (Not IsNull([txtQteEstimee])) Then ' On évite la division par 0
' Indicateur de l'avancement théorique du process
Me.txtAvancementEstime = CLng(Nz([txtQteEstimee], 0)) / [QtePieces]
Else
Me.txtAvancementEstime = Null
End If

If Nz(Me.txtQtePieces, 0) = Nz(txtQteFab, 0) Then ' Si toutes les pièces ont été fabriquées
Me.IntervalleTimer = 0 ' On désactive le timer, ce qui interrompt le processus.
Me.txtFinProcess = Now() ' On met à jour le champ FinProcess de la table T_Gamme avec la date et l'heure actuelles
End If

End Sub

VII-F - Événement sur Timer

Description de la procédure événementielle form_timer qui s'exécute à intervalle régulier. On pourra prendre comme intervalle 1000 ms soit 1 seconde :

Propriétés Timer

On constate que la propriété Intervalle minuterie (IntervalleTimer) est réglée à zéro, elle indique que le timer est par défaut inactif. La mise à jour de cette propriété avec une valeur entière X entraine automatiquement l'exécution de la procédure sur minuterie toutes les X millisecondes.

VII-F-1 - Procédure événementielle sur Timer

Déroulé de la procédure

- Pour chaque machine, on teste si elle est prête (R : Ready) et s'il y a des pièces en attente.

- Si c'est le cas, on met la machine à l'état de progression (P : Progress) et on décrémente le nombre de pièces en attente.

- Sinon, s'il y a déjà une pièce, on incrémente l'indicateur de progression de la pièce sur la machine.

- Pour chaque machine, on teste si la machine est à l'état de progression et si l'indicateur de progression est au maximum.

- Si c'est le cas, on initialise l'indicateur de progression, on met la machine à l'état prête (R) et on transfère la pièce sur la machine suivante.

- Sinon, on incrémente l'indicateur de progression de la pièce sur la machine.

Procédure sur Timer - Partie n°1

Private Sub Form_Timer()
On Error GoTo err_Form_Timer
Dim i As Long ' Variable d'indice de la machine
Dim p As Single ' Variable indiquant le pas de progression en secondes
Dim dm As Long, dt As Long ' Variables indiquant la durée maximum et total des opérations sur les machines
Dim dp As Long, dr As Long ' Variables indiquant la durée estimée et restante du process
Dim ar As Boolean ' Variable indiquant si le process est arrêté

p = CLng(Me.txtIntervalleTimer.Value) / 1000 ' Définition du pas de progression en secondes

i = 1 ' Initialisation de l'indice des machines
ar = True ' Initialisation de la variable indiquant si le process est à l'arrêt

Do While (i <= 14) ' On parcourt les machines

If (Me("txtEtatMachine" & i).Value = "R") And (Me.txtEtatProcess.Value = "Progression") Then ' si la machine est prête et si le process est en cours
If (CLng(Nz(Me("txtQteMachine" & i).Value, 0)) > 0) Then ' s'il y a encore des pièces en attente
Me("txtQteMachine" & i).Value = CLng(Me("txtQteMachine" & i).Value) - 1 ' on décompte le nombre de pièces en attente
Me("txtEtatMachine" & i).Value = "P" ' On met la machine en progression
ar = False ' Processus en cours
End If
Else
If (Me("txtEtatMachine" & i).Value = "P") Then ' si la machine est en progression
ar = False ' Processus en cours
If (prgProgressionOperation(i).Value < prgProgressionOperation(i).Max) Then ' Si la pièce n'est pas terminée
' Durée de fabrication pas encore atteinte
prgProgressionOperation(i).Value = prgProgressionOperation(i).Value + p ' On incrémente d'une seconde la progression
Else
Me("txtEtatMachine" & i).Value = "R" ' On met la machine dans l'état 'Prête' (R: 'Ready')
prgProgressionOperation(i).Value = 0
prgProgressionTransfert(i).Value = 0
prgProgressionTransfert(i).Tag = "P"
End If
End If
End If

...

End Sub

Déroulé de la procédure — partie n°2

- On teste pour chaque machine, si le transfert est terminé.

- Si c'est le cas, on incrémente le nombre de pièces en attente sur la machine suivante.

- Sinon, on incrémente l'indicateur de progression du transfert.

- À la fin, on met à jour les indicateurs d'avancement du process.

Procédure sur Timer - Partie n°2

...

If (prgProgressionTransfert(i).Tag = "P") Then ' Si la Transfert est en cours

If (prgProgressionTransfert(i).Value < prgProgressionTransfert(i).Max) Then ' Si la Transfert n'est pas terminée
prgProgressionTransfert(i).Value = prgProgressionTransfert(i).Value + p ' On incrémente d'une seconde la progression
ar = False ' Processus en cours
Else
prgProgressionTransfert(i).Value = 0 ' On remet à 0 l'indicateur de progression du Transfert
prgProgressionTransfert(i).Tag = ""

If i < 14 Then ' Si on a pas dépassé la dernière machine
If Nz(Me("txtIdMachine" & (i + 1)).Value) <> "" Then ' si on est pas sur la dernière machine
' on incrémente le nombre de pièces en attente sur la machine suivante
Me("txtQteMachine" & (i + 1)).Value = CLng(Me("txtQteMachine" & (i + 1)).Value) + 1
Else ' Sinon
' on incrémente le nombre total de pièces fabriquées
Me.txtQteFab.Value = Nz(Me.txtQteFab.Value, 0) + 1
MajIndicateursAvancement ' Mise à jour des indicateurs d'avancement
End If
Else ' Sinon
' on incrémente le nombre total de pièces fabriquées
Me.txtQteFab.Value = Nz(Me.txtQteFab.Value, 0) + 1
MajIndicateursAvancement ' Mise à jour des indicateurs d'avancement
End If
End If

End If

i = i + 1 ' on passe à l'indice suivant

If Nz(Me("txtIdMachine" & i).Value) = "" Then ' Si plus d'indice de machine on sort
Exit Do
End If

Loop

If ar Then ' Processus arrêté
Me.txtEtatProcess.Value = "Arrêt" ' Mise à jour de l'état du process
Me.TimerInterval = 0 ' Interruption du timer
End If

err_Form_Timer:

If Err.Number <> 0 Then ' Gestion d'erreur
MsgBox (Err.Description) ' Affichage du message d'erreur
End If

End Sub

VIII - Téléchargement

Pour faciliter les tests et assurer leur portabilité, les bases exemples n'utiliseront pas de composants externes à Access (ActiveX ou autres).

Les bases exemples sont au format mdb et accdb.

IX - Remerciements

Un grand Merci à toute l'équipe de Dvp et plus particulièrement à :

Pour leurs remarques et conseils avisés :
. arkham46
. Laurent Ott

Pour sa relecture :
. f-leb