En su día creamos 3 scripts para incrementar, decrementar y poner a cero el contador de turnos. Hoy empezamos añadiendo un script nuevo que nos permitirá dar un valor concreto, que entrará como parámetro, al marcador:
# cat pon_turno.sh
#!/bin/bash
num=$1
test "$num" == "" && num="00"
servidor="172.X.Y.Z"
topic="numero"
if num=$(printf "%02d" $num 2> /dev/null)
then #Si $num no es un numero, falla el comando anterior
mosquitto_pub -t $topic -h $servidor -m "A-$num" -r
fi
Esto será útil si hay cualquier fallo que descontrole el marcador y necesitemos ir hasta un número directamente.Para continuar, se me planteó la siguiente necesidad: controlar los turnos desde una aplicación normal y corriente como alternativa al método de la entrada anterior del blog (la cual usaba un ratón y triggerhappy como sistema para incrementar/decrementar el control del marcador de turnos). Podría escribir una aplicación de escritorio en Python y GTK, ya que con bash sería mas complicado, pero al final me he ido a lo mas sencillo y he optado por usar una aplicación web con Javascript+Mosquitto, usando la misma tecnología con que hice la pantalla que muestra el sistema de turnos del post anterior.
El aspecto de la aplicación será:
Al pulsar "Definir Turno" nos permitirán introducir un turno nuevo directamente:
Todo esto se implementa con una página HTML con mucho CSS y Javascript, ahí va:
# cat /var/www/turnos/GestionTurnos.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema de Gestión de Turnos</title>
<style type="text/css">
html, body {
margin: 0px;
height: 100%;
background-color: #000099;
}
@font-face{
font-family: 'Antonio';
src: url('Antonio-Regular.ttf') format('truetype');
}
#Pagina {
font-family: 'Antonio', Arial, sans-serif;
margin: 0px;
height: 100%;
}
#cabecera {
font-style: normal;
font-weight: bold;
background-color: #000099;
font-size:7vw;
width: 100%;
color: white;
text-align: center;
background-image: url(img/logo_guadalupe.png);
background-position: left center;
background-repeat: no-repeat;
}
#cuerpo {
color: yellow;
text-align: center;
background-color: black;
font-size:22vw;
font-weight: bold;
width: 100%;
height: 60%;
}
#pie {
font-size:7vw;
background-color: #000099;
font-weight: bold;
width: 100%;
height: 15%;
color: white;
font-style: normal;
text-align: center;
}
.botonAvanza {
-moz-box-shadow:inset 0px -3px 7px 0px #29bbff;
-webkit-box-shadow:inset 0px -3px 7px 0px #29bbff;
box-shadow:inset 0px -3px 7px 0px #29bbff;
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #2dabf9), color-stop(1, #0688fa));
background:-moz-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-webkit-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-o-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-ms-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:linear-gradient(to bottom, #2dabf9 5%, #0688fa 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2dabf9', endColorstr='#0688fa',GradientType=0);
background-color:#2dabf9;
-moz-border-radius:12px;
-webkit-border-radius:12px;
border-radius:12px;
border:1px solid #0b0e07;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:28px;
padding:22px 48px;
text-decoration:none;
text-shadow:0px 1px 0px #263666;
vertical-align:middle;
}
.botonAvanza:hover {
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #0688fa), color-stop(1, #2dabf9));
background:-moz-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-webkit-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-o-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-ms-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:linear-gradient(to bottom, #0688fa 5%, #2dabf9 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0688fa', endColorstr='#2dabf9',GradientType=0);
background-color:#0688fa;
}
.botonAvanza:active {
position:relative;
top:1px;
}
.botonRetrocede {
-moz-box-shadow:inset 0px -3px 7px 0px #29bbff;
-webkit-box-shadow:inset 0px -3px 7px 0px #29bbff;
box-shadow:inset 0px -3px 7px 0px #29bbff;
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #2dabf9), color-stop(1, #0688fa));
background:-moz-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-webkit-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-o-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-ms-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:linear-gradient(to bottom, #2dabf9 5%, #0688fa 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2dabf9', endColorstr='#0688fa',GradientType=0);
background-color:#2dabf9;
-moz-border-radius:12px;
-webkit-border-radius:12px;
border-radius:12px;
border:1px solid #0b0e07;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:28px;
padding:22px 48px;
text-decoration:none;
text-shadow:0px 1px 0px #263666;
vertical-align:middle;
}
.botonRetrocede:hover {
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #0688fa), color-stop(1, #2dabf9));
background:-moz-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-webkit-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-o-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-ms-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:linear-gradient(to bottom, #0688fa 5%, #2dabf9 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0688fa', endColorstr='#2dabf9',GradientType=0);
background-color:#0688fa;
}
.botonRetrocede:active {
position:relative;
top:1px;
}
.botonSet {
-moz-box-shadow:inset 0px -3px 7px 0px #29bbff;
-webkit-box-shadow:inset 0px -3px 7px 0px #29bbff;
box-shadow:inset 0px -3px 7px 0px #29bbff;
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #2dabf9), color-stop(1, #0688fa));
background:-moz-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-webkit-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-o-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-ms-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:linear-gradient(to bottom, #2dabf9 5%, #0688fa 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2dabf9', endColorstr='#0688fa',GradientType=0);
background-color:#2dabf9;
-moz-border-radius:12px;
-webkit-border-radius:12px;
border-radius:12px;
border:1px solid #0b0e07;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:28px;
padding:22px 48px;
text-decoration:none;
text-shadow:0px 1px 0px #263666;
vertical-align:middle;
}
.botonSet:hover {
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #0688fa), color-stop(1, #2dabf9));
background:-moz-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-webkit-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-o-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:-ms-linear-gradient(top, #0688fa 5%, #2dabf9 100%);
background:linear-gradient(to bottom, #0688fa 5%, #2dabf9 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0688fa', endColorstr='#2dabf9',GradientType=0);
background-color:#0688fa;
}
.botonSet:active {
position:relative;
top:1px;
}
.inputturno {
-moz-box-shadow:inset 0px -3px 7px 0px #29bbff;
-webkit-box-shadow:inset 0px -3px 7px 0px #29bbff;
box-shadow:inset 0px -3px 7px 0px #29bbff;
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #2dabf9), color-stop(1, #0688fa));
background:-moz-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-webkit-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-o-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:-ms-linear-gradient(top, #2dabf9 5%, #0688fa 100%);
background:linear-gradient(to bottom, #2dabf9 5%, #0688fa 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2dabf9', endColorstr='#0688fa',GradientType=0);
background-color:#2dabf9;
-moz-border-radius:12px;
-webkit-border-radius:12px;
border-radius:12px;
border:1px solid #0b0e07;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:28px;
padding:22px 48px;
text-decoration:none;
text-shadow:0px 1px 0px #263666;
vertical-align:middle;
width: 30px;
}
.labelturno {
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:28px;
padding:22px 2px;
text-decoration:none;
text-shadow:0px 1px 0px #263666;
vertical-align:middle;
}
</style>
<script src="http://tercero/turnos/mqttws31.js" type="text/javascript"></script>
<script src="http://tercero/turnos/jquery.min.js" type="text/javascript"></script>
<script src="http://tercero/turnos/config.js" type="text/javascript"></script>
<script type="text/javascript">
var valor;
var mqtt;
var reconnectTimeout = 2000;
function MQTTconnect() {
if (typeof path == "undefined") {
path = '/mqtt';
}
mqtt = new Paho.MQTT.Client(
host,
port,
path,
"web_" + parseInt(Math.random() * 100, 10)
);
var options = {
timeout: 3,
useSSL: useTLS,
cleanSession: cleansession,
onSuccess: onConnect,
// mqttVersion: 3,
onFailure: function (message) {
$('#status').val("Connection failed: " + message.errorMessage + "Retrying");
setTimeout(MQTTconnect, reconnectTimeout);
}
};
mqtt.onConnectionLost = onConnectionLost;
mqtt.onMessageArrived = onMessageArrived;
if (username != null) {
options.userName = username;
options.password = password;
}
console.log("Host="+ host + ", port=" + port + ", path=" + path + " TLS = " + useTLS + " username=" + username + " password=" + password);
mqtt.connect(options);
}
function onConnect() {
$('#status').val('Connected to ' + host + ':' + port + path);
// Connection succeeded; subscribe to our topic
mqtt.subscribe(topic, {qos: 0});
$('#topic').val(topic);
}
function onConnectionLost(response) {
setTimeout(MQTTconnect, reconnectTimeout);
$('#status').val("connection lost: " + responseObject.errorMessage + ". Reconnecting");
};
function onMessageArrived(message) {
var topic = message.destinationName;
var payload = message.payloadString;
valor=payload;
$('#cuerpo').html(payload);
};
$(document).ready(function() {
MQTTconnect();
var caja=$('#id_turno');
caja.hide();
var label=$('#id_label');
label.hide();
});
function subirTurno () {
var numero=valor.substring(2);
if (numero==99) numero=0;
else numero++;
topicMessage = new Paho.MQTT.Message("A-"+("00" + numero).slice (-2)); //Para añadir padZero a la izquierda del numero
topicMessage.destinationName = "numero";
topicMessage.retained = true;
mqtt.send(topicMessage);
}
function bajarTurno () {
var numero=valor.substring(2);
if (numero==0) numero=0;
else numero--;
topicMessage = new Paho.MQTT.Message("A-"+("00" + numero).slice (-2)); //Para añadir padZero a la izquierda del numero
topicMessage.destinationName = "numero";
topicMessage.retained = true;
mqtt.send(topicMessage);
}
function verCaja () {
var caja=$('#id_turno');
caja.show();
var numero=valor.substring(2);
caja.val(numero);
var label=$('#id_label');
label.show();
var boton=$('#id_boton');
boton.hide();
}
function ponerTurno () {
var caja=$('#id_turno');
var numero=caja.val();
if ( !! numero && ! isNaN(numero) ) { // !!numero-> string no vacio.
caja.hide();
var label=$('#id_label');
label.hide();
var boton=$('#id_boton');
boton.show();muchomucho
if ( numero<0 || numero > 99) numero=valor.substring(2);
topicMessage = new Paho.MQTT.Message("A-"+("00" + numero).slice (-2)); //Para añadir padZero a la izquierda del numero
topicMessage.destinationName = "numero";
topicMessage.retained = true;
mqtt.send(topicMessage);
}
}
</script>
</head>
<body>
<div id="Pagina">
<input type='text' id='topic' disabled hidden/>
<input type='text' id='status' size="80" disabled hidden/>
<div id="cabecera">IES VIRGEN DE GUADALUPE</div>
<div id="cuerpo">TURNO</div>
<div id="pie">
<a href="#" onclick="subirTurno();" class="botonAvanza">Avanza Turno</a>
<a href="#" onclick="bajarTurno();" class="botonRetrocede">Retrocede Turno</a>
<a href="#" onclick="verCaja();" class="botonSet" id="id_boton">Definir Turno</a>
<label class="labelturno" id="id_label">Introduzca nuevo turno ==></label>
<input id="id_turno" class="inputturno" onclick="this.select()" onKeyDown="if (event.keyCode==13) ponerTurno();" value="" />
</div>
</div>
</body>
</html>
Todos los ficheros necesarios ya los enlacé en entrada anterior del blog. Simplemente habría que añadir GestionTurnos.html en el mismo sitio web donde pusimos SuTurno.htmlAl estar basada en Mosquitto la página web GestionTurnos.html se actualizará en tiempo real y si está abierta en varias ubicaciones (por ejemplo todos los PC de administración) cualquier cambio se propagará a todas los demás de forma inmediata.
Una vez la tenemos funcionando se me ocurre dar una vuelta de tuerca más y convertir la aplicación web en una aplicación independiente de escritorio. De esta manera correrá en una ventana autónoma del navegador que podrá minimizarse y maximizarse a voluntad y será mas sencilla de usar. Hay muchas herramientas para realizar esta conversión web->escritorio, pero me ha gustado por su sencillez Nativifier. Lo que haremos será:
# cat /etc/apt/sources.list.d/nodesource.list
deb https://deb.nodesource.com/node_0.10 trusty main
deb-src https://deb.nodesource.com/node_0.10 trusty main
# apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 1655A0AB68576280
# apt-get update
# apt-get install nodejs
# npm install nativefier -g
# nativefier -n "Sistema de Gestión de Turnos" http://172.X.Y.Z/turnos/GestionTurnos.html
Esto crea un directorio con un fichero ejecutable estándar de Linux que al lanzarlo abre la aplicación Web de Gestión de Turnos como si fuera una aplicación de escritorio. Bastará con poner un acceso directo en el escritorio del usuario y quedará todo simple y accesible.Si queremos que la aplicación sea usada por todos los usuarios de la máquina lo mas sencillo será llevarla a /opt y dar permisos 777 a todo el directorio donde está.
Bueno, pues ya solo nos falta hacer algún día una aplicación para el móvil...
Eres el puto amo,
ResponderEliminarAlfonso no me funciona la parte de subir, bajar y poner. Si mando mosquitto_pub desde consola si actualiza....
Lo estoy haciendo en Raspberry aunque no creo que eso influya.
Un saludo y gracias.
Te voy comentando por si se te ocurre algo. Lo único que creo hay que configurar es el archivo config.js en el que pongo la Ip de la Raspberry y el puerto. Si pongo la Ip que le asigna el router, desde cualquier sitio puedo acceder a la web y ver la cola, pero no funcionan los botones de subir, bajar o poner. Si lo configuro con 127.0.0.1 solo funciona desde la propia Raspberry, pero tampoco funcionan los botones...
ResponderEliminarGracias por todo.
Hola Juan. En principio es solo configurar config.js, no tiene más.
EliminarLo único que se me ocurre es que sea un fallo de javascript. ¿Tienes la raspberry pi con GestionTurnos.html en una dirección pública a la que pueda acceder desde dentro de la red educativa?
Como estoy con las pruebas, la tengo en casa. Si quieres echarle un vistazo:
Eliminarshaserver2.ddns.net
Puerto 80 y 22 hacia la Raspberry
OK. La variable "valor" toma valor "undefined". Eso quiere decir que no hay nada en la cola mqtt cuando carga la página web.
EliminarTendría que entrar por ssh para ver que pasa con la cola, pero no tengo contraseña para entrar ahí. ¿Me la haces llegar a alfonso.pastor(arroba)gmail.com?
Esperate. Antes de seguir he entrado en la depuración de Firefox y me salen 2 cosas.
EliminarEn ponerTurno tienes:
boton.show();muchomucho
Ese "muchomucho" rompe todo el código Javascript de la máquina.
Luego, en la consola del depurador me dice:
Firefox no puede establecer una conexión con el servidor en ws://192.168.10.111:8000/mqtt.
Pero claro, mi navegador no puede acceder ahí ya que no alcanza esa máquina. Esto está pensado para ser ejecutado en una misma red todo, no en una página publicada a través de Internet, ya que el direccionamiento es diferente.
Como te he comentado por privado, funcionando.
ResponderEliminarEres una makina.
Un saludo y te debo unos cafés con algo de picar.
Me alegro :-). A ver si rula bien, ya me cuentas cuando se ponga en funcionamiento en entorno real.
EliminarFunciona perfectamente, he creado cinco html (mesa A, mesa B, mesa C, mesa D) cada administrativo entra en su html correspondiente para bajar y subir número y cambiar letra. Por otro lado está el Index que es el que se muestra por pantalla sin botones.
ResponderEliminarMuchísimas gracias.
Me alegro, es un placer que te sea útil. Me apunto lo de los html por mesas, aquí de momento al haber solo 2 mesas se coordinarán entre ellas pero nunca se sabe si tendremos que montarlo como tú.
EliminarSaludos.