/*------------------------------------------------------------------------- // UNCLASSIFIED /*------------------------------------------------------------------------- RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON COMPANY. THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S. GOVERNMENT. UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY. -------------------------------------------------------------------------*/ using System; using System.Collections.Generic; using System.ComponentModel.Composition; using Raytheon.Communication; using Raytheon.Communication.Rpc; using Raytheon.Logging; using Raytheon.Composition; using Raytheon.Common; using System.Xml.Linq; using Microsoft.Win32; using System.IO; using System.ServiceProcess; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections.ObjectModel; using System.Reflection; using System.IO.Ports; namespace Raytheon.Instruments { public enum Mode { /// /// instruments must be managed by the service (like RINSS) /// Service, /// /// ignore the service and initialize instruments directly /// StandAlone, /// /// dynamically identify if instrument service is running /// Auto } /// /// hybrid implementation of the instrument manager interface /// will check if /// public class GeneralInstrumentManager : IInstrumentManager, IPartImportsSatisfiedNotification { #region Private Fields private readonly bool _haveService; private IUms _umsHost; private IUmsClient _instrumentManager; private IUmsClient _rpcInstrumentManagerHost; private bool _partsLoaded { get; set; } /// /// PartsLocation - where the instrument manager should get it's MEF components /// public string _partsLocation { get; set; } /// /// ConfigLocation - where the configuration manager stores config files /// specifically Instruments.xml /// public string _configLocation { get; set; } private List _availableInstruments = new List(); private readonly Dictionary _factoryMap = new Dictionary(); private readonly Dictionary _instruments = new Dictionary(); private readonly HashSet _instrumentTypes = new HashSet(); // simulation private readonly bool _isThereHardware; #endregion #region Constants private const string NO_SERVER = "Client for communication to the server has not been setup"; private const string SECTION = "RpcClient"; private const string RegistryValue = @"ConsumerInstrumentManagerPartsDirectory"; private const string RegistryValueNoRINSS = @"InstrumentManagerPartsDirectory"; private const string RegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Raytheon"; private const string DefaultName = "Default"; private const string DefaultIP = "127.0.0.1"; private const string DefaultPort = "8006"; private const string DefaultMedia = "Tcp"; private const string DefaultSerializer = "Fusion"; #endregion #region Imports [ImportMany(typeof(IInstrumentFactory))] private Lazy[] InstrumentFactories { get; set; } [Import(typeof(IUmsFactory))] private Lazy LazyUmsFactory { get; set; } private IUmsFactory UmsFactory { get { return LazyUmsFactory?.Value; } } [Import(typeof(IConfigurationManager))] private Lazy LazyConfigManager { get; set; } private IConfigurationManager ConfigManager { get { return LazyConfigManager?.Value; } } [Import(typeof(IUmsClientFactory))] private Lazy LazyUmsClientFactory { get; set; } private IUmsClientFactory UmsClientFactory { get { return LazyUmsClientFactory?.Value; } } [Import(typeof(ILogFactory), AllowDefault = true)] private Lazy LazyLogFactory { get; set; } private ILogFactory LogFactory { get { return LazyLogFactory?.Value; } } [ImportMany(typeof(IInstrumentProxyFactory))] private Lazy[] ProxyFactories { get; set; } #endregion #region Logging private static ILogger _logger; private static ILogger GetLogger() { FusionLogManager.Changed += l => _logger = l.GetLogger(); return _logger; } #endregion /// /// default constructor /// in Auto mode the Instrument Manager will use RINSS based on the service running status /// in Service mode the Instrument Manager must have the RINSS Service running /// in StandAlone mode the Instrument Manager will ignore RINSS /// public GeneralInstrumentManager(Mode mode = Mode.Auto) { _haveService = CheckServiceRunningStatus("RINSS"); if (mode == Mode.Service && !_haveService) { throw new Exception("RINSS Service is not running for Service mode"); } if (mode == Mode.StandAlone) { _haveService = false; } _logger = GetLogger(); } /// /// constructor to be used when no RINSS available to set parts location /// /// public GeneralInstrumentManager(string partsLocation, Mode mode = Mode.Auto) : this(mode) { _partsLocation = partsLocation; } /// /// constructor to be used when no RINSS available to set parts location and configuration location /// /// /// /// public GeneralInstrumentManager(string partsLocation, string configLocation, bool isThereHardware = true, Mode mode = Mode.Auto) : this(partsLocation, mode) { _configLocation= configLocation; _isThereHardware = isThereHardware; } /// /// Initializes this instance. /// public void Initialize() { try { //Setup the instrument part path if (!string.IsNullOrWhiteSpace(_partsLocation) && !Directory.Exists(_partsLocation)) { _logger.Error($"Unable to id parts in this location: {_partsLocation}"); _partsLocation = string.Empty; } SetupPath(); //load the instruments if (!_partsLoaded) { LoadParts(); } if(_haveService) { // 1. Create the Ums Client CreateUmsClient(); _logger.Debug("created client"); // 2. Find out what instruments are available _availableInstruments = new List(_rpcInstrumentManagerHost.Contract.EnumRpcInstruments()); _logger.Debug("geting list of availible instruments"); // 3. Find all the instrument interfaces supported by proxy factories InitializeFactories(); _logger.Debug("initialized all the factories"); } else { //configure all the instruments found ConfigureInstruments(); } } catch (CompositionException) { throw; } catch (Exception) { throw; } } /// /// InitializeInstruments - init all the instruments /// public void InitializeInstruments() { _logger.Info("Instrument initialization complete"); } /// /// InitializeInstrument - inits a specific instrument /// /// instrument's unique name public void InitializeInstrument(string instName) { } /// /// implementation for IPartImportsSatisfiedNotification interface /// public void OnImportsSatisfied() { if (LogFactory != null && LogFactory != null) { FusionLogManager.Current = LogFactory; } } /// /// Gets the generic instrument. /// /// The name. /// public IInstrument GetGenericInstrument(string name) { _logger.Trace("In ConsumerInstrumentManager in method GetGenericInstrument with name: {0} ", name); return GetInstrument(name); } /// /// gets a specific instrument by name /// the name should match one of the names in Instruments.xml file /// /// /// /// public T GetInstrument(string name) where T : class { _logger.Trace($"Starting GetInstrument with name: {name}"); object inst = null; if (_haveService) { try { string interfaceName = typeof(T).FullName; if (typeof(IInstrument).FullName == interfaceName) { _logger.Trace($"GetInstrument with typeof(IInstrument).FullName == interfaceName: {interfaceName}"); //get the real interface behind the scenes //first find the appropriate factory RpcInstrumentDescriptor correctDesc = _availableInstruments.FirstOrDefault((desc) => 0 == string.Compare(name, desc.InstrumentName, true)); if (null != correctDesc) { _logger.Trace($"GetInstrument with correctDesc.name: {correctDesc.InstrumentName}"); string temp = correctDesc.InstrumentInterfaces.FirstOrDefault(); if (!string.IsNullOrWhiteSpace(temp)) { interfaceName = temp; } } _logger.Debug("Requested generic instrument, found {0} to be the correct interface", interfaceName); } if (InstrumentIsAvailable(name) && FactoryIsAvailable(interfaceName)) { _logger.Trace($"GetInstrument with InstrumentIsAvailable(name) && FactoryIsAvailable(interfaceName) name: {name}, interfaceName: {interfaceName}"); IInstrumentProxyFactory factory = _factoryMap.Where((t) => 0 == string.Compare(t.Key, interfaceName, true)) .Select(t => t.Value) .FirstOrDefault(); if (null != factory) { inst = factory.GetInstrument(name); _logger.Trace($"GetInstrument got an instrument (name: {name}) from factory: {factory}, interfaceName: {interfaceName}"); } else { _logger.Warn($"Could not find factory for interface: {interfaceName}, instrument: {name}"); } } } catch (InstrumentException ex) { _logger.WarnException(ex, ex.Message); } } else { _instruments.TryGetValue(name.ToLower(), out inst); } _logger.Trace($"GetInstrument returning with inst: {inst}"); return inst as T; } /// /// returns a collection of instrument names /// /// public ICollection GetInstrumentNames() { _logger.Trace("Returning instrument list"); if (_haveService) { return _instrumentManager.Contract.GetInstrumentNames(); } else { return new ReadOnlyCollection(_instruments.Keys.ToList()); } } public string[] GetInstrumentNamesArray() { _logger.Debug("Getting Instrument Names Array"); return GetInstrumentNames().ToArray(); } /// /// Gets instruments collection /// /// public ICollection GetInstruments() { _logger.Debug("GetInstruments with null"); return GetInstruments(null); } /// /// Gets instruments collection by type /// /// /// public ICollection GetInstruments(Type type) { _logger.Debug($"Entering GetInstruments with type {type}"); var instruments = new List(); if (_haveService) { if (null == _rpcInstrumentManagerHost) { _logger.Warn(NO_SERVER); } else { //gather all the instruments of the requested type if (null == type) { type = typeof(IInstrument); } _logger.Debug("GetInstruments geting collection"); var collection = from avail in _availableInstruments where avail.InstrumentInterfaces.Contains(type.FullName) select avail; _logger.Debug("GetInstruments got collection count = " + collection.Count()); MethodInfo method = typeof(GeneralInstrumentManager).GetMethod("GetInstrument"); MethodInfo generic = method.MakeGenericMethod(type); foreach (var item in collection) { object[] objs = { item.InstrumentName }; IInstrument created = generic.Invoke(this, objs) as IInstrument; instruments.Add(created); } } } else { if(type == null) { instruments.AddRange(_instruments.Values); } else { foreach (var inst in _instruments.Values) { if (type.IsAssignableFrom(inst.GetType())) { instruments.Add(inst); } } } } return new ReadOnlyCollection(instruments); } /// /// returns instrument array of specific type /// /// /// /// public object[] GetInstrumentsArray(Type type) { if (null == type) { throw new ArgumentNullException("type", "GetInstrumentsArray, cannot get null instrument types"); } _logger.Debug($"GetInstrumentsArray with type {type}"); return GetInstruments(type).ToArray(); } /// /// returns instrument array /// /// public object[] GetInstrumentsArray() { _logger.Debug("Get Instruments Array"); return GetInstruments().ToArray(); } /// /// Shuts down this instance. /// Close out the RPC host connection. Does not close / shutdown RINSS or any /// public void Shutdown() { _logger.Info("Shutting down instruments"); if (_haveService) { _rpcInstrumentManagerHost.Close(); } else { ShutdownInstruments(); } _logger.Info("Instrument shut down complete"); } /// /// Shuts down the instruments. /// public void ShutdownInstruments() { } /// /// Shutdowns the instrument. /// /// Name of the inst. public void ShutdownInstrument(string instName) { } #region Private Functions /// /// for the Auto mode will check if RINSS available /// /// /// private bool CheckServiceRunningStatus(string serviceName) { if(!DoesServiceExist(serviceName)) return false; ServiceController sc = new ServiceController(serviceName); return sc.Status == ServiceControllerStatus.Running; } private static bool DoesServiceExist(string serviceName) { return ServiceController.GetServices().Any(serviceController => serviceController.ServiceName.Equals(serviceName)); } /// /// Load parts with RINSS running or without RINSS /// private void LoadParts() { _logger.Debug($"Loading Parts from path {_partsLocation}"); //install we are assuming will put the needed parts into a directory ./Parts just off the //directory where the assembly is MefHelper helper = new MefHelper(false, false); try { helper.AddCatalog(_partsLocation, true); helper.Container.ComposeParts(this); if(_haveService) { if (null == ConfigManager || null == UmsClientFactory || null == UmsFactory) { throw new CompositionException($"General Instrument Manager has null MEF components, please check MEF plugin location: {_partsLocation}"); } } else { if (null == ConfigManager || null == InstrumentFactories) { throw new CompositionException($"Error during MEF composition, check MEF Part Location: {_partsLocation}"); } } _partsLoaded = true; if(!string.IsNullOrEmpty(_configLocation)) ConfigManager.ConfigurationStoragePath = _configLocation; } catch (CompositionException ex) { _logger.ErrorException(ex, ex.Message); } } /// /// will set up path where either proxy instruments are (in case when RINSS running) /// or where the actual instruments are (in case RINSS not running) /// /// /// /// /// private void SetupPath() { _logger.Trace("Setting up Instrument Manager parts location"); if (string.IsNullOrEmpty(_partsLocation)) { string pf86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); string pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); //ATEasy and other apps are messing with the previously used ./Parts locations //so we have moved to using a registry entry that is created on install and refers //to the directory installed to, the PartsLocation property still allows for custom directories //to be used for the Consumer Instrument Manager string defaultPath = _haveService ? $"{pf86}\\Raytheon\\RINSS-ConsumerIM\\Parts" : $"{pf86}\\Raytheon\\RINSS\\Parts\\CommonModuleInstruments"; if (Environment.OSVersion.Version.Major < 6) { //if we are windows xp, then we need to look in a different default directory defaultPath = _haveService ? $"{pf}\\Raytheon\\RINSS-ConsumerIM\\Parts" : $"{pf}\\Raytheon\\RINSS\\Parts\\CommonModuleInstruments"; } var keyName = RegistryKey; var valueName = _haveService ? RegistryValue : RegistryValueNoRINSS; string path = (string)Registry.GetValue(keyName, valueName, defaultPath); if (string.IsNullOrWhiteSpace(path) || !Directory.Exists(path)) { path = defaultPath; } _partsLocation = path; } _logger.Trace($"Mef parts load Location is: {_partsLocation}"); } /// /// creates Umc client used in remote procedure calls /// /// private void CreateUmsClient() { _logger.Trace("Loading Proxy Instrument Configuration Settings"); string configName = "ProxyInstruments"; IConfiguration config = ConfigManager.GetConfiguration(configName); if (null == config) { string errorMessage = string.Format(@"Could not get configuration file, check configuration location for file: {0}", configName); _logger.Error(errorMessage); throw new ArgumentNullException(errorMessage); } string rpcName = config.GetConfigurationValue(SECTION, "Name", string.Empty); //if the configuration element does not exist, then create the defaults if (string.IsNullOrWhiteSpace(rpcName)) { _logger.Info("No Rpc Client Instrument Manager configuration found, creating default"); config.SetConfigurationValue(SECTION, "Name", DefaultName); config.SetConfigurationValue(SECTION, "IPAddress", DefaultIP); config.SetConfigurationValue(SECTION, "Port", DefaultPort); config.SetConfigurationValue(SECTION, "MediaType", DefaultMedia); config.SetConfigurationValue(SECTION, "SerializerType", DefaultSerializer); } _logger.Trace("Consumer Instrument Manager is creating Ums Client"); if (null == UmsClientFactory || null == UmsFactory) { var ex = new ArgumentNullException(string.Format("Ums components are null, please check MEF plugin location: {0}", _partsLocation)); _logger.ErrorException(ex, ex.Message); throw ex; } string name = config.GetConfigurationValue(SECTION, "Name", DefaultName); string ipAddress = config.GetConfigurationValue(SECTION, "IPAddress", DefaultIP); string hostPort = config.GetConfigurationValue(SECTION, "Port", DefaultPort); string mediaType = config.GetConfigurationValue(SECTION, "MediaType", DefaultMedia); string serializerType = config.GetConfigurationValue(SECTION, "SerializerType", DefaultSerializer); //we already checked for null above! _umsHost = UmsFactory.GetInstance(name, ipAddress, hostPort, UmsInstanceType.Client, mediaType, serializerType); //open a client for the instrument manager _instrumentManager = UmsClientFactory.GetClient(_umsHost); _instrumentManager.Open(); //open a client for the host server _rpcInstrumentManagerHost = UmsClientFactory.GetClient(_umsHost); _rpcInstrumentManagerHost.Open(); } /// /// will initialize factories for instuments managed under RINSS /// private void InitializeFactories() { _logger.Debug($"InitializeFactories ProxyFactories count = {ProxyFactories.Count()}"); foreach (var fact in ProxyFactories) { _logger.Debug("InitializeFactories fact=" + fact.Value); //initialize each factory so it can create the clients fact.Value.Initialize(_umsHost, UmsClientFactory); //map out the factories with their interface names var availableInterfaces = fact.Value.GetSupportedInterfaces(); _logger.Debug($"InitializeFactories availableInterfaces count = {availableInterfaces.Count}"); foreach (var face in availableInterfaces) { _logger.Debug($"InitializeFactories Interface = {face}"); _factoryMap.Add(face.ToLower(), fact.Value); } } } /// /// will configure instruments when not managed by Service /// /// private void ConfigureInstruments() { _logger.Info("Configuring Instruments"); IConfiguration config = ConfigManager.GetConfiguration("Instruments"); if (null == config) { throw new ConfigurationException("could not find configuration file"); } string rawConfig = config.GetXmlConfiguration("Instruments"); if (string.IsNullOrWhiteSpace(rawConfig)) { _logger.Warn("No RPC configuration found, creating default 'Instruments'"); config.SetXmlConfiguration("Instruments", new XElement("Instrument", new XElement("Name", "Sample"), new XElement("Factory", "Sample")).ToString()); rawConfig = config.GetXmlConfiguration("Instruments"); } _instrumentTypes.Add(typeof(IInstrument)); if (0 == InstrumentFactories.Count()) { _logger.Warn($"There are no instrument factories registered using the following path: {Path.GetFullPath(_partsLocation)}"); } XElement xmlConfig = XElement.Parse(rawConfig); if (null == xmlConfig) { throw new ConfigurationException("could not parse configuration file"); } var decendants = xmlConfig.Descendants("Instrument"); if (null != decendants) { foreach (var instrument in decendants) { var instName = instrument.Element("Name").Value; var instFactory = instrument.Element("Factory").Value; _logger.Trace($"In ConfigureInstruments before factory call with XML name: {instName} and XML factory: {instFactory}"); var factory = (from f in InstrumentFactories where !string.IsNullOrWhiteSpace(f.Metadata.ModelNumber) && f.Metadata.ModelNumber.Equals(instFactory, StringComparison.OrdinalIgnoreCase) select f).FirstOrDefault(); if (factory != null) { object inst = null; try { // instantiate the applicable instrument inst = factory.Value.GetInstrument(instName, !_isThereHardware); _logger.Info($"Creating instrument '{instName}'"); } catch (Exception) { throw; } if (null == inst) { _logger.Warn($"Could not create the instrument: {instName}"); } else { var supported = factory.Value.GetSupportedInterfaces(); if (null != supported) { //add the instrument string txt = $"Adding instrument: Name - {instName}, Type - {inst.GetType()}"; _instruments.Add(instName.ToLower(), inst); foreach (var supportedInterface in supported) { _instrumentTypes.Add(supportedInterface); _logger.Info($"{txt}, Interface - {supportedInterface}"); } } else { _logger.Warn($"Did not see any supported interfaces for: {instName}"); } } } else { if (InstrumentFactories.Count() > 0) { var factories = new StringBuilder(); factories.Append("Available Factory types loaded: "); foreach (var kvp in InstrumentFactories) { factories.AppendLine(); factories.AppendFormat(" {0}", kvp.Metadata.ModelNumber); } _logger.Info(factories.ToString()); } throw new Exception($"{instName} did not have a matching factory with supported type {instFactory}"); } } } else { _logger.Warn("could not find instrument section of configuration"); } } /// /// checks if instrument is available /// /// /// private bool InstrumentIsAvailable(string name) { //check if our host has a instrument by this name available to us if (!_availableInstruments.Any(t => 0 == string.Compare(t.InstrumentName, name, true))) { _logger.Warn($"{name} instrument not available"); return false; } return true; } /// /// checks if instrument factory is available /// /// /// private bool FactoryIsAvailable(string interfaceName) { bool bReturn = false; if (typeof(IInstrument).FullName == interfaceName) { _logger.Debug($"In GeneralInstrumentManager in method FactoryIsAvailable with typeof(IInstrument).FullName == interfaceName : {interfaceName}"); bReturn = true; } else { //check if we have a factory that supports this interface var sb = new StringBuilder(); foreach (KeyValuePair x in _factoryMap) { sb.Append("FactoryMap IInstrumentProxyFactory: key = "); sb.Append(x.Key); sb.Append(" value = "); sb.Append(x.Value); sb.Append(" supportedInterfaces = "); sb.Append(x.Value.GetSupportedInterfaces()); sb.Append(Environment.NewLine); } _logger.Debug(sb.ToString()); var kvp = _factoryMap.Where((t) => 0 == string.Compare(t.Key, interfaceName, true)) .Select(t => new { t.Key, t.Value }) .FirstOrDefault(); if (null == kvp) { _logger.Warn($"{interfaceName} interface proxy factory not found"); } bReturn = kvp != null; } return bReturn; } #endregion } }