Principal::~Principal()
{
- if (conexion != NULL)
- delete conexion;
+ if (conexion) {
+ conexion->finish();
+ }
+ // Espera a que termine realmente.
+ while (conexion) {
+ Glib::usleep(10000); // 10 milisegundos
+ }
}
void Principal::on_dlg_connect_ok()
}
catch (...) {
txt_view->get_buffer()->insert_at_cursor("NO SE PUDO CREAR OBJETO\n");
- delete conexion;
- conexion == NULL;
+ //delete conexion; XXX Si no me equivoco, si falla el
+ //constructor, no se reserva la memoria (el delete no va).
+ conexion = NULL;
return;
}
PlaQui::Server::Command c("connection", "stop");
c.add_arg(conexion->get_host());
- c.add_arg("7522");
+ c.add_arg(conexion->get_port());
conexion->send(c);
}
/// Pedido HTTP.
class Command: private HTTPRequest {
+ /////////////////////////////////////////////////////////////////////
// Tipos.
public:
/// Tipo de métodos HTTP reconocidos.
typedef std::vector<std::string> Arguments;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
private:
/// Lista de argumentos que recibe el comando.
Arguments args;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
private:
/// Conexión.
class Connection: public Runnable {
+ /////////////////////////////////////////////////////////////////////
// Tipos.
public:
/// Puerto.
typedef unsigned Port;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
protected:
/// Mutex para el socket.
//Glib::Mutex socket_mutex;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
public:
/**
* Finaliza la conexión.
*
- * \param attach Si es true, la función no retorna hasta que no
- * finalice la tearea (no recomendable).
- *
* \note Para saber cuando la tarea fue finalizada puede utilizar
* la señal signal_finished().
*/
- virtual void finish(bool attach = false);
+ virtual void finish(void);
/**
* Obtiene el nombre del host local de la conexión.
/// Conexión para enviar comandos de control a una planta.
class ControlClient: public Connection {
+ /////////////////////////////////////////////////////////////////////
// Tipos.
public:
/// Tipo de señal para indicar que se recibió un error.
typedef SigC::Signal1<void, unsigned> SignalErrorReceived;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
protected:
/// Receptor del estado de la planta TODO Temporal.
Receiver* receiver;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
private:
/// Conexión para recibir comandos de control para una planta.
class ControlServer: public Connection {
-
+ /////////////////////////////////////////////////////////////////////
// Tipos.
public:
/// Tipo de señal para indicar que se recibió un comando.
typedef SigC::Signal1<void, const Command&> SignalCommandReceived;
-
+ /////////////////////////////////////////////////////////////////////
// Atributos.
private:
/// Señal para indicar que se recibió un comando.
SignalCommandReceived command_received;
-
+ /////////////////////////////////////////////////////////////////////
// Métodos.
protected:
/// Error HTTP.
class HTTPError: public std::runtime_error {
+ /////////////////////////////////////////////////////////////////////
// Atributos.
public:
/// Código de error.
unsigned code;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
public:
/// Cabeceras HTTP.
class HTTPHeaders: public std::map<std::string, std::string> {
+ /////////////////////////////////////////////////////////////////////
// Métodos.
public:
/// Pedido HTTP.
class HTTPMessage {
+ /////////////////////////////////////////////////////////////////////
// Constantes.
public:
static const unsigned NOT_IMPLEMENTED = 501;
static const unsigned HTTP_VERSION_NOT_SUPPORTED = 505;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
private:
/// Cabeceras HTTP.
HTTPHeaders headers;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
public:
/// Pedido HTTP.
class HTTPRequest: public HTTPMessage {
+ /////////////////////////////////////////////////////////////////////
// Constantes.
protected:
/// Caracteres no hexa para URIs (RFC 2396).
static const std::string CHARS_HEX;
+ /////////////////////////////////////////////////////////////////////
// Tipos.
public:
/// Tipo de métodos HTTP reconocidos.
typedef enum {GET, POST} HTTPMethod;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
protected: // TODO hacer privados con get() y set() ???
/// Query string.
std::string query;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
public:
/// Respuesta HTTP.
class HTTPResponse: public HTTPMessage {
+ /////////////////////////////////////////////////////////////////////
// Tipos.
public:
MISSING_HTTP_RESPONSE_CODE
} Error;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
//protected: FIXME - hacer privado con get/set?
/// Descripción del código (razón).
std::string reason;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
public:
/// Planta Química.
class Plant: public Runnable {
-
+ /////////////////////////////////////////////////////////////////////
// Tipos.
private:
/// Lista de conexiones de control.
typedef std::list<Transmitter*> TransmitterList;
-
+ /////////////////////////////////////////////////////////////////////
// Atributos.
private:
/// Nombre del archivo donde esta el XML de la planta.
std::string filename;
-
+ /////////////////////////////////////////////////////////////////////
// Métodos.
protected:
/// Conexión para recibir el estado de una planta.
class Receiver: public Connection {
+ /////////////////////////////////////////////////////////////////////
// Constantes.
private:
/// Marca de fin de frame.
static const std::string FRAME_END;
-
+ /////////////////////////////////////////////////////////////////////
// Tipos.
public:
/// Señal que indica que se recibió un cuadro.
SignalFrameReceived frame_received;
-
+ /////////////////////////////////////////////////////////////////////
// Métodos.
private:
/// ealizauna tarea (generalmente en un thread).
class Runnable {
+ /////////////////////////////////////////////////////////////////////
// Tipos.
public:
/// Tipo de señal para indicar que hubo un error.
typedef SigC::Signal2<void, const Error&, const std::string&> SignalError;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
private:
/// Thread en el cual correr la tarea.
- Glib::Thread* thread;
+ Glib::Thread* _thread;
/// Señal que indica que se finalizó la tarea.
- SignalFinished finished;
-
- protected:
+ SignalFinished _finished;
/// Señal que indica que hubo un error.
- SignalError error;
+ SignalError _error;
- /**
- * Indica si se debe frinalizar la tarea.
- *
- * \todo Poner como privado y poner get() set() con locks.
- */
- bool stop;
+ /// Indica si se debe frinalizar la tarea.
+ bool _stop;
+ /// Mutex para stop.
+ Glib::Mutex stop_mutex;
+
+ /////////////////////////////////////////////////////////////////////
// Métodos.
private:
protected:
- /// Realiza la terea.
+ /**
+ * Indica si la tarea debe finalizar.
+ */
+ bool stop(void);
+
+ /**
+ * Establece si la tarea debe finalizar.
+ *
+ * \param stop Nuevo valor.
+ *
+ * \return Valor anterior.
+ */
+ bool stop(bool stop);
+
+ /**
+ * Realiza la terea.
+ */
virtual void real_run(void) = 0;
public:
/**
* Finaliza la tarea.
*
- * \param attach Si es true, la función no retorna hasta que no
- * finalice la tearea (no recomendable).
- *
* \note Para saber cuando la tarea fue finalizada puede utilizar
* la señal signal_finished().
*/
- virtual void finish(bool attach = false);
+ virtual void finish(void);
/**
* Obtiene la señal que avisa cuando la tarea es finalizada.
*/
class Server: public TCPServer {
+ /////////////////////////////////////////////////////////////////////
// Tipos.
private:
/// Lista de plantas químicas.
typedef std::map<std::string, Plant*> PlantList;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
private:
/// Mutex para las plantas.
Glib::Mutex plants_mutex;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
protected:
return p2;
}
- /// Conexión.
+ /// String con varios métodos útiles.
class String: public std::string {
+ /////////////////////////////////////////////////////////////////////
+ // Métodos.
+
public:
/// Caracteres que son considerados espacios a la hora de parsear.
*/
class TCPServer: public Runnable {
+ /////////////////////////////////////////////////////////////////////
// Constantes.
private:
/// Cantidad máxima de conexiones pendientes.
static const unsigned MAX_PENDING_CONNECTIONS = 10;
+ /////////////////////////////////////////////////////////////////////
// Tipos.
private:
/// Lista de información de conexiones de control.
typedef std::vector<ConnectionInfo> ConnectionInfoList;
+ /////////////////////////////////////////////////////////////////////
// Atributos.
- private:
+ protected: //FIXME
/// Socket para escuchar conexiones.
sockinetbuf socket;
+ private: // FIXME
/// Conexiones de control.
ConnectionList connections;
/// Mutex para las conexiones.
Glib::Mutex connections_mutex;
+ /////////////////////////////////////////////////////////////////////
// Métodos.
private:
/**
* Finaliza la tarea.
*
- * \param attach Si es true, la función no retorna hasta que no
- * finalice la tearea (no recomendable).
- *
* \note Para saber cuando la tarea fue finalizada puede utilizar
* la señal signal_finished().
*/
- //virtual void finish(bool attach = false);
+ //virtual void finish(void);
/**
* Se encarga de borrar una conexión de la lista cuando finaliza.
/// Conexión para transmitir el estado de una planta.
class Transmitter: public Connection {
+ /////////////////////////////////////////////////////////////////////
// Métodos.
private:
#endif // DEBUG
}
-void Connection::finish(bool attach) {
+void Connection::finish(void) {
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
- << ": finish(attach = " << attach << ")." << endl;
+ << ": finish();" << endl;
#endif // DEBUG
//socket_mutex.lock();
try {
socket->shutdown(sockbuf::shut_readwrite);
+ // FIXME socket->close(sockbuf::shut_readwrite);
+ // close(socket->sd());
} catch (const sockerr& e) {
- error(e.serrno(), e.errstr());
+ signal_error().emit(e.serrno(), e.errstr());
}
//socket_mutex.unlock();
- Runnable::finish(attach);
+ Runnable::finish();
}
const string& Connection::get_host(void) const {
<< ": destructor." << endl;
#endif // DEBUG
// TODO Temporal: espero que el receiver muera.
- receiver->finish(true);
+ // Conectar señal on_receiver_finished() y esperar a que el puntero sea
+ // NULL para saber que terminó.
+ receiver->finish();
}
ControlClient::ControlClient(const string& _host,
try {
} catch (const sockerr& e) {
// TODO Poner una señal de error específica?
- error(e.serrno(), e.errstr());
+ signal_error().emit(e.serrno(), e.errstr());
return;
}
// TODO sacar signal_connected?
connected();
// TODO Temporal: el receiver empieza a escuchar.
receiver->run();
- while (!stop) {
+ while (!stop()) {
HTTPResponse response;
try {
//Glib::Mutex::Lock lock(socket_mutex);
// Si se cerró el socket.
} catch (const ios::failure& e) {
// TODO poner buenos codigos de error.
- error(1000000, "Se desconectó.");
+ signal_error().emit(1000000, "Se desconectó.");
return;
} catch (const sockerr& e) {
- error(e.serrno(), e.errstr());
+ signal_error().emit(e.serrno(), e.errstr());
return;
// Si hay un error al parsear la respuesta.
} catch (const HTTPResponse::Error& e) {
try {
socket << command << flush;
} catch (const sockerr& e) {
- error(e.serrno(), e.errstr());
+ signal_error().emit(e.serrno(), e.errstr());
finish();
}
#ifdef DEBUG
<< ": real_run()" << endl;
#endif // DEBUG
//char buf[BUFSIZ];
- while (!stop) {
+ while (!stop()) {
Command command;
try {
//Glib::Mutex::Lock lock(socket_mutex);
socket >> command;
} catch (const ios::failure& e) {
// TODO poner buenos codigos de error.
- error(1000000, "Se desconectó.");
+ signal_error().emit(1000000, "Se desconectó.");
return;
} catch (const sockerr& e) {
- error(e.serrno(), e.errstr());
+ signal_error().emit(e.serrno(), e.errstr());
return;
// Si se cerró el socket.
//} catch (const ios::failure& e) {
} 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;
// Conecto señal para atender errores.
server->signal_error().connect(SigC::slot(on_error));
+ // Conecto señal para atender la finalización del server.
+ server->signal_finished().connect(SigC::slot(on_finished));
+
// Corre el server.
- server->run(false);
+ server->run();
// Espera a que el server se muera.
while (server) {
- Glib::usleep(1000000);
+ //cerr << "-----------------\n\nAHHHHHHH\n\n----------------" << endl;
+ Glib::usleep(100000); // 0,1 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__ << ")"
<< ": destructor." << endl;
#endif // DEBUG
- // Termino transmisiones.
- Glib::Mutex::Lock lock(transmissions_mutex);
- for (TransmitterList::iterator trans = transmissions.end();
+ // Mando a terminar todas las transmisiones.
+ transmissions_mutex.lock();
+ for (TransmitterList::iterator trans = transmissions.begin();
trans != transmissions.end(); trans++) {
- (*trans)->finish(true);
+ (*trans)->finish();
+ }
+ TransmitterList::size_type count = transmissions.size();
+ transmissions_mutex.unlock();
+ // Espero que terminen realmente.
+ while (count) {
+ Glib::usleep(10000); // 10 milisegundos
+ transmissions_mutex.lock();
+ count = transmissions.size();
+ transmissions_mutex.unlock();
}
}
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": real_run." << endl;
#endif // DEBUG
- while (!stop) {
+ while (!stop()) {
simulator_mutex.lock();
simulator.simulate();
string plantstatus = simulator.get_state_as_xml();
(*i)->send(plantstatus);
}
transmissions_mutex.unlock();
- Glib::usleep(1000000);
+ Glib::usleep(100000);
}
}
char buf[BUFSIZ];
bool in_frame = false;
stringstream ss;
- while (!stop) {
+ while (!stop()) {
try {
if (!socket.getline(buf, BUFSIZ)) {
return; // Se terminó la transmision.
}
} catch (const sockerr& e) {
- error(e.serrno(), e.errstr());
+ signal_error().emit(e.serrno(), e.errstr());
return;
}
string sbuf = buf;
#endif // DEBUG
}
-Runnable::Runnable(void): thread(0), stop(false) {
+Runnable::Runnable(void): _thread(NULL), _stop(false) {
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": constructor." << endl;
void Runnable::static_run(Runnable* runner) {
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
- << ": static_run(runner = " << runner << ")"
- << endl;
+ << ": static_run(runner = " << runner << ")" << endl;
#endif // DEBUG
runner->real_run();
- runner->finished();
- //runner->thread->join();
+ runner->_finished();
delete runner;
}
if (detach) {
// Corremos el thread en una funcion estática para poder destruirlo al
// finalizar, pasandole el puntero al objeto.
- thread = Glib::Thread::create(
+ _thread = Glib::Thread::create(
SigC::bind<Runnable*>(SigC::slot(&Runnable::static_run), this),
- true);//false);
+ false);//true);
// Si no corremos la tarea normalmente.
} else {
real_run();
}
-void Runnable::finish(bool attach) {
+void Runnable::finish(void) {
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
- << ": finish(attach = " << attach << ")" << endl;
+ << ": finish();" << endl;
#endif // DEBUG
- stop = true;
- if (attach) {
- thread->join();
- }
+ stop(true);
+}
+
+bool Runnable::stop(void) {
+ Glib::Mutex::Lock lock(stop_mutex);
+ bool tmp = _stop;
+ return tmp;
+}
+
+bool Runnable::stop(bool stop) {
+ Glib::Mutex::Lock lock(stop_mutex);
+ bool tmp = _stop;
+ _stop = stop;
+ return tmp;
}
Runnable::SignalFinished& Runnable::signal_finished(void) {
- return finished;
+ return _finished;
}
Runnable::SignalError& Runnable::signal_error(void) {
- return error;
+ return _error;
}
} // namespace Server
#include "plaqui/server/connection.h"
#include "plaqui/server/controlserver.h"
#include <sigc++/class_slot.h>
+#include <glibmm/timer.h>
#include <sstream>
#include <exception>
#ifdef DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": destructor." << endl;
#endif // DEBUG
- // Termino plantas.
- Glib::Mutex::Lock lock(plants_mutex);
- for (PlantList::iterator i = plants.end(); i != plants.end(); i++) {
- i->second->finish(true);
+ // Mando a terminar todas las plantas.
+ plants_mutex.lock();
+ for (PlantList::iterator i = plants.begin(); i != plants.end(); i++) {
+ i->second->finish();
+ }
+ PlantList::size_type count = plants.size();
+ plants_mutex.unlock();
+ // Espero que terminen realmente.
+ while (count) {
+ Glib::usleep(10000); // 10 milisegundos
+ plants_mutex.lock();
+ count = plants.size();
+ plants_mutex.unlock();
}
}
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": port = " << port << endl;
#endif // DEBUG
- // FIXME
+ // FIXME - hacer que se puedan cargar mas plantas bien.
Glib::Mutex::Lock lock(plants_mutex);
plants["default"] = new Plant(plant_filename);
plants["default"]->signal_finished().connect(
finish();
response = new HTTPResponse(HTTPMessage::OK,
"<response desc=\"El server se apagará en instantes...\" />");
+ response->headers["Content-Type"] = "text/xml; charset=iso-8859-1";
+ controlserver->send(*response);
+ delete response;
+ // Creo una conexión suicida para que el accept() del server retorne
+ // el control y el server pueda terminar realmente.
+ try {
+ sockinetbuf suicida(sockbuf::sock_stream);
+ suicida.connect(socket.localhost(), socket.localport());
+ } catch (...) {
+ // FIXME
+ signal_error().emit(12345, "ahhhh! no puedo crear conexion suicida");
+ }
+ return;
} else {
response = new HTTPResponse(HTTPMessage::NOT_FOUND,
"<response desc=\"Invalid command for 'server' taget!\" />");
#include "plaqui/server/tcpserver.h"
#include <sigc++/class_slot.h>
+#include <glibmm/timer.h>
#ifdef DEBUG
# include <iostream>
#endif // DEBUG
cerr << __FILE__ << "(" << __LINE__ << ")"
<< ": destructor." << endl;
#endif // DEBUG
- Glib::Mutex::Lock lock(connections_mutex);
+ // Mando a terminar todas las conexiones.
+ connections_mutex.lock();
for (ConnectionList::iterator con = connections.begin();
con != connections.end(); con++) {
- (*con)->finish(true);
+ (*con)->finish();
+ }
+ ConnectionList::size_type count = connections.size();
+ connections_mutex.unlock();
+ // Espero que terminen realmente.
+ while (count) {
+ Glib::usleep(10000); // 10 milisegundos
+ connections_mutex.lock();
+ count = connections.size();
+ connections_mutex.unlock();
}
}
}
/*void TCPServer::finish(bool attach) {
+#ifdef DEBUG
+ cerr << __FILE__ << "(" << __LINE__ << ")"
+ << ": finish(attach = " << attach << ");" << endl;
+#endif // DEBUG
//socket_mutex.lock();
- socket.shutdown(sockbuf::shut_readwrite);
+ close(socket.sd());
+ //socket.shutdown(sockbuf::shut_readwrite);
//socket_mutex.unlock();
Runnable::finish(attach);
}*/
<< ": real_run()" << endl;
#endif // DEBUG
Connection* connection;
- while (!stop) {
+ while (!stop()) {
// Forma grasa de salir del accept: crear conexion que salga al toque.
try {
connection = new_connection(socket.accept());
} catch (const sockerr& e) { // No se si el accept() puede fallar.
- error(e.serrno(), e.errstr());
+ signal_error().emit(e.serrno(), e.errstr());
continue; // Supongo que puede seguir aceptando conexiones.
}
#ifdef DEBUG
<< ": real_run()." << endl;
#endif // DEBUG
// No hace nada, porque solo actua cuando se manda algo con send().
- while (!stop) {
+ while (!stop()) {
Glib::usleep(500000); // 1/2 segundo
}
}
<< ": send()." << endl;
// << ": send(data = " << data << ")." << endl;
#endif // DEBUG
- if (stop) {
+ if (stop()) {
return;
}
try {
socket << data << flush;
} catch (const sockerr& e) {
- error(e.serrno(), e.errstr());
- stop = true;
+ signal_error().emit(e.serrno(), e.errstr());
+ stop(true);
}
}