context: /*******************************************************************************
  
      Copyright 2009 Whole Foods Co-op
  
      This file is part of IT CORE.
  
      IT CORE is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.
  
      IT CORE is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      in the file license.txt along with IT CORE; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  *********************************************************************************/
  
  /*************************************************************
   * Magellan
   *     Main app. Starts all requested Serial Port Handlers
   * and monitors UDP for messages
   *
   * Note that exit won't work cleanly if a SerialPortHandler
   * blocks indefinitely. Use timeouts in polling reads.
  *************************************************************/
  using System;
  using System.Threading;
  using System.IO;
  using System.Collections.Generic;
  using System.Linq;
  using System.Net.Sockets;
  
  #if NEWTONSOFT_JSON
  using Newtonsoft.Json;
  using Newtonsoft.Json.Linq;
  #endif
  
  #if CORE_RABBIT
  using RabbitMQ.Client;
  #endif
  
  using CustomForms;
  using CustomUDP;
  using SPH;
  using Discover;
  
  public class Magellan : DelegateForm 
  {
      private List<SerialPortHandler> sph;
      private UDPMsgBox u;
  
      private bool mq_enabled = false;
      private bool full_udp = false;
  
      #if CORE_RABBIT
      private bool mq_available = true;
      ConnectionFactory rabbit_factory;
      IConnection rabbit_con;
      IModel rabbit_channel;
      #else
      private bool mq_available = false;
      #endif
  
      // read deisred modules from config file
      public Magellan(int verbosity)
      {
          var d = new Discover.Discover();
          var modules = d.GetSubClasses("SPH.SerialPortHandler");
  
          List<MagellanConfigPair> conf = ReadConfig();
          sph = new List<SerialPortHandler>();
          foreach (var pair in conf) {
              try {
                  if (modules.Any(m => m.Name == pair.module)) {
                      var type = d.GetType("SPH." + pair.module);
                      Console.WriteLine(pair.module + ":" + pair.port);
                      SerialPortHandler s = (SerialPortHandler)Activator.CreateInstance(type, new Object[]{ pair.port });
                      s.SetParent(this);
                      s.SetVerbose(verbosity);
                      sph.Add(s);
                  } else {
                      throw new Exception("unknown module: " + pair.module);
                  }
              } catch (Exception ex) {
                  Console.WriteLine(ex);
                  Console.WriteLine("Warning: could not initialize "+pair.port);
                  Console.WriteLine("Ensure the device is connected and you have permission to access it.");
              }
          }
          MonitorSerialPorts();
          UdpListen();
  
          factorRabbits();
      }
  
      // alternate constructor for specifying
      // desired modules at compile-time
      public Magellan(SerialPortHandler[] args)
      {
          this.sph = new List<SerialPortHandler>(args);
          MonitorSerialPorts();
          UdpListen();
      }
  
      private void factorRabbits()
      {
          #if CORE_RABBIT
          try {
              rabbit_factory = new ConnectionFactory();
              rabbit_factory.HostName = "localhost";
              rabbit_con = rabbit_factory.CreateConnection();
              rabbit_channel = rabbit_con.CreateModel();
              rabbit_channel.QueueDeclare("core-pos", false, false, false, null);
          } catch (Exception) {
              mq_available = false;
          }
          #endif
      }
  
  
      private UdpClient udp_client = null;
      private UdpClient getClient()
      {
          if (udp_client == null) {
              udp_client = new UdpClient();
              udp_client.Connect(System.Net.IPAddress.Parse("127.0.0.1"), 9451);
          }
  
          return udp_client;
      }
  
      private void UdpListen()
      {
          u = new UDPMsgBox(9450);
          u.SetParent(this);
          u.My_Thread.Start();
      }
  
      private void MonitorSerialPorts()
      {
          var valid = sph.Where(s => s != null);
          valid.ToList().ForEach(s => { s.SPH_Thread.Start(); });
      }
  
      public void MsgRecv(string msg)
      {
          if (msg == "exit") {
              this.ShutDown();
          } else if (msg == "full_udp") {
              full_udp = true;
          } else if (msg == "mq_up" && mq_available) {
              mq_enabled = true;
          } else if (msg == "mq_down") {
              mq_enabled = false;
          } else {
              sph.ForEach(s => { s.HandleMsg(msg); });
          }
      }
  
      public void MsgSend(string msg)
      {
          if (full_udp) {
              byte[] body = System.Text.Encoding.UTF8.GetBytes(msg);
              getClient().Send(body, body.Length); 
          } else if (mq_available && mq_enabled) {
              #if CORE_RABBIT
              byte[] body = System.Text.Encoding.UTF8.GetBytes(msg);
              rabbit_channel.BasicPublish("", "core-pos", null, body);
              #endif
          } else {
              string filename = System.Guid.NewGuid().ToString();
              string my_location = AppDomain.CurrentDomain.BaseDirectory;
              char sep = Path.DirectorySeparatorChar;
  
              TextWriter sw = new StreamWriter(my_location + sep + "ss-output/" +sep+"tmp"+sep+filename);
              sw = TextWriter.Synchronized(sw);
              sw.WriteLine(msg);
              sw.Close();
              File.Move(my_location+sep+"ss-output/" +sep+"tmp"+sep+filename,
                    my_location+sep+"ss-output/" +sep+filename);
          }
      }
  
      public void ShutDown()
      {
          try {
              sph.ForEach(s => { s.Stop(); });
              u.Stop();
          }
          catch(Exception ex) {
              Console.WriteLine(ex);
          }
      }
  
      #if NEWTONSOFT_JSON
      private List<MagellanConfigPair> JsonConfig()
      {
          string my_location = AppDomain.CurrentDomain.BaseDirectory;
          char sep = Path.DirectorySeparatorChar;
          string ini_file = my_location + sep + ".." + sep + ".." + sep + ".." + sep + "ini.json";
          List<MagellanConfigPair> conf = new List<MagellanConfigPair>();
          if (!File.Exists(ini_file)) {
              return conf;
          }
  
          try {
  
gt:             string ini_json = File.ReadAllText(ini_file);