package org.dgpf.search.api.utils;
import java.io.File;
import java.io.Serializable;
import org.dgpf.search.api.NonDominatedList;
import org.dgpf.search.api.SearchEngine;
import org.dgpf.search.api.SearchEngineThread;
import org.dgpf.search.api.SearchState;
import org.dgpf.search.api.events.SearchUpdateEvent;
import org.sfc.events.ErrorEvent;
import org.sfc.events.IEventListener;
import org.sfc.events.SfcEvent;
import org.sfc.io.IO;
import org.sfc.io.TextWriter;
import org.sfc.net.Net;
import org.sfc.parallel.events.ParallelStateEvent;
import org.sfc.text.Text;
import org.sfc.utils.Typesafe;
/**
* This thread is used to print the non-dominated elements of a search
* peridically into log-files situated in a specified directory.
*
* @param <Genotype> The genotype of the elements to be printed.
*
* @author Thomas Weise
*/
public class NonDominatedPrinter<Genotype extends Serializable>
extends SearchEngineThread<Genotype>
{
/**
* The default intervall.
*/
public static final long DEFAULT_INTERVALL = (15*60*1000);
/**
* The directory to print the data into.
*/
private final File m_directory ;
/**
* The time intervall for printing the log data, in ms.
*/
private final long m_intervall ;
/**
* The log-file name prefix.
*/
private final String m_prefix ;
/**
* The update count.
*/
long m_count ;
/**
* The update level.
*/
long m_level;
/**
* Create a new non-dominated list printer.
* @param p_directory The directory to print the log files into.
* @param p_intervall The time intervall for printing the log data, in ms.
* If this is -1, a default value will be used.
* @param p_se The search engine to print the data of.
*/
public NonDominatedPrinter (final SearchEngine<Genotype> p_se,
final Object p_directory,
final int p_intervall)
{
super(p_se);
this.m_directory = IO.get_file(p_directory);
this.m_intervall = ((p_intervall > 0) ? p_intervall : DEFAULT_INTERVALL);
this.m_prefix = (Net.LOCAL_HOST.getHostAddress() + '_');
p_se.add_listener(new Listener(p_se));
}
/**
* Store the non-dominated data into a file.
* @param p_writer The writer to write the data to.
* @param p_data The list to be printed.
*/
protected void store_data (final TextWriter p_writer,
final NonDominatedList<Genotype> p_data)
{
p_data.print(p_writer);
}
/**
* Print the data.
*/
private synchronized final void print()
{
SearchEngine<Genotype> l_se;
long l_t, l_c, l_l;
TextWriter l_w;
String l_s, l_n;
NonDominatedList<Genotype> l_ndl;
l_se = this.get_search_engine();
l_t = System.currentTimeMillis();
l_s = Long.toString(l_t % 1000);
while(l_s.length() < 3) l_s = ('0' + l_s);
l_s = (Text.time_to_string(l_t, false) + '_' + l_s);
l_n = (this.m_prefix + l_s.replace(':', '.') + "_non-dominated.txt");
l_c = this.m_count;
l_l = this.m_level;
try
{
l_w = TextWriter.get_text_writer(
(this.m_directory != null) ?
new File(this.m_directory, l_n) : l_n);
if(l_w != null)
{
try
{
l_w.write("Time : ");
l_w.write(l_s);
l_w.write(" (");
l_w.write_long(l_t);
l_w.write(')');
l_w.write_linebreak();
l_w.write("Update : ");
l_w.write_long(l_c);
l_w.write_linebreak();
l_w.write("Globally non dominated:");
l_w.write_linebreak();
l_ndl = l_se.get_non_dominated();
synchronized(l_ndl)
{
this.store_data(l_w, l_ndl);
}
if(l_c != l_l)
{
l_w.write_linebreak();
l_w.write_linebreak();
l_w.write_linebreak();
l_w.write("Level : ");
l_w.write_long(l_l);
l_w.write_linebreak();
l_w.write("Relative non dominated:");
l_w.write_linebreak();
l_ndl = l_se.get_rel_non_dominated();
synchronized(l_ndl)
{
this.store_data(l_w, l_ndl);
}
}
}
finally
{
l_w.release();
}
}
}
catch(Throwable l_tt)
{
this.get_listener().receive(new ErrorEvent(this, l_tt));
}
}
/**
* This method must be overriden to contain the thread's code. It will be
* executed in a loop as long as it returns <code>true</code> and throws
* <b>no</b> <code>Throwable</code>.
* @return <code>true</code> if this method should be executed again.
*/
@Override
protected final boolean do_run ()
{
if(this.safe_sleep(this.m_intervall))
{
this.print();
return this.get_search_engine().is_running();
}
return false;
}
/**
* Override this method to perform something when the thread dies.
*/
@Override
protected void after_termination ()
{
try
{
this.print();
}
finally
{
super.after_termination();
}
}
/**
* Obtain the name of the thread.
* @return A human readable name of the sfc thread.
*/
@Override
public String toString()
{
return "Non-Dominated Individual Printer";
}
/**
* The internal event listener.
*
* @author Thomas Weise
*/
private final class Listener implements IEventListener
{
/**
* The search engine to wait for.
*/
private final SearchEngine<Genotype> m_se;
/**
* Create a new internal listener.
* @param p_se The search engine to print the data of.
*/
Listener (final SearchEngine<Genotype> p_se)
{
super();
this.m_se = p_se;
}
/**
* This method is called when the event sink receives an event.
* @param p_event The event to be received.
*/
public final void receive (final SfcEvent p_event)
{
ParallelStateEvent l_p;
SearchUpdateEvent<Genotype> l_se;
SearchState<Genotype> l_ss;
if(p_event instanceof ParallelStateEvent)
{
l_p = ((ParallelStateEvent)p_event);
if((!(l_p.get_state())) && (l_p.getSource() == this.m_se))
{
NonDominatedPrinter.this.abort();
}
}
else
{
if(p_event instanceof SearchUpdateEvent)
{
l_se = Typesafe.cast(p_event);
l_ss = l_se.get_state();
synchronized(NonDominatedPrinter.this)
{
NonDominatedPrinter.this.m_count = l_ss.get_update_count();
NonDominatedPrinter.this.m_level = l_ss.get_update_level();
}
}
}
}
}
}