|
|
|
Cas Alarme - Mise en oeuvre avec DelphiA/ Présentation de l'application1. FonctionnalitésCette application a pour but de gérer les "post-it virtuels" de l'ensemble des collaborateurs d'une entreprise possédant un réseau d'ordinateurs. A titre d'exemple :
L'application doit en conséquence :
2. Interface utilisateurL'écran ci-dessous permet de s'identifier et de saisir de nouvelles alarmes.
Un simple message d'alerte suffira lors du déclenchement d'une alarme.
3. Architecture techniqueCette application peut être développée de multiples manières. En voici trois, à titre d'exemple... Solution 1
Solution 2
Solution 3
Etant donné le titre de ce document, la solution 3 s'impose... Etant donné le sous-titre, Dcom s'impose... Nous aurons donc le schéma suivant :
Il faut bien prendre conscience des différences entre les trois solutions et des avantages de la dernière. Considérons un scénario d'évolution possible de cette application : - Etape 1 : développement de l'application cliente et d'une première version de l'application serveur. Cette version mémorise toutes les informations en mémoire centrale (elle est localisée sur un serveur qui n'est jamais arrêté). Dès cette première étape, l'ensemble est fonctionnel... - Etape 2 : craignant de devoir subir une panne de courant accompagnée d'une panne d'onduleur, le responsable informatique demande la mise des alarmes sur disque. On ne modifie absolument pas l'application cliente. Seul le programme serveur est remanié pour mémoriser ses informations sur disque à l'aide de fichiers. - Etape 3 : le responsable du personnel souhaite pouvoir interroger simplement l'ensemble des alarmes stockées (même si cela se discute...). La solution adoptée est de modifier l'application serveur pour mémoriser les données à l'aide d'Access dont le QBE est facile à utiliser. Encore une fois, l'application cliente n'est pas modifiée. C'est la première apparition du troisième niveau... - Etape 4 : l'entreprise fait l'acquisition du SGBDR Oracle et décide d'y transférer l'ensemble des bases de données existantes. La notre n'échappe pas à la règle... Seul le niveau "données" est concerné par cette évolution. Le programme serveur et le programme client ne sont pas modifiés. Si vous avez du temps, n'hésitez pas à refaire l'ensemble du parcours... et même imaginer des étapes supplémentaires. 4. Phases de développement prévuesDans le cas contraire, je vous propose de développer directement la dernière version. Nous pratiquerons en trois phases successives : Phase 1: les utilisateurs
L'identification sera très simpliste : choix dans une liste déroulante. Phase 2: saisie des alarmes
Phase 3 : exploitation des alarmes
Remarques : - Le SGBD utilisé importe peu. - L'outil de développement utilisé est Delphi Professionnel version 6. B/ Les utilisateurs1. Base de donnéesa. Comment s'y prendreLes utilisateurs seront mémorisés dans la table UTILISATEUR ( idUtilisateur, nomUtilisateur) Etant donnée la rareté des mises à jour, les informations seront directement saisies au niveau du SGBD. b. A vous de jouer
2. Programme serveura. Approche algorithmique> Principe Le composant serveur est formé d'une interface et d'une classe implémentant cette interface :
Le programme client fait appel aux services du composant en manipulant un objet de type CoGestAlarme via l'interface IgestAlarme. > Interface IgesAlarme Il faut fournir un accès à la liste des utilisateurs recensés dans la base de données (ceci permettra notamment aux programmes clients d'afficher la liste déroulante prévue dans l'interface utilisateur). Il nous faut donc concevoir un mécanisme permettant d'obtenir l'ensemble des utilisateurs. Voici une possibilité (ce n'est pas la seule) :
Ces deux méthodes permettent effectivement aux programmes clients de parcourir la liste des utilisateurs, elles constituent un itérateur. L'interface IgestAlarme peut donc être décrite par : Interface IgestAlarme procédure premierUtilisateur(var p_id : entier, var p_nom : chaine) procédure utilisateurSuivant(var p_id : entier, var p_nom : chaine) Fin interface > Classe CoGestAlarme Il s'agit d'implémenter l'interface IgestAlarme. Le principe est simple : accéder à la base de données pour réaliser les deux méthodes prévues. Classe CoGestAlarme (implémente IgestAlarme) lesUtilisateurs : jeu d'enregistrements procédure init début se connecter à la base de données établir le lien entre lesUtilisateurs et la table utilisateurs fin procédure libérer début se déconnecter de la base de données fin procedure premierUtilisateur(var p_id: interger;var p_nom: chaine) début lesUtilisateurs.premier si non (lesUtilisateurs.fin) alors p_id ß lesUtilisateurs.valeur("idUtilisateur") p_nom ß lesUtilisateurs.valeur("nomUtilisateur") sinon p_id ß 0 p_nom ß "" fin de si fin procedure utilisateurSuivant (var p_id: interger;var p_nom: chaine) début lesUtilisateurs.suivant si non (lesUtilisateurs.fin) alors p_id ß lesUtilisateurs.valeur("idUtilisateur") p_nom ß lesUtilisateurs.valeur("nomUtilisateur") sinon p_id ß 0 p_nom ß "" fin de si fin Fin Classe b. Mise en ouvre avec Delphi> Création de l'application et du composant serveur (Interface IgestAlarme et CoClasse CogestAlarme) Revoir si nécessaire le document "initiation aux objets distribués" : Delphi doit créer une classe TgestAlarme sur laquelle nous travaillerons effectivement. > Accès aux données Les objets de la CoClasse doivent pouvoir accéder aux données de la base. Il faut pour cela créer un module de données comportant un composant TDataBase et un composant TTable. Cette démarche est classique... Appelons ce module "dm" : Delphi crée un type Tdm et une variable dm de type Tdm. Mais attention : chaque objet devra posséder son propre accès à la base ! Il faut donc ajouter un membre privé de type Tdm dans la classe TgestAlarme : appelons-le "dmPrive". > Problème des sessions Chaque composant orienté données existe et travaille à l'intérieur d'une "Tsession". Une session correspond à une connexion de l'application à une base de données (le concept est sensiblement le même que celui d'une session utilisateur lors d'une connexion interactive, mis à part le fait que l'identification n'est pas du ressort de la session, mais du composant database). Une session par défaut est définie automatiquement et chaque composant lui est rattachée. Cette session se nomme tout simplement "session". Il existe des cas où l'on ne peut pas se contenter d'une seule session. En effet, il est impossible d'effectuer deux accès simultanés à la base à l'intérieur de la même session. Considérons notre application : si deux clients se connectent en même temps, il y aura création de deux objets de type TgestAlarme, donc de deux Tdm "dmPrive", un pour chacun d'entre eux. Supposons que les deux clients demandent en même temps la liste des utilisateurs pour l'afficher, il y aura deux accès simultanés à la base : il nous faut donc deux sessions. Pour les obtenir, rien n'est plus simple : il suffit d'ajouter un composant Tsession sur notre module de données et d'y lier les autres composants. Chaque objet possédant son propre Tdm (dmPrive), il possédera sa propre session. Il reste un problème à régler : à l'intérieur d'une même application, il est impossible de gérer deux sessions portant le même nom. Comment appeler notre composant Tsession ? Une solution serait de lui attribuer un nom à la création du Tdm dmPrive (donc à la création de l'objet). En mémorisant le dernier numéro de session créée, on pourrait imaginer quelque chose comme : numSession ß numSession+1 nom ß "session" + IntToStr(numSession) Cette opération de "nommage automatique" est un peu complexe, il faudrait encore lier les composants du Tdm dmPrive à la session, cette liaison se faisant à l'aide du nom... Delphi vient à notre secours, cette opération peut être automatisée : il suffit d'attribuer la valeur "true" à la propriété "autoSessionName" du composant Tsession et le tour est joué :
> Construction et destruction des objets Chaque objet du type TgestAlarme représente la connexion d'un client et possède une propriété dmPrive de type Tdm. Cette propriété est de type classe et doit donc :
// fm_servAlarme est le nom de la fiche principale de l'application serveur, cette fiche sera donc propriétaire de tous les modules de données créés lors de l'exécution.
Classiquement, ces opérations se font en redéfinissant le constructeur et le destructeur de la classe de base de TgestAlarme (directive override). Or ici il est impossible de redéfinir le constructeur car la classe de base de toute classe d'objets COM (TComObject) possède un constructeur Create non virtuel. Bien sûr, ce besoin d'initialisation des objets COM a été prévu, le constructeur de TComObject appelle systématiquement la méthode Initialize, virtuelle celle-là. Initialize ne fait rien... mais ne demande qu'à être redéfinie. Il n'y a par contre aucun problème avec le destructeur Destroy, il peut être redéfini. c. A vous de jouer
3. Programme clienta. Approche algorithmique
Il faut d'une part remplir la liste déroulante avec les informations fournies par l'objet alarme et mémoriser l'utilisateur sélectionné. Il est évident que l'on a intérêt à mémoriser l'identificateur de l'utilisateur sélectionné et non l'ensemble des informations. > variables utilisées alarme : IgestAlarme // l'objet distant cb_utilisateur : comboBox // la liste déroulante idUtilisateurConnecte : entier // le numéro de l'utilisateur connecté > Au lancement de l'application Il faut effectuer le traitement suivant :
alarme ß CoGestAlarme.CreateRemote('192.168.210.2') alarme.premierUtilisateur(id,nom); tant que (id<>0) faire cb_utilisateur.ajouter(id, nom); alarme.utilisateurSuivant(id,nom); fin tant que > Lors du choix dans la liste déroulante idUtilisateurConnecte ß cb_utilisateur.valeur("id") b. Mise en ouvre avec Delphi> Déclenchement des opérations
> L'application La création de l'application ne pose pas de problème particulier. L'utilisation du composant serveur est décrite dans le document "initiation aux objets distribués". c. A vous de jouer
C/ Les alarmes1. Base de donnéesa. Comment s'y prendreVoici le schéma de la table à utiliser : ALARME ( idAlarme, momentAlarme, libelleAlarme, utilisateur#) L'attribut momentAlarme est de type DateHeure. b. A vous de jouerLa seule difficulté est à venir : le type DateHeure du SGBD utilisé sera-t-il compatible avec le type correspondant dans l'outil de développement utilisé (TDateTime pour Delphi) ? Ce problème est récurrent, l'absence de normalisation est un frein important au développement des architectures distribuées. 2. Programme serveura. Approche algorithmiqueCette seconde phase est beaucoup plus simple que la première :
b. A vous de jouer
3. Programme clienta. Approche algorithmique
Lorsque l'utilisateur clique sur le bouton "Ajouter l'alarme", le programme client transmet les informations saisies à l'objet distant alarme en appelant sa méthode setAlarme. b. Mise en ouvre avec DelphiLes zones de saisie date et heure sont des composants TDateTimePicker. Ce composant est paramétrable et peut donc représenter une date (TDate) ou une heure (TTime). Pour obtenir une valeur de type DateHeure (TDateTime), il suffit en fait d'ajouter une valeur de type date et une valeur de type heure. En effet, une valeur de type TDateTime est un réel dont la partie entière représente la date et la partie décimale représente l'heure. c. A vous de jouer
D/ Le réveil sonne1. Base de donnéesa. Comment s'y prendreIl n'y a rien à faire !!! b. A vous de jouerAucun problème si vous avez lu le paragraphe précédent... 2. Programme serveura. Approche algorithmiqueIl faut offrir aux clients la possibilité de récupérer sur demande la (ou les) alarme(s) éventuelle(s) à déclencher en fonction de l'heure système du serveur. Le fonctionnement pourrait être le suivant :
b. A vous de jouer
3. Programme clienta. Approche algorithmiqueLe programme client doit interroger régulièrement le serveur. A chaque interrogation, le traitement consiste à récupérer les éventuelles alarmes : demander la première alarme à l'objet distant tant qu'il y a une alarme faire afficher le libellé de l'alarme à l'utilisateur demander sa suppression à l'objet distant demander l'alarme suivante à l'objet distant fin tant que b. A vous de jouer
Pour aller plus loin...Il est possible d'envisager plusieurs évolutions de cette application (liste non exhaustive !) :
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
|
_____________________________________________
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||