Interfaçage OpenFlyers et armoire à clé : Différence entre versions

Aller à : navigation, rechercher
(Exemple 2 de script en Python permettant de modifier l'état des clés (commande notify))
(Exemple 2 de script en Python permettant de modifier l'état des clés (commande notify))
Ligne 252 : Ligne 252 :
 
Nécessite la librairie Twisted.
 
Nécessite la librairie Twisted.
  
<python>
+
<python># load librairy
# load librairy
+
 
from twisted.web.xmlrpc import Proxy
 
from twisted.web.xmlrpc import Proxy
 
from twisted.internet import reactor
 
from twisted.internet import reactor

Version du 10 mars 2012 à 21:56

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 :

Menu contextuel ouverture de vol.png


Il se retrouve sur le formulaire de saisie du vol :

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é :

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é :

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 cliquer 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 :
    • 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é.

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é

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

  • Le serveur OpenFlyers vérifie alors :
    • Si l'utilisateur qui a passé la demande est administrateur, il libère la clé sans condition (cela permet de libérer la clé directement depuis l'interface de gestion des aéronefs)
    • Si l'utilisateur qui a passé la demande n'est pas administrateur, 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 où l'autorisation de libération de clé est donnée par le serveur
  • 0 dans le cas contraire

Modification de l'état d'une clé

  • Le logiciel de pilotage doit envoyer un ordre notify avec comme paramètre un tableau dont les clés sont les numéros de clés et les valeurs leur position (0 = absente de l'armoire, 1 = présente dans l'armoire)

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

#!/usr/bin/python
import xmlrpclib, time, dummy_proto, hashlib
from twisted.internet import reactor, task, threads
from twisted.application import internet, service
from twisted.internet.protocol import Protocol, ClientCreator, ReconnectingClientFactory
from twisted.web import resource, server
 
# HTTP Port from which the OF client contact OpenKeys service
SERVICE_PORT=4080 
SERVICE_HOST="10.0.2.15"
 
KEYS_ADDR='192.168.23.118'
KEYS_PORT=6002
KEY_RELEASE_DURATION=15
 
# sha224 passwords
PASSWORDS=('847bed9bc354e7e47bc5350a3b3aaf6124f5748224a3c7ad79586c3c')
 
OF_XMLRPC_URL="http://structure.openflyers.tld/openkeys.rpc.php5"
 
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())
		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)

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

Nécessite la librairie Twisted.

# load librairy
from twisted.web.xmlrpc import Proxy
from twisted.internet import reactor
 
def printValue(value):
    print repr(value)
    reactor.stop()
 
def printError(error):
    print 'error', error
    reactor.stop()
 
# URL of the XML-RPC server
proxy = Proxy('http://yourURL.xx/openkeys.rpc.php5')
 
# init array to send
status = []
state = 0
# Alternate 0 and 1 for test
for i in range(0,10):
        status.append(state)
        state = 0 if state == 1 else 1
#print status
 
# send to the XML-RPC server
proxy.callRemote('notify', status).addCallbacks(printValue, printError)
reactor.run()
 

Maquette de script Openkeys.rpc.php5 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
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();
 
?>