/**
* Obtiene el nombre del host local de la conexión.
*/
- const std::string get_host(void) const;
+ const std::string& get_host(void) const;
/**
* Obtiene el puerto local de la conexión.
*/
- const Port get_port(void) const;
+ const Port& get_port(void) const;
};
/// Conexión para enviar comandos de control a una planta.
class ControlClient: public Connection {
- /////////////////////////////////////////////////////////////////////
- // Tipos.
-
- public:
-
/////////////////////////////////////////////////////////////////////
/// \name Señales
//@{
class ControlServer: public Connection {
/////////////////////////////////////////////////////////////////////
- // Tipos.
+ /// \name Señales
+ //@{
public:
/// Tipo de señal para indicar que se recibió un comando.
typedef SigC::Signal1<void, const Command&> SignalCommandReceived;
+ /// Obtiene la señal que avisa cuando se recibió un comando.
+ SignalCommandReceived& signal_command_received(void);
+
+ //@}
+
/////////////////////////////////////////////////////////////////////
// Atributos.
*/
void send(const Response& response);
- /**
- * Obtiene la señal que avisa cuando se recibió un comando.
- */
- SignalCommandReceived& signal_command_received(void);
-
};
}
\section page_server_general Descripción General
El servidor está dividido en 2 módulos que provean 2 servicios diferentes.
- \subsection page_server_general_control Módulo de Control.
+ \subsection page_server_general_control Módulo de Control
El módulo de control se basa en el protocolo TCP y se encarga de listar
los archivos de planta disponibles en el servidor, permitiendo cambiar
las propiedades de cada uno y conocer su estado en términos generales
Este módulo está implementado por las clases PlaQui::Server::ControlServer
y PlaQui::Server::ControlClient.
- \subsection page_server_general_transmision Módulo de Transmisión.
+ \subsection page_server_general_transmision Módulo de Transmisión
Este módulo se encarga de transmitir la simulación en tiempo real por UDP
(como si fuera un video). Comienza luego de que el módulo de control recibe
una petición de transmisión y continúa transmitiendo (en un principio)
PlaQui::Server::Receiver.
-\section page_server_protocolo Comandos del Módulo de Control.
+\section page_server_protocolo Comandos del Módulo de Control
Todos los comandos son rutas de archivos. En un principio no se van a utilizar
los <em>query string</em> de los datos pasados por GET ni datos adicionales
recibidos por POST. Es decir, de un <em>request</em> HTTP solo se usara la ruta
- <tt>transmission</tt>: Comandos para las transmisiones.
- <tt>plant</tt>: Comandos para las plantas.
- \subsection page_server_protocolo_general Comandos para el Servidor.
+ \subsection page_server_protocolo_general Comandos para el Servidor
Los comandos para el servidor, como se vio previamente, comienzan con
<tt>/server/</tt> seguido de alguna de las siguientes opciones:
<table>
</tr>
</table>
- \subsection page_server_protocolo_control Comandos para una Conexión de Control.
+ \subsection page_server_protocolo_control Comandos para una Conexión de Control
Todos los comandos de transmisiones comienzan con <tt>/connection/</tt>
y continúan con alguna de las siguientes opciones:
<table>
<td>Nada.</td>
</tr>
</table>
- \note Los nombres entre <tt>[</tt> y <tt>]</tt> denotan un argumento.
+ \note Los nombres entre <tt>[</tt> y <tt>]</tt> representan un argumento.
- \subsection page_server_protocolo_transmision Comandos para una Transmisión.
+ \subsection page_server_protocolo_transmision Comandos para una Transmisión
Todos los comandos de transmisiones comienzan con <tt>/transmission/</tt>
y continúan con alguna de las siguientes opciones:
<table>
<td>Nada.</td>
</tr>
</table>
- \note Los nombres entre <tt>[</tt> y <tt>]</tt> denotan un argumento.
+ \note Los nombres entre <tt>[</tt> y <tt>]</tt> representan un argumento.
- \subsection page_server_protocolo_planta Comandos para una Planta.
+ \subsection page_server_protocolo_planta Comandos para una Planta
Todos los comandos de plantas comienzan con <tt>/plant/</tt> y continúan con
alguna de las siguientes opciones:
<table>
<td>Nada.</td>
</tr>
</table>
- \note Los nombres entre <tt>[</tt> y <tt>]</tt> denotan un argumento.
+ \note Los nombres entre <tt>[</tt> y <tt>]</tt> representan un argumento.
-\section page_server_uso Modo de uso.
- Para utilizar el servidor es necesario disponer de un archivo XML de planta
- (por ejemplo, generado por el Constructor).
+\section page_server_uso Modo de uso
+ \subsection page_server_uso_inicio Inicio del servidor
+ Para iniciar el servidor es necesario disponer de un archivo XML de
+ planta (por ejemplo, generado por el Constructor).
- Invocación del servidor:
- \verbatim ./plaqui-server [archivo] [puerto] \endverbatim
+ Invocación del servidor:
+ \verbatim ./plaqui-server [archivo] [puerto] \endverbatim
- Ambos argumentos son opcionales. El primero, <tt>[archivo]</tt>, es la
- ubicación del archivo con la descripción de la planta a simular (por omisión
- <tt>planta.xml</tt>). El segundo, <tt>[puerto]</tt>, es el puerto en el cual
- se van a atender las peticiones al servidor (por omisión 7522).
+ Ambos argumentos son opcionales. El primero, <tt>[archivo]</tt>, es la
+ ubicación del archivo con la descripción de la planta a simular (por omisión
+ <tt>planta.xml</tt>). El segundo, <tt>[puerto]</tt>, es el puerto en el cual
+ se van a atender las peticiones al servidor (por omisión 7522).
+
+ \subsection page_server_uso_estado Estado del servidor
+ Mientras el servidor se ejecuta, va imprimiendo en la salida estándar su
+ estado. Se imprime cada vez que llega una conexión entrante y cada vez que
+ se detecta un error.
+
+ Otro tipo de información del estado del servidor puede ser obtenida desde
+ el cliente a través del comando <tt>/server/info</tt> (ver
+ \ref page_server_protocolo_general).
+
+ \note Los errores se imprimen en la salida de error, no en la salida
+ estándar.
+
+ \subsection page_server_uso_fin Finalización del servidor
+ Hay varias formas de finalizar el servidor:
+ - Enviando una señal de interrupción (<tt>SIGINT</tt>), por ejemplo,
+ presionando la combinación de teclas <tt>CTRL-C</tt>.
+ - Enviando una señal de salida (<tt>SIGQUIT</tt>) o de terminación
+ (<tt>SIGTERM</tt>), por ejemplo, a través del comando
+ <tt>kill(1)</tt>.
+ - Enviando un comando <tt>/server/stop</tt> desde un cliente (ver
+ \ref page_server_protocolo_general).
+
+ Cualquiera de estos métodos es válido y finaliza el servidor de forma
+ correcta.
*/
+
+/** \namespace PlaQui::Server
+
+Infrastructura cliente-servidor para PlaQui.
+
+Bajo este espacio de nombres (namespace) se encuentran todas las clases para la
+comunicación cliente-servidor de PlaQui. Esto no incluye la interfaz gráfica del
+cliente.
+
+*/
+
static const std::string FRAME_END;
/////////////////////////////////////////////////////////////////////
- // Tipos.
+ /// \name Señales
+ //@{
public:
/// Tipo de señal para indicar que se recibió un cuadro.
typedef SigC::Signal1<void, const std::string&> SignalFrameReceived;
+ /// Obtiene la señal que avisa cuando se recibió un cuadro.
+ SignalFrameReceived& signal_frame_received(void);
+
+ //@}
+ /////////////////////////////////////////////////////////////////////
// Atributos.
private:
Receiver(const Connection::Port& port = 7528,
const std::string& host = "localhost") throw(sockerr);
- /**
- * Obtiene la señal que avisa cuando se recibió un cuadro.
- */
- SignalFrameReceived& signal_frame_received(void);
-
};
}
/**
* Constructor.
*
- * \param plant_filename Nombre del archivo con la planta a cargar.
- * \param port Puerto en el cual escuchar.
+ * \param port Puerto en el cual escuchar.
*/
- Server(const std::string& plant_filename,
- const Connection::Port& port = 7522) throw(sockerr);
+ Server(const Connection::Port& port = 7522) throw(sockerr);
+
+ /**
+ * Agrega una planta al servidor.
+ *
+ * \param name Nombre que utilizará el servidor para identificar
+ * a la planta.
+ * \param filename Nombre del archivo con la planta a cargar.
+ * \return true si se agregó la planta, false si ya existía una
+ * planta con ese nombre.
+ */
+ bool add_plant(const std::string& name,
+ const std::string& filename);
/**
* Maneja los comandos recibidos por las conexiones.
#include "plaqui/server/runnable.h"
#include "plaqui/server/connection.h"
#include <socket++/sockinet.h>
+#include <sigc++/signal.h>
#include <list>
#include <vector>
/// Lista de información de conexiones de control.
typedef std::vector<ConnectionInfo> ConnectionInfoList;
+ /////////////////////////////////////////////////////////////////////
+ /// \name Señales
+ //@{
+
+ public:
+
+ /// Tipo de señal para indicar que se inició una conexión.
+ typedef SigC::Signal2<void, const std::string&,
+ const Connection::Port&> SignalConnectionOpened;
+
+ /// Obtiene la señal que avisa que se inició una conexión.
+ SignalConnectionOpened& signal_connection_opened(void);
+
+ //@}
+
/////////////////////////////////////////////////////////////////////
// Atributos.
- protected: //FIXME
+ protected:
/// Socket para escuchar conexiones.
sockinetbuf socket;
- private: // FIXME
+
+ private:
+
+ /// Señal que indica que se inició una conexión.
+ SignalConnectionOpened _connection_opened;
/// Conexiones de control.
ConnectionList connections;
INCLUDES = \
-I../include \
-I../../Model/include \
- -DDEBUG \
+## -DDEBUG \
-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
@PACKAGE_CFLAGS@
}
}
-const string Connection::get_host(void) const {
+const string& Connection::get_host(void) const {
return host;
}
-const Connection::Port Connection::get_port(void) const {
+const Connection::Port& Connection::get_port(void) const {
return port;
}
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": destructor." << endl;
#endif // DEBUG
- // TODO Temporal: espero que el receiver muera.
+ // FIXME Temporal: espero que el receiver muera.
if (receiver) {
receiver->finish();
}
<< ": host = " << host
<< " | port = " << port << endl;
#endif // DEBUG
+ // FIXME temporal
receiver = new Receiver(7528, host);
receiver->signal_finished().connect(SigC::slot_class(*this,
&ControlClient::on_receiver_finished));
socket >> response;
// Si se cerró el socket.
} catch (const ios::failure& e) {
- // TODO poner buenos codigos de error.
+ // FIXME poner buenos codigos de error.
signal_error().emit(1000000, "Se desconectó.");
return;
} catch (const sockerr& e) {
cerr << __FILE__ << "(" << __LINE__ << ")"
<< " : real_run() ERROR nro: " << e << endl;
#endif // DEBUG
- // TODO - es un error recuperable? O manda señal error()?
- error_received(e, "La respuesta recibida es inválida");
+ signal_error().emit(e, "La respuesta recibida es inválida");
continue;
}
switch (response.get_code()) {
#include "plaqui/server/command.h"
#include "plaqui/server/httperror.h"
#include "plaqui/server/httpresponse.h"
-//#include <cstring>
-//#include <sstream>
#ifdef DEBUG
# include "plaqui/server/string.h"
# include <iostream>
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": real_run()" << endl;
#endif // DEBUG
- //char buf[BUFSIZ];
while (!stop()) {
Command command;
try {
- //Glib::Mutex::Lock lock(socket_mutex);
socket >> command;
} catch (const ios::failure& e) {
// TODO poner buenos codigos de error.
- signal_error().emit(1000000, "Se desconectó.");
+ signal_error().emit(200000, "Se desconectó.");
return;
} catch (const sockerr& e) {
signal_error().emit(e.serrno(), e.errstr());
return;
- // Si se cerró el socket.
- //} catch (const ios::failure& e) {
- // stop = true;
- // continue;
// Si hay un error al parsear el comando, se envia una respuesta con el
// error.
} catch (const HTTPError& e) {
<< e.code << " | reason = " << HTTPMessage::reason(e.code)
<< " | desc = " << e.what() << endl;
#endif // DEBUG
- //Glib::Mutex::Lock lock(socket_mutex);
socket << HTTPResponse(e) << flush;
continue;
}
- // TODO agregar las verificaciones de abajo a HTTPRequest y padres.
- // Actualizacion: Estoy usando trim() en casi todos lados, no debería
- // ser necesario.
-/*
- // Primera línea no vacía (que debe ser el request).
- bool is_first = true;
- while (!stop && socket.getline(buf, BUFSIZ)) {
-#ifdef DEBUG
- cerr << __FILE__ << "(" << __LINE__ << ")"
- << " Recibiendo inea: " << buf << endl;
-#endif // DEBUG
- int len = strlen(buf);
- // Si tiene un retorno de carro, lo elimina.
- if (len && (buf[len-1] == '\r')) {
- buf[--len] = '\0';
- }
- // Si tiene contenido, lo agrego a la información del request.
- if (len) {
- // Si es la primera línea, es el request.
- if (is_first) {
- request.set_request(buf, socket->peerhost(),
- socket->peerport());
- is_first = false;
- } else {
- // TODO request.parse_header(buf);
- }
- // Si viene la línea vacía
- } else {
- // Si no es la primera, terminan las cabeceras HTTP.
- if (!is_first) {
- // Podría ir un break.
- stop = true;
- continue;
- }
- // Si es la primera, no pasa nada, sigue esperando un request.
- }
- }
-*/
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
<< " : real_run() Despachando comando: target = "
}
void ControlServer::send(const Response& response) {
- //Glib::Mutex::Lock lock(socket_mutex);
socket << response << flush;
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
if (line.length() != 3) {
throw HTTPResponse::INVALID_HTTP_RESPONSE_CODE;
}
- stringstream ss;
- ss << line; // TODO ver forma mas linda de convertir
- ss >> resp.status_code;
+ to(line, resp.status_code);
is >> static_cast<HTTPMessage&>(resp);
return is;
}
Server* server = NULL;
void on_error(const Runnable::Error& code, const string& desc) {
- cerr << "--------------------------------------------------------" << endl;
- cerr << "Error en el servidor:" << endl;
+ cerr << "ERROR EN EL SERVIDOR" << endl;
cerr << "Código: " << code << endl;
cerr << "Descripción: " << desc << endl;
- cerr << "--------------------------------------------------------" << endl;
}
void on_finished(void) {
- cerr << "Murió el servidor!" << endl;
+ cout << "Servidor finalizado!" << endl;
server = NULL;
}
+void on_connection_opened(const string& host, const Connection::Port& port) {
+ cout << "Se abrió una nueva conexión a " << host << ":" << port << "."
+ << endl;
+}
+
void on_signal(int signal) {
+ static bool called = false;
+ if (!called) {
+ cout << "Se recibe señal " << signal << "." << endl;
+ }
switch (signal) {
case SIGINT:
case SIGTERM:
case SIGQUIT:
- if (server) {
+ if (!called && server) {
+ cout << "Terminando servidor... " << flush;
server->finish();
}
break;
}
+ called = true;
}
int main(int argc, char* argv[]) {
// Termina con mas informacion si hay una excepcion no manejada.
set_terminate(__gnu_cxx::__verbose_terminate_handler);
+ // Calculo número de revisión.
+ string rev = "$Rev$";
+ rev = rev.substr(6, rev.length() - 8);
+
// Bienvenida.
- cout << "PlaQui Server. Modo de uso: " << endl;
- cout << "\t" << argv[0] << " [planta] [puerto]" << endl;
+ cout << "PlaQui Server version " << VERSION << " (revisión " << rev
+ << ")." << endl;
+ cout << "Modo de uso: " << argv[0] << " [planta] [puerto]" << endl;
// Acepta argumentos.
string filename = "planta.xml";
try {
// Crea el server (empieza a escuchar).
- server = new Server(filename, port);
+ server = new Server(port);
} catch (const sockerr& e) {
cerr << "Socket Error: " << e.operation() << " | serrno = "
<< e.serrno() << " | errstr = " << e.errstr() << endl;
if (e.serrno() == 98) {
cerr << "No se puede usar el puerto " << port << " porque ya está "
"siendo utilizado por otro programa." << endl;
- }
- if (e.io()) {
- cerr << "Es: non-blocking and interrupt io recoverable error."
- << endl;
- } else if (e.arg()) {
- cerr << "Es: incorrect argument supplied. recoverable error."
- << endl;
- } else if (e.op()) {
- cerr << "Es: operational error. recovery difficult." << endl;
- } else if (e.conn()) {
- cerr << "Es: connection error." << endl;
- } else if (e.addr()) {
- cerr << "Es: address error." << endl;
- } else if (e.benign()) {
- cerr << "Es: recoverable read/write error like EINTR etc." << endl;
+ } else {
+ cerr << "Error al crear el socket: operación: " << e.operation()
+ << ", código: " << e.serrno() << ", descripción: "
+ << e.errstr() << endl;
}
return e.serrno();
} catch (const exception& e) {
// Conecto señal para atender la finalización del server.
server->signal_finished().connect(SigC::slot(on_finished));
+ // Conecto señal para saber cuando se abre una nueva conexión.
+ server->signal_connection_opened().connect(SigC::slot(on_connection_opened));
+
+ // Agrego planta.
+ if (!server->add_plant("default", filename)) {
+ cerr << "Advertencia: Ya existe una planta llamada 'default'. "
+ "No se puede agregar la planta almacenada en '" << filename
+ << "'." << endl;
+ }
+
// Corre el server.
server->run();
+ cout << "Atendiendo conexiones en el puerto " << port << "." << endl;
// Espera a que el server se muera.
while (server) {
- //cerr << "-----------------\n\nAHHHHHHH\n\n----------------" << endl;
- Glib::usleep(100000); // 0,1 segundos
+ Glib::usleep(500000); // 0,5 segundos
}
- // Espera un segundo más por las dudas, para asegurarse de que terminó.
- Glib::usleep(1000000); // 1 segundo
-
- // Como no detachee el server, lo tengo que eliminar a mano.
- //delete server;
return 0;
}
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": constructor. filename = " << filename << endl;
#endif // DEBUG
- // TODO plant
-/* simulator.add_pump("bomba1");
- simulator.add_conduct("c");
- simulator.add_conduct("c1");
- simulator.add_drainage("d");
- simulator.add_tank("tanque");
-
- simulator.connect("bomba1", "c", Model::IConector::OUT);
- simulator.connect("c", "tanque", Model::IConector::OUT);
- simulator.connect("tanque", "c1", Model::IConector::OUT);
- simulator.connect("c1", "d", Model::IConector::OUT);
-*/
}
void Plant::real_run(void) throw() {
Transmitter* trans;
try {
trans = new Transmitter(host, port);
- } catch (const sockerr& e) { // TODO - Hace mas selectivo el catch?
+ } catch (const sockerr& e) {
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": transmission_start ERROR (" << e.serrno()
<< "): " << e.errstr() << endl;
#endif // DEBUG
- //delete trans;
return false;
-// } catch (...) { // TODO - Hace mas selectivo el catch?
-//#ifdef DEBUG
-// cerr << __FILE__ << "(" << __LINE__ << ")"
-// << ": transmission_start ERROR: desconocido."
-// << endl;
-//#endif // DEBUG
-// //delete trans;
-// return false;
}
transmissions.push_back(trans);
trans->signal_finished().connect(SigC::bind(
#endif // DEBUG
ostringstream oss;
ifstream ifs(filename.c_str());
- // XXX Saco la línea de definición de XML (<?xml ?>), ver si esta hecho muy
+ // FIXME Saco la línea de definición de XML (<?xml ?>), ver si esta hecho muy
// feo.
ifs.ignore(50, '\n'); // Ignora 50 caracteres o hasta un enter.
ifs >> oss.rdbuf();
paused = paused_;
}
-/*
-bool Plant::transmission_exists(const string& host,
- const Connection::Port& port) {
- Glib::Mutex::Lock lock(transmissions_mutex);
- for (TransmitterList::const_iterator i = transmissions.begin();
- i != transmissions.end(); i++) {
- if (((*i)->get_host() == host) && ((*i)->get_oprt() == port)) {
- return true;
- }
- }
- return false; // No la encontró.
-}
-*/
-
-//const std::string& Plant::get_filename(void) const {
-// return filename;
-//}
-
-/// \todo FIXME esto deberia estar protegido por un mutex.
-//Plant::SignalUpdated& Plant::signal_updated(void) {
-// return updated;
-//}
-
} // namespace Server
} // namespace PlaQui
return is;
}
-/// \todo TODO hacer el metodo build como en command.
ostream& operator<<(ostream& os, const Response& resp) {
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")" << ": operator<<()" << endl;
}
}
-Server::Server(const string& plant_filename, const Connection::Port& port)
+Server::Server(const Connection::Port& port)
throw(sockerr): TCPServer(port) {
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": port = " << port << endl;
#endif // DEBUG
- // FIXME - hacer que se puedan cargar mas plantas bien.
+}
+
+bool Server::add_plant(const string& name, const string& filename) {
Glib::Mutex::Lock lock(plants_mutex);
- plants["default"] = new Plant(plant_filename);
- plants["default"]->signal_finished().connect(SigC::bind(
- SigC::slot_class(*this, &Server::on_plant_finished),
- "default"));
- plants["default"]->run();
+ if (plants.find(name) == plants.end()) { // No existe
+ plants[name] = new Plant(filename);
+ plants[name]->signal_finished().connect(SigC::bind(
+ SigC::slot_class(*this, &Server::on_plant_finished),
+ name.c_str()));
+ plants[name]->run();
+ return true;
+ }
+ return false;
}
Connection* Server::new_connection(const sockbuf::sockdesc& sd) {
<< endl;
#endif // DEBUG
ControlServer* connection = new ControlServer(sd);
- // TODO verificar si el new se hace bien? no creo.
connection->signal_command_received().connect(SigC::bind(
SigC::slot_class(*this, &Server::on_control_command_received),
connection));
- // TODO:
return connection;
}
plants.erase(plant);
}
-/// \todo Terminar de implementar.
void Server::on_control_command_received(const Command& command,
ControlServer* controlserver) {
#ifdef DEBUG
response = new Response(Response::OK,
"El server se cerrará en instantes");
// XXX - Sin mandar la respuesta enseguida podría ser que el server
- // cierre la conexión antes de mandar la respuesta.
+ // cierre la conexión antes de mandar la respuesta. En las pruebas
+ // esto nunca pasó.
//response->headers["Content-Type"] = "text/xml; charset=iso-8859-1";
//controlserver->send(*response);
//delete response;
response = new Response(Response::INVALID_TARGET, command.get_target()
+ " es un destino inválido");
}
- // FIXME
- //response->headers["Connection"] = "close";
controlserver->send(*response);
delete response;
- // FIXME con timeout no debería ser necesario. Verificar cabecera Connection
- // para saber si hay que finish()earlo o no.
- //if (stop_controlserver) {
- // controlserver->finish();
- //}
}
Response* Server::cmd_server_info(void) const {
- // FIXME
stringstream xml;
xml << "<serverstatus>" << endl;
xml << "\t<version>" VERSION "</version>" << endl;
}
Response* Server::cmd_connection_list(void) {
- // FIXME
+ // TODO implementar con lista genérica.
TCPServer::ConnectionInfoList cil = get_connected();
stringstream xml;
xml << "<list type=\"connection\">" << endl;
}
Response* Server::cmd_transmission_list(void) {
- // FIXME
+ // TODO implementar con lista genérica.
stringstream xml;
xml << "<list type=\"transmission\">" << endl;
-/*TODO plants_mutex.lock();
+/*FIXME plants_mutex.lock();
for (PlantList::const_iterator i = plants.begin();
i != plants.end(); i++) {
trans
return new Response(Response::PLANT_NOT_FOUND,
string("No existe la planta '") + plant + "'");
}
- // TODO try/catch?
string xml = plants[plant]->get_xml();
if (xml.length()) {
return new Response(xml);
}
unsigned hz;
to(command.get_args()[1], hz);
- /* TODO poner cantidad real que tomó: hz = */plants[name]->set_frequency(hz);
+ plants[name]->set_frequency(hz);
String shz;
shz.from(hz);
return new Response(Response::OK,
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": port = " << port << endl;
#endif // DEBUG
- // FIXME
- //cerr << "recvtimeout = " << socket.recvtimeout(1) << endl;
- //cerr << "sendtimeout = " << socket.sendtimeout(1) << endl;
- //cerr << "recvtimeout = " << socket.recvtimeout(1) << endl;
- //cerr << "sendtimeout = " << socket.sendtimeout(1) << endl;
socket.reuseaddr(true);
socket.bind(port);
#ifdef DEBUG
sockinetbuf suicida(sockbuf::sock_stream);
suicida.connect(socket.localhost(), socket.localport());
} catch (const sockerr& e) {
- // FIXME
+ // FIXME mejorar codigos de error.
signal_error().emit(100000 + e.serrno(), string("No se pudo crear "
"conexión 'suicida' para terminar el servidor: ")
+ e.errstr());
try {
connection = new_connection(socket.accept());
} catch (const sockerr& e) { // No se si el accept() puede fallar.
- signal_error().emit(e.serrno(), e.errstr());
+ signal_error().emit(e.serrno(),
+ string("Error creando conexión nueva: ") + e.errstr());
continue; // Supongo que puede seguir aceptando conexiones.
}
+ _connection_opened(connection->get_host(), connection->get_port());
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": real_run(): connection = " << connection
<< endl;
#endif // DEBUG
Glib::Mutex::Lock lock(connections_mutex);
- // XXX connections_mutex.lock();
connections.push_back(connection);
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
cerr << "\t " << *i << endl;
}
#endif // DEBUG
- // XXX connections_mutex.unlock(); // Si pongo el mutex antes del run(),
- // muere.
// Conecto la señal para cuando termina una conexión, borrarla.
connection->signal_finished().connect(SigC::bind(
SigC::slot_class(*this, &TCPServer::on_connection_finished),
return cl;
}
+TCPServer::SignalConnectionOpened& TCPServer::signal_connection_opened(void) {
+ return _connection_opened;
+}
+
} // namespace Server
} // namespace PlaQui
- socket++ ( >= 1.12.10): Wrapper de socket portables en C++ streams
[http://members.aon.at/hstraub/linux/socket++/]
- \subsection requerimientos Requerimientos de Hardware y SO.
+ \subsection requerimientos Requerimientos de Hardware y SO
PlaQui fue desarrollado bajo Debian GNU/Linux sid (http://www.debian.org/),
pero debería andar en cualquier GNU/Linux e incluso probablemente en otros
Unixes (e incluso podría llegar a andar en WIN32). La versión para el
- Procesador: PII 400 Mhz
- Memoria RAM: 64 MB
- \subsection instalacion Instalación.
- El programa se divide en 4 módulos:
- - Modelo: es el módulo que se encarga de la simulación y el modelo \c
- físico de la planta (es una biblioteca).
- - \ref page_server "Servidor": es la infrastructura de red. Comprende tanto
- el servidor como el cliente en cuando al manejo de la red (es una
- biblioteca y un programa).
- - Cliente: es el cliente gráfico que permite ver la simulación (es un
- programa).
- - Constructor: es el programa para diseñar la planta química que será
- simulada por el modelo en el servidor (es un programa).
-
- La instalación puede realizarse de dos formas: instalando módulo por
- módulo o instalando todos los módulos a la vez.
+ \subsection instalacion Instalación
+ El programa se divide en 4 módulos:
+ - Modelo: es el módulo que se encarga de la simulación y el modelo \c
+ físico de la planta (es una biblioteca).
+ - \ref page_server "Servidor": es la infrastructura de red. Comprende tanto
+ el servidor como el cliente en cuando al manejo de la red (es una
+ biblioteca y un programa).
+ - Cliente: es el cliente gráfico que permite ver la simulación (es un
+ programa).
+ - Constructor: es el programa para diseñar la planta química que será
+ simulada por el modelo en el servidor (es un programa).
+
+ La instalación puede realizarse de dos formas: instalando módulo por
+ módulo o instalando todos los módulos a la vez.
\subsubsection instalacion_global Instalación Global
- Para instalar todos los programas es sencillo.
- -# Descomprimir el paquete:
- \verbatim tar -xvzf plaqui-proyect.tar.gz \endverbatim
- -# Entrar al directorio del proyecto:
- \verbatim cd plaqui \endverbatim
- -# Inicializar el proyecto:
- \verbatim aclocal && autoconf && automake -a \endverbatim
- -# Configurar la compilación del proyecto (generalmente basta con):
- \verbatim ./configure \endverbatim
- Esto lo instala por defecto en /usr/local, se puede
- especificar otra ruta con la opcion \c --prefix
- -# Compilar el proyecto:
- \verbatim make \endverbatim
- -# Instalar el proyecto:
- \verbatim make install \endverbatim
+ Para instalar todos los programas es sencillo.
+ -# Descomprimir el paquete:
+ \verbatim tar -xvzf plaqui-proyect.tar.gz \endverbatim
+ -# Entrar al directorio del proyecto:
+ \verbatim cd plaqui \endverbatim
+ -# Inicializar el proyecto:
+ \verbatim aclocal && autoconf && automake -a \endverbatim
+ -# Configurar la compilación del proyecto (generalmente basta con):
+ \verbatim ./configure \endverbatim
+ Esto lo instala por defecto en <tt>/usr/local</tt>, se puede
+ especificar otra ruta con la opcion <tt>--prefix</tt>.
+ -# Compilar el proyecto:
+ \verbatim make \endverbatim
+ -# Instalar el proyecto:
+ \verbatim make install \endverbatim
+
+ \note Para realizar el último paso, dependiendo de si usó la
+ opción <tt>--prefix</tt> o no, es posible que necesite
+ permisos de superusuario (<tt>root</tt>).
\subsubsection instalacion_global Instalación Por Partes
- El procedimiento es muy similar al anterior:
- -# Descomprimir el paquete:
- \verbatim tar -xvzf plaqui-proyect.tar.gz \endverbatim
- -# Entrar al directorio del módulo [Módulo]:
- \verbatim cd plaqui/[Módulo] \endverbatim
- -# Inicializar el módulo:
- \verbatim aclocal && autoconf && automake -a \endverbatim
- -# Configurar la compilación del módulo (generalmente basta con):
- \verbatim ./configure \endverbatim
- -# Compilar el módulo:
- \verbatim make \endverbatim
- -# Instalar el módulo:
- \verbatim make install \endverbatim
+ El procedimiento es muy similar al anterior:
+ -# Descomprimir el paquete:
+ \verbatim tar -xvzf plaqui-proyect.tar.gz \endverbatim
+ -# Entrar al directorio del módulo [Módulo]:
+ \verbatim cd plaqui/[Módulo] \endverbatim
+ -# Inicializar el módulo:
+ \verbatim aclocal && autoconf && automake -a \endverbatim
+ -# Configurar la compilación del módulo (generalmente basta con):
+ \verbatim ./configure \endverbatim
+ -# Compilar el módulo:
+ \verbatim make \endverbatim
+ -# Instalar el módulo:
+ \verbatim make install \endverbatim
+
+ \note Para realizar el último paso, dependiendo de si usó la
+ opción <tt>--prefix</tt> o no, es posible que necesite
+ permisos de superusuario (<tt>root</tt>).
\section mainpage_servidor Servidor
Puede ver la documentación del servidor en \ref page_server
*/
+
+/** \namespace PlaQui
+
+Diseñador, simulador y controlador de plantas químicas distribuido.
+
+El Programa se divide en 4 módulos. Los módulo que se comportan como
+biblioteca compartida tienen a su vez un espacio de nombres (namespace)
+anidado: PlaQui::Model y PlaQui::Server.
+
+*/
+