//     $Id: ServerThread.java,v 3.10 1998/12/14 20:18:03 smg9c Exp $    
 /////////////////////////////////////////////////////////////////////
 //
 // File: ServerThread.java
 //
 // Purpose: The ServerEngine class is responsible for manipulating the
 //          master map and sending it out to the clients.
 //
 // Authors: Steve Geist smg9c@cs.virginia.edu
 // 
 // Modifications:
 //
 //   14-DEC-1998 smg  Fixed "sum" propagation
 //
 //   11-DEC-1998 smg  Removed annoying print messages about stable state and
 //                    new map sent to all
 //
 //   09-DEC-1998 smg  Fixed double send of final stats and send of tick 0 stats
 //
 //   09-DEC-1998 smg  Added a final send of map and stats for each iteration
 //                    in automatic mode
 //
 //   08-DEC-1998 smg  Removed code for use with only one thread
 //
 //   04-DEC-1998 smg  Changes made to run with auto and manual modes.
 //
 //   01-DEC-1998 smg  Added automatic destabilization option
 //
 //   01-DEC-1998 smg  Added file writes after each propagation
 //
 //   23-NOV-1998 smg  Added GenerateInitialStats method and a call to it to
 //                    send out stats at the beginning of the stabilization
 //                    round
 //
 //   20-NOV-1998 smg  Changed prop methods to use new GetDepends method
 //
 //   20-NOV-1998 smg  Fixed prop method "sum" error
 //
 //   20-NOV-1998 smg  Fixed -1 exception.
 //
 //   19-NOV-1998 smg  Added Error method call to handle exceptions, fixed
 //                    "all" prop method.
 //
 //   18-NOV 1998 smg  Added check to see if node on other end of depend is 
 //                    unstable before trying to pass on instability  
 //
 //   17-NOV-1998 smg  Added instability propagation methods, added delay
 //                    and propagation_method variables, added SendMap and
 //                    SendStats methods, added statistics gathering
 //
 //   09-NOV-1998 smg  Added comments and thread sleep command
 //
 //   06-NOV-1998 smg  Initial creation
 /////////////////////////////////////////////////////////////////////
 import java.util.*;
 import java.io.*;
 public class ServerThread extends Thread {
   private ServerRMI rmi;          // RMI connection for sending map
   private ServerEngine engine;    // ServerEngine necessary for access to
                                   // current map and number of clients
   private Map map;                // The current system Map
   private long delay;             // Delay in milliseconds between sending
                                   // maps to all clients
   private int propagation_method; // Indicates the propagation method to be
                                   // used for unstable nodes
   private Random generator;       // Random number generator
   private int num_clients;        // Number of clients in the system
   private Stats local_stats;      // Local statistics
   private Stats global_stats;     // Global statistics
   private int[] recoveries;       // Recoveries made in each module this turn
   private String map_file;        // Name for saving the map to a file
   private int map_file_count;     // Int for appending to map file name
   private int mode;               // Indicates auto or manual mode
   private int iterations;         // Indicates how many iterations to run in
                                   // auto mode
   private final int automatic_mode = 0;   // Auto mode
   private final int manual_mode = 1;      // Manual mode
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  ServerThread [constructor]
   //
   // Invoker: ServerEngine
   //
   // Purpose: Creates a new ServerThread generating the necessary values
   //          for private variables from the passed ServerEngine.
   //   
   /////////////////////////////////////////////////////////////////// 
   public ServerThread(ServerRMI r, ServerEngine se) {
     rmi = r;
     engine = se;
     num_clients = se.GetNumClients();
     Date d = new Date();
     generator = new Random(d.getTime());
     map = se.GetMap();
     try {
       local_stats = new Stats(num_clients);
       global_stats = new Stats(num_clients);
     }
     catch (Exception e) {
       engine.Error(e);
     }
     delay = se.GetDelay();
     propagation_method = se.GetPropagationMethod();
     recoveries = new int[num_clients];
     for(int i=0;i<num_clients;i++) {
       recoveries[i] = 0;
     }
     map_file = "smap";
     map_file_count = 1;
     mode = se.GetMode();
     iterations = se.GetIterations();
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  run
   //
   // Invoker: ServerEngine (as start())
   //
   // Purpose: The run method sends out the modified map to all clients, 
   //          stabilizes the map through a series of propagations and
   //          spontaneous node stabilizations, generates statistics, and 
   //          sends out each of the intermediate maps and stats to all 
   //          clients and the demon.
   //   
   ///////////////////////////////////////////////////////////////////
   public void run() {
     try {
       map.SetFinal(false);
       for(int j=0; j<iterations; j++) {
     if(mode == automatic_mode) {      // If mode is automatic
       map.SetStable(false);           // destabilize map
     }
     local_stats.Reset();
     GenerateInitialStats();
     while(true) {
       if(map.IsStable()) {
         SendMap();
         SendLocalStats();
         break;
       }
       SendMap();
       SendLocalStats();
       WriteMap();
       // Pause to let all clients view new map
       sleep(delay);
       AttemptRecovery();
       PropagateInstabilities();
     }
       }
       // Send out stable map to all clients
       map.SetFinal(true);
       SendMap();
       engine.SetThreadRunning(false);
       rmi.RunEnded();
       if(mode == automatic_mode) {
     SendGlobalStats();
       }
       stop();
     }
     catch (Exception e) {
       engine.Error(e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  SendMap
   //
   // Invoker: (internal)
   //
   // Purpose: SendMap sends the current map to all clients and the demon.
   //   
   ///////////////////////////////////////////////////////////////////
   private void SendMap() {
     // Send out map to all clients
     try {
       for(int i=0; i<num_clients; i++) {
     rmi.SendMapClient(map, i);
       }
       rmi.SendMapDemon(map);
     }
     catch (Exception e) {
       engine.Error(e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  SendLocalStats
   //
   // Invoker: (internal)
   //
   // Purpose: SendStats sends the current local stats to all clients and the
   //          demon.
   //   
   ///////////////////////////////////////////////////////////////////
   private void SendLocalStats() {
     // Send out stats to all clients
     try {
       for(int i=0; i<num_clients; i++) {
     rmi.SendStatsClient(local_stats, i);
       }
       rmi.SendStatsDemon(local_stats);
     }
     catch (Exception e) {
       engine.Error(e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  SendGlobalStats
   //
   // Invoker: (internal)
   //
   // Purpose: SendStats sends the current global stats to all clients and the
   //          demon.
   //   
   ///////////////////////////////////////////////////////////////////
   private void SendGlobalStats() {
     // Send out stats to all clients
     try {
       for(int i=0; i<num_clients; i++) {
     rmi.SendStatsClient(global_stats, i);
       }
       rmi.SendStatsDemon(global_stats);
     }
     catch (Exception e) {
       engine.Error(e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  WriteMap
   //
   // Invoker: (internal)
   //
   // Purpose: WriteMap writes the map out to a file.
   //   
   ///////////////////////////////////////////////////////////////////
   private void WriteMap()
        throws Exception {
     File map_output = new File(map_file + map_file_count + ".txt");
     DataOutputStream dos = new DataOutputStream(new FileOutputStream(map_output));
     dos.writeBytes(map.ToSerial());
     dos.close();
     map_file_count++;
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  GenerateInitialStats
   //
   // Invoker: (internal)
   //
   // Purpose: GenerateInitialStats sets the unstable_nodes values to the
   //          correct numbers.
   //   
   ///////////////////////////////////////////////////////////////////
   private void GenerateInitialStats() {
     Module m;
     Node n;
     int num_nodes;
     int num_unstable = 0;
     try {
       for(int i=0;i<num_clients;i++) {
     m = map.GetModule(i);
     num_unstable = 0;
     num_nodes = m.GetNumNodes();
     for(int j=0;j<num_nodes;j++) {
       n = m.GetNode(j);
       if(!n.IsStable()) {
         num_unstable++;
       }
     }
     local_stats.LogTick(i, 0, num_unstable, num_unstable);
     if(mode == automatic_mode) {
       global_stats.LogTick(i, 0, num_unstable, num_unstable);
     }
       }
     }
     catch (Exception e) {
       engine.Error(e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  AttemptRecovery
   //
   // Invoker: (internal)
   //
   // Purpose: AttemptRecovery takes each node in the map and make it stable
   //          with probability equal to the value of the recovery variable
   //          of that node.
   //   
   ///////////////////////////////////////////////////////////////////
   private void AttemptRecovery() {
     int num_nodes;
     Module m;
     Node n;
     double recovery;
     int num_recoveries;
     ResetRecoveries();
     try {
       for(int i=0;i<num_clients;i++) {
     num_recoveries = 0;
     m = map.GetModule(i);
     num_nodes = m.GetNumNodes();
     for(int j=0;j<num_nodes;j++) {
       n = m.GetNode(j);
       if(!n.IsStable()) {
         recovery = n.GetRecovery();
         if(generator.nextDouble() < recovery) {
           n.SetStable(true);
           num_recoveries++;
         }
       }
     }
     recoveries[i] = num_recoveries;
       }
     }
     catch (Exception e) {
       engine.Error(e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  ResetRecoveries
   //
   // Invoker: (internal)
   //
   // Purpose: ResetRecoveries resets the recoveries array to all 0's.
   //   
   ///////////////////////////////////////////////////////////////////
   private void ResetRecoveries() {
     for(int i=0;i<recoveries.length;i++) {
       recoveries[i] = 0;
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  PropagateInstabilities
   //
   // Invoker: (internal)
   //
   // Purpose: PropagateInstabilities uses to given propagation method to
   //          call the correct propagation function.
   //   
   ///////////////////////////////////////////////////////////////////
   private void PropagateInstabilities()
   throws Exception {
     switch(propagation_method) {
     case 1:
       PropInstabilitiesAny();
       break;
     case 2:
       PropInstabilitiesSum();
       break;
     case 3:
       PropInstabilitiesAll();
       break;
     default:
       engine.PrintMessage("Invalid propagation method variable");
       break;
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  PropInstabilitiesAny
   //
   // Invoker: (internal)
   //
   // Purpose: PropInstabilitiesAny propagates instabilities throughout 
   //          the map.  A node becomes unstable if any of its dependencies 
   //          fire.
   //   
   ///////////////////////////////////////////////////////////////////
   private void PropInstabilitiesAny()
   throws Exception {
     Map old_map = new Map(map);
     Module m;
     Module current_module;
     Node n;
     Node current_node;
     int num_nodes;
     int num_destabilizations;
     int num_unstable;
     Depend[] dependencies;
     double prob = 0.0;
     // This loop propagates instabilities
     for(int mod_num=0;mod_num<num_clients;mod_num++) {
       m = map.GetModule(mod_num);
       num_nodes = m.GetNumNodes();
       num_destabilizations = 0;
       num_unstable = 0;
       for(int node_num=0;node_num<num_nodes;node_num++) {
     n = m.GetNode(node_num);
     // Only try to destabilize if the node is currently stable
     if(n.IsStable()) {
       // Get dependencies for this node
       dependencies = map.GetDepends(mod_num, node_num, true);
       // Check each dependency
       for(int j=0;j<dependencies.length;j++) {
         current_module = old_map.GetModule(dependencies[j].GetSourceModuleID());
         current_node = current_module.GetNode(dependencies[j].GetSourceNodeID());
         if(!current_node.IsStable()) {
           prob = dependencies[j].GetProbability();
           // If the dependency fires...
           if(generator.nextDouble() < prob) {
         // set node unstable
         n.SetStable(false);
         num_destabilizations++;
         // End for loops
         j = dependencies.length;
           }
         }
       }
     }
     if(!n.IsStable()) {
       num_unstable++;
     }
       }
       local_stats.LogTick(mod_num, recoveries[mod_num], num_destabilizations,
             num_unstable);
       if(mode == automatic_mode) {
     global_stats.LogTick(mod_num, recoveries[mod_num],
                  num_destabilizations, num_unstable);
       }
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  PropInstabilitiesSum
   //
   // Invoker: (internal)
   //
   // Purpose: PropInstabilitiesSum propagates instabilities throughout the 
   //          map.  A node becomes unstable if the inverse of the product 
   //          of the inverses of the dependencies fire.
   //   
   ///////////////////////////////////////////////////////////////////
   private void PropInstabilitiesSum()
   throws Exception {
     Map old_map = new Map(map);
     Module m;
     Module current_module;
     Node n;
     Node current_node;
     int num_nodes;
     int num_destabilizations;
     int num_unstable;
     Depend[] dependencies;
     double prob = 0.0;
     double inverse = 0.0;
     double node_prob = 0.0;
     // This loop propagates instabilities
     for(int mod_num=0;mod_num<num_clients;mod_num++) {
       m = map.GetModule(mod_num);
       num_nodes = m.GetNumNodes();
       num_destabilizations = 0;
       num_unstable = 0;
       for(int node_num=0;node_num<num_nodes;node_num++) {
     n = m.GetNode(node_num);
     prob = 0.0;
     // Only try to destabilize if the node is currently stable
     if(n.IsStable()) {
       // Get dependencies for this node
       dependencies = map.GetDepends(mod_num, node_num, true);
       // Check each dependency
       for(int j=0;j<dependencies.length;j++) {
         current_module = old_map.GetModule(dependencies[j].GetSourceModuleID());
         current_node = current_module.GetNode(dependencies[j].GetSourceNodeID());
         if(!current_node.IsStable()) {
           node_prob = dependencies[j].GetProbability();
           inverse = 1 - node_prob;
           // Multiply this inverse by current product
           if(prob == 0.0) {
         prob = inverse;
           }
           else {
         prob = prob * inverse;
           }
         }
       }
       // Take the inverse of the product of the inverses
       if(prob != 0.0) {
         prob = 1 - prob;
       }
       // If the dependency fires...
       if(generator.nextDouble() < prob) {
         // set node unstable
         n.SetStable(false);
         num_destabilizations++;
       }
     }
     if(!n.IsStable()) {
       num_unstable++;
     }
       }
       local_stats.LogTick(mod_num, recoveries[mod_num], num_destabilizations,
             num_unstable);
       if(mode == automatic_mode) {
     global_stats.LogTick(mod_num, recoveries[mod_num],
                  num_destabilizations, num_unstable);
       }
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  PropInstabilitiesAny
   //
   // Invoker: (internal)
   //
   // Purpose: PropInstabilitiesAll propagates instabilities throughout the 
   //          map.  A node becomes unstable only if all of its dependencies 
   //          fire.
   //   
   ///////////////////////////////////////////////////////////////////
   private void PropInstabilitiesAll()
   throws Exception {
     Map old_map = new Map(map);
     Module m;
     Module current_module;
     Node n;
     Node current_node;
     int num_nodes;
     int num_destabilizations;
     int num_unstable;
     Depend[] dependencies;
     double prob = 0.0;
     boolean unstable_depend;        // Indicates whether this node has an
     // unstable dependecy
     boolean all_fired;              // Indicates whether all dependencies 
     // fired for the current node
     // This loop propagates instabilities
     for(int mod_num=0;mod_num<num_clients;mod_num++) {
       m = map.GetModule(mod_num);
       num_nodes = m.GetNumNodes();
       num_destabilizations = 0;
       num_unstable = 0;
       for(int node_num=0;node_num<num_nodes;node_num++) {
     n = m.GetNode(node_num);
     // Only try to destabilize if the node is currently stable
     if(n.IsStable()) {
       unstable_depend = false;
       all_fired = true;
       // Get dependencies for this node
       dependencies = map.GetDepends(mod_num, node_num, true);
       // Check each dependency
       for(int j=0;j<dependencies.length;j++) {
         current_module = old_map.GetModule(dependencies[j].GetSourceModuleID());
         current_node = current_module.GetNode(dependencies[j].GetSourceNodeID());
         if(!current_node.IsStable()) {
           unstable_depend = true;
           prob = dependencies[j].GetProbability();
           // If the dependency doesn't fire...
           if(generator.nextDouble() > prob) {
         all_fired = false;
         // Terminate for loops
         j = dependencies.length;
           }
         }
       }
       // Check to see if node should be destabilized
       if(unstable_depend && all_fired) {
         n.SetStable(false);
         num_destabilizations++;
       }
     }
     if(!n.IsStable()) {
       num_unstable++;
     }
       }
       local_stats.LogTick(mod_num, recoveries[mod_num], num_destabilizations,
             num_unstable);
       if(mode == automatic_mode) {
     global_stats.LogTick(mod_num, recoveries[mod_num],
                  num_destabilizations, num_unstable);
       }
     }
   }
 }