Interfaçage OpenFlyers et armoire à clés: Difference between revisions

From Documentation de la solution web de gestion OpenFlyers
Jump to navigation Jump to search
imported>Jcheng
imported>Claratte
Line 452: Line 452:


?></php>
?></php>
==Création d'une nouvelle commande closeFlight==
Il est possible d'implémenter de nouvelle commande : On désire par exemple créer une commande '''closeFlight''' qui prendre l'id d'une ressource en paramètre et entraîne la fermeture du vol effectué sur cette ressource si ce vol avait été ouvert. La commande retournera un entier pour déterminer si la fermeture du vol s'est bien réalisée ou non.
Pour permettre cette implémentation, on va éditer la [[#Maquette_de_script_actionOnDemand.php_c.C3.B4t.C3.A9_serveur_recevant_les_appels_du_logiciel_de_pilotage_de_l.27armoire|maquette de script actionOnDemand.php]] et rajouter la nouvelle commande :
<php><?php
include 'XML/RPC2/Server.php';
class OpenKeysGateway {
    /**
    * closeFlight
    *
    * Close opened flight from a resource
    *
    * @param integer $resourceId
    * @return integer 1: OK, 0: OK
    */
    public static function closeFlight($resourceId) {
        $closeDone = 0;
        // Insert your own code here
        $flightId = Flight::getOpenedFlightForAircraft($resourceId);
        if ( $flightId != null ) {
            Flight::close($flightId);
            $closeDone = 1;
        }
        // Or insert your own code here
        return $closeDone;
    }
    /**
    * Update the status of the keys
    *
    * @param array $status Status of keys
    * @return integer 1 if ok, 0 else
    */
    public static function notify($status) {
        // Insert your own code
    }
    /**
    * Check if user is able to release the key 'key_num'
    *
    * @param string $sessid PHPSESSID of a connected user
    * @param integer $key_num number of the key to release
    * @return integer 0:NOK, 1:OK
    */
    public static function checkCommand($sessid, $key_num) {
        // Insert your own code
    }
}
$options = array(
    'autoDocument' => true,
);
// dirty hack to get things work !
$GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents('php://input');
$server = XML_RPC2_Server::create('OpenKeysGateway', $options);
$server->handleCall();
?></php>
Ensuite pour tester la commande, on va passer par ce script Python :
<python># load library
from twisted.web.xmlrpc import Proxy
from twisted.internet import reactor, ssl
import random
def printValue(value):
    print repr(value)
    reactor.stop()
def printError(error):
    print 'error', error
    reactor.stop()
# URL of the XML-RPC server
proxy = Proxy('https://yourURL.xx/actionOnDemand.php')
$resource_id = 1
# send to the XML-RPC server
proxy.callRemote('closeFlight', $resource_id).addCallbacks(printValue, printError)
reactor.run()</python>
Cela affichera 1 en cas de fermeture de vol ou 0 si ça ne s'est pas réalisé.

Revision as of 12:48, 21 July 2012

Présentation

L'objet de cette page est de décrire le protocole d'interfaçage avec toute solution d'armoire à clé.

Le protocole repose sur le principe que l'interfaçage est piloté par un logiciel installé sur un PC ou une solution embarquée et qui dispose d'un accès à internet lui permettant de communiquer avec OpenFlyers.

Interface utilisateur OpenFlyers

L'utilisateur ouvre un vol dans OpenFlyers :

Fichier:Menu-contextuel-ouverture-de-vol.png


Il se retrouve sur le formulaire de saisie du vol :

Fichier:Formulaire-ouverture-de-vol.png


Lorsqu'il clique sur le bouton de validation, 3 cas peuvent se présenter :

  • Une alerte ou plusieurs alertes rouges (bloquantes), s'affichent : l'utilisateur n'a pas la possibilité de surpasser ses alertes, il ne peut alors récupérer la clé :

Fichier:Alertes-bloquantes-en-ouverture-vol.png

  • Une alerte ou plusieurs alertes oranges (non-bloquantes), s'affichent : l'utilisateur a la possibilité de surpasser ses alertes, et ainsi de récupérer la clé :

Fichier:Alertes-non-bloquantes-en-ouverture-vol.png

  • Aucune alerte ne s'affiche, l'utilisateur se retrouve alors directement sur la page lui indiquant :

Fichier:Message autorisation libération clé.png

Une fois qu'il a cliqué sur LIBERER LA CLE, il dispose de X secondes. Cette durée est paramétrable par OpenFlyers dans le cas du protocole piloté par OpenKeys.exe sur Windows ou est programmé en dur dans PyOpenKeys sous Linux.

  • Lorsqu'il rentre de vol, l'utilisateur n'a plus qu'a retourner sur le formulaire de saisie de vol et à fermer son vol. Après la fermeture, il lui est demandé de remettre sa clé sur l'armoire s'il ne l'a pas fait avant.

Interface administrateur OpenFlyers

Activation et configuration de la gestion des armoires à clé

  • Admin
  • Configuration > Paramétrage
  • Dans le formulaire Gestion des vols, sélectionner Activé(e) pour le champ Gestion armoire à clé
  • Cliquer sur le bouton Valider lié au formulaire
  • Le formulaire apparait alors comme suit :

Fichier:Paramétrage armoire à clé.png

  • Signification des paramètres :
    • Gestion des clés : Gestion des clés de l'armoire ou non
    • Nombre de clé : nombre de clés possibles dans l'armoire
    • Temporisation : temps en secondes pendant lequel la clé peut être retirée après autorisation (la configuration de la temporisation n'est effective qu'avec le logiciel de pilotage OpenKey sous Windows).
    • IP du PC avec le driver du tableau de clés : adresse IP du PC sur lequel se trouve le logiciel de pilotage de l'armoire à clé.
    • Port du driver du tableau de clés : port sur lequel écoute le logiciel de pilotage de l'armoire à clé.
    • Type de serveur : PyOpenKey (sous Linux) ou OpenKey (sous Windows). Il s'agit du nom du logiciel de pilotage de l'armoire à clé.
    • Fermeture automatique des vols au retour de la clé : permet de programmer la fermeture automatique des vols au retour de la clé.
    • Phrase mot de passe : phrase permettant d'authentifier la source de la transmission dans le cas d'une requête XML-RPC notify.

Attention : dans le cas où le logiciel de pilotage se trouve sur le même PC que pour la saisie des vols dans OpenFlyers, il peut y avoir des problèmes de loopback au niveau du routeur.

Attribution des clés aux ressources/aéronefs

  • Admin
  • Ressources > Ressources > Actives ou Flotte > Aéronefs > Aéronefs actifs sous version OF 2.1

Fichier:Liste des aéronefs.png

Pour chaque aéronef concerné, il suffit d'attribuer un numéro et un nom de clé dans les champs correspondants. La validation de la saisie se fait automatiquement en cliquant hors du champ de saisie.

Protocole de dialogue XML-RPC entre OpenFlyers et le logiciel de gestion de l'armoire à clé

Le dialogue avec le serveur XML-RPC d'OpenFlyers doit s'effectuer en HTTPS.

Demande de libération d'une clé

  • Lorsque qu'une clé doit être libérée, le navigateur envoie un message au logiciel de pilotage par le protocole HTTP sous la forme suivante :

http://127.0.0.1:4080/?sessid=e5f01p2oqh2vb36arisr8k5j87&key=1&resource=2&person=12

  • L'adresse IP (ici 127.0.0.1) et le port (ici 4080) sont fonction de la configuration de l'armoire à clé dans OpenFlyers.
  • sessid contient le numéro de session en cours
  • key contient le numéro de la clé concernée
  • resource contient le numéro de la ressource concernée
  • person contient le numéro de l'utilisateur qui fait la demande
  • Le logiciel de pilotage doit alors envoyer une demande d'ordre au serveur OpenFlyers à l'adresse suivante https://structure.openflyers.tld/actionOnDemand.php où il faut remplacer structure.openflyers.tld par l'adresse de la plateforme OpenFlyers concernée. avec comme commande XML_RPC checkCommand ("e5f01p2oqh2vb36arisr8k5j87",int(key)).
  • Le serveur OpenFlyers vérifie alors :
    • Si l'utilisateur qui a passé la demande a le droit Gestion des clés (administrateur), il libère la clé sans condition (cela permet de libérer la clé sans contrôle)
    • Si l'utilisateur qui a passé la demande n'a le droit Gestion des clés (pilote), alors il vérifie s'il existe un vol ouvert qui remplit la double condition :
      • Vol attribué à l'utilisateur
      • Aéronef du vol associé à la demande de libération de la clé
  • Le serveur retourne ensuite la réponse :
  • 1 dans le cas d'autorisation de libération de clé
  • 0 dans le cas contraire

Modification de l'état d'une clé

  • Le logiciel de pilotage doit envoyer un ordre notify([1,0,1,1,1,1,0,0], "passphrase") avec comme paramètre un tableau chronologique des clés ayant pour valeur leur état (1 = interrupteur de présence de clé fermé = clé absente de l'armoire, 0 = interrupteur de présence de clé ouvert = clé présente dans l'armoire)

Exemple 1 de script en Python de logiciel de pilotage d'une armoire

<python>#!/usr/bin/python import xmlrpclib, time, dummy_proto, hashlib from twisted.internet import reactor, task, threads, ssl from twisted.application import internet, service from twisted.internet.protocol import Protocol, ClientCreator, ReconnectingClientFactory from twisted.web import resource, server

  1. HTTP Port from which the OF client contact OpenKeys service

SERVICE_PORT=4080 SERVICE_HOST="127.0.0.1"

KEYS_ADDR='192.168.23.118' KEYS_PORT=6002 KEY_RELEASE_DURATION=15

  1. sha224 passwords

PASSWORDS=('847bed9bc354e7e47bc5350a3b3aaf6124f5748224a3c7ad79586c3c')

  1. init passphrase

passphrase = 'SDjklsdiuQSIPO23879ZERK2098ZL2908DFZLK'

OF_XMLRPC_URL="https://structure.openflyers.tld/actionOnDemand.php"

DEBUG=False

class dummyProtocol(Protocol): def __init__(self, rpc_server): self.rpc_server=rpc_server self.lc = None

def connectionMade(self): if not self.lc: # update status every 10 minutes self.lc = task.LoopingCall(self.updateStatus) self.lc.start(600)

def dataReceived(self, data): msg=dummy_proto.dummy_message.newFromData(data) if DEBUG: msg.display() try: if type(msg)==dummyproto.dummy_ONOFF_Control_Response: response = self.rpc_server.notify(msg.getKeysStatus(),passphrase) except Exception, err: if DEBUG: print "error: ", err pass # ignore

def updateStatus(self): msg = dummy_proto.dummy_State_Request() self.transport.write(msg.build_message())

def send(self, dummy_data): self.transport.write(dummy_data.build_message())

class dummyClientFactory(ReconnectingClientFactory): def __init__(self, rpc_server): self.rpc_server = rpc_server

def buildProtocol(self, addr): self.resetDelay() self.protocol = dummyProtocol(self.rpc_server) return self.protocol

def clientConnectionLost(self, connector, reason): ReconnectingClientFactory.clientConnectionLost(self, connector, reason) connector.connect()

def clientConnectionFailed(self, connector, reason): if DEBUG: print 'Connection failed. Reason:', reason ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)

# blocking method ! must be run in a new thread def release_key(self, key_num): m = dummy_proto.dummy_ONOFF_Control() m.setON(key_num) self.protocol.send(m) time.sleep(KEY_RELEASE_DURATION) m.setOFF(key_num) self.protocol.send(m) return key_num

class WebResource(resource.Resource): def __init__(self, rpc_server, dummy_client_factory): self.rpc_server = rpc_server self.dummy_client_factory = dummy_client_factory resource.Resource.__init__(self) self.keys_webcontrol_state = [0,0,0,0,0,0,0,0,0,0]

def getChild(self, name, request): return self

def render_GET(self, request): reponse = 0 # NOK par defaut key_num = 0 if len(request.args) == 0: # no GET args. # return javascript code return """ var handleSuccess = function(o){ if(o.responseText !== undefined){ /* TODO : regarder si on a OK ou NOK */

   }

} var handleFailure = function(o){

   if(o.responseText !== undefined){

alert("Erreur lors de la liberation de la clef : "+o.status+" -- "+o.statusText);

   }

} var callback = {

 success:handleSuccess,
 failure: handleFailure,
 argument: {}

};

function releasekey(sessid, keynum) { YAHOO.util.Connect.asyncRequest('GET', "http://%s:%i/?sessid="+sessid+"&key="+keynum, callback); document.getElementById('keybutton').enabled=false; setTimeout ("document.getElementById('keybutton').enabled=true;", 10000); } """ % (SERVICE_HOST,SERVICE_PORT) try: sessid = request.args.get('sessid', [""])[0] password = request.args.get('password', [""])[0] key_num = int(request.args.get('key', ["0"])[0]) response = 0 if password: if hashlib.sha224(password).hexdigest() in PASSWORDS: response = 1 else: response = self.rpc_server.checkCommand(sessid, key_num) except Exception, err: if DEBUG: print "error: ", err return "NOK:bad request"

if response == 1: # Don't try to release a key that is being released if self.keys_webcontrol_state[key_num-1]: return "OK:already released" self.keys_webcontrol_state[key_num-1] = 1 d = threads.deferToThread(self.dummy_client_factory.release_key, key_num) d.addCallback(self.unset_webcontrol_state) return "OK:releasing key..." else: return "NOK:permission refused"

def unset_webcontrol_state(self, key_num): self.keys_webcontrol_state[key_num-1] = 0

class OpenKeysService(service.Service): def __init__(self): rpc_server=xmlrpclib.Server(OF_XMLRPC_URL); self.dummy_client_factory = dummyClientFactory(rpc_server) self.web_resource = WebResource(rpc_server, self.dummy_client_factory)

def getdummyClientFactory(self): return self.dummy_client_factory

def getWebResource(self): return self.web_resource

application = service.Application('openkeys') f = OpenKeysService() serviceCollection = service.IServiceCollection(application) internet.TCPClient(KEYS_ADDR, KEYS_PORT, f.getdummyClientFactory() ).setServiceParent(serviceCollection) internet.TCPServer(SERVICE_PORT, server.Site(f.getWebResource()) ).setServiceParent(serviceCollection)</python>

Exemple 2 de script en Python permettant de modifier l'état des clés (commande notify)

Nécessite les librairies Twisted et pyOpenSSL

<python># load library from twisted.web.xmlrpc import Proxy from twisted.internet import reactor, ssl import random

def printValue(value):

   print repr(value)
   reactor.stop()

def printError(error):

   print 'error', error
   reactor.stop()
  1. URL of the XML-RPC server

proxy = Proxy('https://yourURL.xx/actionOnDemand.php')

  1. init passphrase

passphrase = 'SDjklsdiuQSIPO23879ZERK2098ZL2908DFZLK'

  1. init array to send

status = [] state = random.randint(0,1)

  1. Alternate 0 and 1 for test

for i in range(0,10):

       status.append(state)
       state = 0 if state == 1 else 1
  1. print status
  1. send to the XML-RPC server

proxy.callRemote('notify', status, passphrase).addCallbacks(printValue, printError) reactor.run()</python>

Ces scripts nécessitent les librairies Twisted et pyOpenSSL.

Ils recevront (et afficheront) comme réponse 0 du fait que le numéro de session transmis n'est pas correc (et laissé à une chaine vide).

Exemple 3 de script en Python permettant de vérifier une demande de libération de clé (commande checkCommand)

<python># load library from twisted.web.xmlrpc import Proxy from twisted.internet import reactor, ssl

def printValue(value):

   print repr(value)
   reactor.stop()

def printError(error):

   print 'error', error
   reactor.stop()

  1. URL of the XML-RPC server

proxy = Proxy('https://yourURL.xx/actionOnDemand.php')

  1. init array to send

sessid = "" key_num = 1

  1. send to the XML-RPC server

proxy.callRemote('checkCommand', sessid, key_num).addCallbacks(printValue, printError) reactor.run()</python>

Ce test peut être complété (pour obtenir une réponse "1") en simulant une armoire à clé à l'aide d'un script PHP de la façon suivante :

  • Installer un serveur local (par exemple wampserver sous Windows) de façon à avoir le port 127.0.0.1 qui lui est attribué
  • Configurer la plateforme OpenFlyers de manière à gérer l'armoire à clé avec les éléments suivants :
    • IP du PC avec le driver du tableau de clés : 127.0.0.1
    • Port du driver du tableau de clés : 80
  • A la racine du répertoire www, mettre le script index.php suivant (on suppose que ce script est appelé par défaut lorsqu'on utilise l'URL http://127.0.0.1 ) :

<php><?php file_put_contents('test.txt', 'TEST', FILE_APPEND ); file_put_contents('test.txt', print_r($_REQUEST, true), FILE_APPEND ); ?></php>

  • Toujours à la racine du répertoire www, créer un fichier test.txt

Le script PHP écrira dans le fichier test.txt les variables transmises lors de la demande d'ouverture de l'armoire à clé :

TESTArray
(
    [sessid] => j2eo92215nbef09borb74jftm1
    [key] => 1
    [resource] => 1
    [person] => 1
)
  • Il suffit alors de recopier la valeur de sessid et de key dans le script python de ce début de paragraphe et de le tester : il devrait renvoyer 1.
  • Configurer la gestion de l'armoire à clé de la façon suivante

Sans gestion des clés

<python># load library from twisted.web.xmlrpc import Proxy from twisted.internet import reactor, ssl

def printValue(value):

   print repr(value)
   reactor.stop()

def printError(error):

   print 'error', error
   reactor.stop()

  1. URL of the XML-RPC server

proxy = Proxy('https://yourURL.xx/actionOnDemand.php')

  1. init array to send

sessid = ""

  1. send to the XML-RPC server

proxy.callRemote('checkCommand', sessid).addCallbacks(printValue, printError) reactor.run()</python>

Ce test peut être complété (pour obtenir une réponse "1") en simulant une armoire à clé à l'aide d'un script PHP de la façon suivante :

  • Installer un serveur local (par exemple wampserver sous Windows) de façon à avoir le port 127.0.0.1 qui lui est attribué
  • Configurer la plateforme OpenFlyers de manière à gérer l'armoire à clé avec les éléments suivants :
    • IP du PC avec le driver du tableau de clés : 127.0.0.1
    • Port du driver du tableau de clés : 80
  • A la racine du répertoire www, mettre le script index.php suivant (on suppose que ce script est appelé par défaut lorsqu'on utilise l'URL http://127.0.0.1 ) :

<php><?php file_put_contents('test.txt', 'TEST', FILE_APPEND ); file_put_contents('test.txt', print_r($_REQUEST, true), FILE_APPEND ); ?></php>

  • Toujours à la racine du répertoire www, créer un fichier test.txt

Le script PHP écrira dans le fichier test.txt les variables transmises lors de la demande d'ouverture de l'armoire à clé :

TESTArray
(
    [sessid] => j2eo92215nbef09borb74jftm1
    [resource] => 1
    [person] => 1
)
  • Il suffit alors de recopier la valeur de sessid dans le script python de ce début de paragraphe et de le tester : il devrait renvoyer 1.

Maquette de script actionOnDemand.php côté serveur recevant les appels du logiciel de pilotage de l'armoire

Ce script nécessite la bibliothèque PEAR XML_RPC2.

Pour les tests, il suffit de modifier la valeur de la variable $weDontWant.

<php><?php include 'XML/RPC2/Server.php';

class OpenKeysGateway {

   /**
    * Update the status of the keys
    *
    * @param array $status Status of keys
    * @return integer 1 if ok, 0 else
    */
   public static function notify($status) {
       $logmsg = "";
       foreach ($status as $key_num_from_zero => $key_pres) {
           if (!is_numeric($key_pres) || intval($key_pres)!=$key_pres || $key_pres < 0 || $key_pres > 1) continue;
           if (!is_numeric($key_num_from_zero) || intval($key_num_from_zero)!=$key_num_from_zero 
               || $key_num_from_zero < 0 || $key_num_from_zero > 9) continue;
           $logmsg .= "".($key_num_from_zero+1).":".$key_pres.",";
       }
       file_put_contents('test.txt', "key notify :".$logmsg, FILE_APPEND );
       return 1;
   }
   /**
    * Check if user is able to release the key 'key_num'
    *
    * @param string $sessid PHPSESSID of a connected user
    * @param integer $key_num number of the key to release
    * @return integer 0:NOK, 1:OK
    */
   public static function checkCommand($sessid, $key_num) {
       /* sanitize input */
       if (!is_numeric($key_num) || intval($key_num)!=$key_num || $key_num < 1 || $key_num > 10) {
           return 0;
       }
       $weDontWant = 1;
       if ($weDontWant) {
           file_put_contents('test.txt', "$key_num has no associated airborne aircraft", FILE_APPEND );
           return 0;
       }
       else {
           file_put_contents('test.txt', "granted key: ".$key_num.":permission granted", FILE_APPEND );
           return 1;
       }
   }

}

$options = array(

   'autoDocument' => true,

);

// dirty hack to get things work ! $GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents('php://input');

$server = XML_RPC2_Server::create('OpenKeysGateway', $options); $server->handleCall();

?></php>