Beispiel #1
0
        public static void Main(string[] _)
        {
            try
            {
                Console.ForegroundColor = ConsoleColor.White;

                Console.Out.WriteLine("Welcome to the PC Sensor application.");
                Console.Out.WriteLine(new string('-', 79));
                Console.Out.WriteLine("This application will publish performace couters as sensor values.");
                Console.Out.WriteLine("Values will be published over XMPP using the interface defined in the IEEE XMPP IoT extensions.");

                Log.Register(new ConsoleEventSink(false));
                Log.RegisterExceptionToUnnest(typeof(System.Runtime.InteropServices.ExternalException));
                Log.RegisterExceptionToUnnest(typeof(System.Security.Authentication.AuthenticationException));

                credentials = SimpleXmppConfiguration.GetConfigUsingSimpleConsoleDialog("xmpp.config",
                                                                                        Environment.MachineName,                              // Default user name.
                                                                                        Guid.NewGuid().ToString().Replace("-", string.Empty), // Default password.
                                                                                        typeof(Program).Assembly);

                using (XmppClient Client = new XmppClient(credentials, "en", typeof(Program).Assembly))
                {
                    if (credentials.Sniffer)
                    {
                        Client.Add(new ConsoleOutSniffer(BinaryPresentationMethod.ByteCount, LineEnding.PadWithSpaces));
                    }

                    if (!string.IsNullOrEmpty(credentials.Events))
                    {
                        Log.Register(new XmppEventSink("XMPP Event Sink", Client, credentials.Events, false));
                    }

                    if (!string.IsNullOrEmpty(credentials.ThingRegistry))
                    {
                        thingRegistryClient = new ThingRegistryClient(Client, credentials.ThingRegistry);

                        thingRegistryClient.Claimed += (sender, e) =>
                        {
                            ownerJid = e.JID;
                            Log.Informational("Thing has been claimed.", ownerJid, new KeyValuePair <string, object>("Public", e.IsPublic));
                            return(Task.CompletedTask);
                        };

                        thingRegistryClient.Disowned += (sender, e) =>
                        {
                            Log.Informational("Thing has been disowned.", ownerJid);
                            ownerJid = string.Empty;
                            Register();
                            return(Task.CompletedTask);
                        };

                        thingRegistryClient.Removed += (sender, e) =>
                        {
                            Log.Informational("Thing has been removed from the public registry.", ownerJid);
                            return(Task.CompletedTask);
                        };
                    }

                    ProvisioningClient ProvisioningClient = null;
                    if (!string.IsNullOrEmpty(credentials.Provisioning))
                    {
                        ProvisioningClient = new ProvisioningClient(Client, credentials.Provisioning);
                    }

                    Timer ConnectionTimer = new Timer((P) =>
                    {
                        if (Client.State == XmppState.Offline || Client.State == XmppState.Error || Client.State == XmppState.Authenticating)
                        {
                            try
                            {
                                Client.Reconnect();
                            }
                            catch (Exception ex)
                            {
                                Log.Critical(ex);
                            }
                        }
                    }, null, 60000, 60000);

                    bool Connected = false;
                    bool ImmediateReconnect;

                    Client.OnStateChanged += (sender, NewState) =>
                    {
                        switch (NewState)
                        {
                        case XmppState.Connected:
                            Connected = true;

                            if (!registered && thingRegistryClient != null)
                            {
                                Register();
                            }
                            break;

                        case XmppState.Offline:
                            ImmediateReconnect = Connected;
                            Connected          = false;

                            if (ImmediateReconnect)
                            {
                                Client.Reconnect();
                            }
                            break;
                        }

                        return(Task.CompletedTask);
                    };

                    Client.OnPresenceSubscribe += (sender, e) =>
                    {
                        e.Accept();                             // TODO: Provisioning

                        RosterItem Item = Client.GetRosterItem(e.FromBareJID);
                        if (Item is null || Item.State == SubscriptionState.None || Item.State == SubscriptionState.From)
                        {
                            Client.RequestPresenceSubscription(e.FromBareJID);
                        }

                        Client.SetPresence(Availability.Chat);

                        return(Task.CompletedTask);
                    };

                    Client.OnPresenceUnsubscribe += (sender, e) =>
                    {
                        e.Accept();
                        return(Task.CompletedTask);
                    };

                    Client.OnRosterItemUpdated += (sender, e) =>
                    {
                        if (e.State == SubscriptionState.None && e.PendingSubscription != PendingSubscription.Subscribe)
                        {
                            Client.RemoveRosterItem(e.BareJid);
                        }

                        return(Task.CompletedTask);
                    };

                    SortedDictionary <string, string[]> CategoryIncluded = new SortedDictionary <string, string[]>();

                    List <string> Instances = new List <string>();
                    XmlDocument   Doc       = new XmlDocument()
                    {
                        PreserveWhitespace = true
                    };
                    Doc.Load("categories.xml");

                    XSL.Validate("categories.xml", Doc, "Categories", "http://waher.se/Schema/PerformanceCounterCategories.xsd",
                                 XSL.LoadSchema("Waher.Service.PcSensor.Schema.PerformanceCounterCategories.xsd"));

                    foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
                    {
                        if (N.LocalName == "Category")
                        {
                            XmlElement E       = (XmlElement)N;
                            string     Name    = XML.Attribute(E, "name");
                            bool       Include = XML.Attribute(E, "include", false);

                            if (Include)
                            {
                                Instances.Clear();

                                foreach (XmlNode N2 in N.ChildNodes)
                                {
                                    if (N2.LocalName == "Instance")
                                    {
                                        E = (XmlElement)N2;
                                        Instances.Add(XML.Attribute(E, "name"));
                                    }
                                }

                                CategoryIncluded[Name] = Instances.ToArray();
                            }
                            else
                            {
                                CategoryIncluded[Name] = null;
                            }
                        }
                    }

                    SensorServer SensorServer = new SensorServer(Client, ProvisioningClient, false);
                    SensorServer.OnExecuteReadoutRequest += (Sender, Request) =>
                    {
                        Log.Informational("Readout requested", string.Empty, Request.Actor);

                        List <Field> Fields = new List <Field>();
                        DateTime     Now    = DateTime.Now;

                        Fields.Add(new StringField(ThingReference.Empty, Now, "Machine Name", Environment.MachineName, FieldType.Identity, FieldQoS.AutomaticReadout));
                        Fields.Add(new StringField(ThingReference.Empty, Now, "OS Platform", Environment.OSVersion.Platform.ToString(), FieldType.Identity, FieldQoS.AutomaticReadout));
                        Fields.Add(new StringField(ThingReference.Empty, Now, "OS Service Pack", Environment.OSVersion.ServicePack, FieldType.Identity, FieldQoS.AutomaticReadout));
                        Fields.Add(new StringField(ThingReference.Empty, Now, "OS Version", Environment.OSVersion.VersionString, FieldType.Identity, FieldQoS.AutomaticReadout));
                        Fields.Add(new Int32Field(ThingReference.Empty, Now, "Processor Count", Environment.ProcessorCount, FieldType.Status, FieldQoS.AutomaticReadout));

                        string[] InstanceNames;
                        string   FieldName;
                        string   Unit;
                        double   Value;
                        byte     NrDec;
                        bool     Updated = false;

                        foreach (PerformanceCounterCategory Category in PerformanceCounterCategory.GetCategories())
                        {
                            FieldName = Category.CategoryName;
                            lock (CategoryIncluded)
                            {
                                if (CategoryIncluded.TryGetValue(FieldName, out InstanceNames))
                                {
                                    if (InstanceNames is null)
                                    {
                                        continue;
                                    }
                                }
                                else
                                {
                                    CategoryIncluded[FieldName] = null;
                                    Updated = true;
                                    continue;
                                }
                            }

                            if (Category.CategoryType == PerformanceCounterCategoryType.MultiInstance)
                            {
                                foreach (string InstanceName in Category.GetInstanceNames())
                                {
                                    if (InstanceNames.Length > 0 && Array.IndexOf <string>(InstanceNames, InstanceName) < 0)
                                    {
                                        continue;
                                    }

                                    foreach (PerformanceCounter Counter in Category.GetCounters(InstanceName))
                                    {
                                        FieldName = Category.CategoryName + ", " + InstanceName + ", " + Counter.CounterName;
                                        Value     = Counter.NextValue();
                                        GetUnitPrecision(ref FieldName, Value, out NrDec, out Unit);

                                        if (Fields.Count >= 100)
                                        {
                                            Request.ReportFields(false, Fields);
                                            Fields.Clear();
                                        }

                                        Fields.Add(new QuantityField(ThingReference.Empty, Now, FieldName, Value, NrDec, Unit, FieldType.Momentary, FieldQoS.AutomaticReadout));
                                    }
                                }
                            }
                            else
                            {
                                foreach (PerformanceCounter Counter in Category.GetCounters())
                                {
                                    FieldName = Category.CategoryName + ", " + Counter.CounterName;
                                    Value     = Counter.NextValue();
                                    GetUnitPrecision(ref FieldName, Value, out NrDec, out Unit);

                                    if (Fields.Count >= 100)
                                    {
                                        Request.ReportFields(false, Fields);
                                        Fields.Clear();
                                    }

                                    Fields.Add(new QuantityField(ThingReference.Empty, Now, FieldName, Value, NrDec, Unit, FieldType.Momentary, FieldQoS.AutomaticReadout));
                                }
                            }
                        }

                        Request.ReportFields(true, Fields);

                        if (Updated)
                        {
                            using (StreamWriter s = File.CreateText("categories.xml"))
                            {
                                using (XmlWriter w = XmlWriter.Create(s, XML.WriterSettings(true, false)))
                                {
                                    w.WriteStartElement("Categories", "http://waher.se/Schema/PerformanceCounterCategories.xsd");

                                    lock (CategoryIncluded)
                                    {
                                        foreach (KeyValuePair <string, string[]> P in CategoryIncluded)
                                        {
                                            w.WriteStartElement("Category");
                                            w.WriteAttributeString("name", P.Key);
                                            w.WriteAttributeString("include", CommonTypes.Encode(P.Value != null));

                                            if (P.Value != null)
                                            {
                                                foreach (string InstanceName in P.Value)
                                                {
                                                    w.WriteStartElement("Instance");
                                                    w.WriteAttributeString("name", P.Key);
                                                    w.WriteEndElement();
                                                }
                                            }

                                            w.WriteEndElement();
                                        }
                                    }

                                    w.WriteEndElement();
                                    w.Flush();
                                }
                            }
                        }

                        return(Task.CompletedTask);
                    };

                    BobClient  BobClient  = new BobClient(Client, Path.Combine(Path.GetTempPath(), "BitsOfBinary"));
                    ChatServer ChatServer = new ChatServer(Client, BobClient, SensorServer, ProvisioningClient);

                    Client.Connect();

                    while (true)
                    {
                        Thread.Sleep(1000);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Out.WriteLine(ex.Message);
            }
            finally
            {
                Log.Terminate();
            }
        }
Beispiel #2
0
        static int Main(string[] args)
        {
            try
            {
                StringBuilder       CommandLine     = new StringBuilder("ComSim.exe");
                LinkedList <string> Master          = new LinkedList <string>();
                LinkedList <string> Css             = new LinkedList <string>();
                LinkedList <string> AssemblyFolders = new LinkedList <string>();
                Encoding            Encoding        = Encoding.UTF8;
                XmlDocument         Model           = null;
                string ProgramDataFolder            = null;
                string SnifferFolder            = null;
                string LogFileName              = null;
                string LogTransformFileName     = null;
                string SnifferTransformFileName = null;
                string XmlOutputFileName        = null;
                string MarkdownOutputFileName   = null;
                int    i;
                int    c             = args.Length;
                int    BlockSize     = 8192;
                int    BlobBlockSize = 8192;
                bool   Encryption    = false;
                string s;
                bool   Help       = args.Length == 0;
                bool   LogConsole = false;
                bool   Quote;

                for (i = 0; i < c; i++)
                {
                    CommandLine.Append(' ');

                    s     = args[i];
                    Quote = s.IndexOf(' ') >= 0;
                    if (Quote)
                    {
                        CommandLine.Append('"');
                    }

                    CommandLine.Append(s);

                    if (Quote)
                    {
                        CommandLine.Append('"');
                    }
                }

                i = 0;
                while (i < c)
                {
                    s = args[i++].ToLower();
                    s = s.ToLower();

                    if (s.StartsWith("/"))
                    {
                        s = "-" + s.Substring(1);
                    }

                    switch (s)
                    {
                    case "-i":
                        if (i >= c)
                        {
                            throw new Exception("Expected model filename.");
                        }

                        s = args[i++];
                        if (!File.Exists(s))
                        {
                            throw new Exception("File not found: " + s);
                        }

                        Model = new XmlDocument();
                        Model.Load(s);
                        break;

                    case "-l":
                        if (i >= c)
                        {
                            throw new Exception("Missing log file name.");
                        }

                        if (string.IsNullOrEmpty(LogFileName))
                        {
                            LogFileName = args[i++];
                        }
                        else
                        {
                            throw new Exception("Only one log file name allowed.");
                        }
                        break;

                    case "-lt":
                        if (i >= c)
                        {
                            throw new Exception("Missing log transform file name.");
                        }

                        if (string.IsNullOrEmpty(LogTransformFileName))
                        {
                            LogTransformFileName = args[i++];
                        }
                        else
                        {
                            throw new Exception("Only one log transform file name allowed.");
                        }
                        break;

                    case "-lc":
                        LogConsole = true;
                        break;

                    case "-s":
                        if (i >= c)
                        {
                            throw new Exception("Missing sniffer folder.");
                        }

                        if (string.IsNullOrEmpty(SnifferFolder))
                        {
                            SnifferFolder = args[i++];
                        }
                        else
                        {
                            throw new Exception("Only one sniffer folder allowed.");
                        }
                        break;

                    case "-st":
                        if (i >= c)
                        {
                            throw new Exception("Missing sniffer transform file name.");
                        }

                        if (string.IsNullOrEmpty(SnifferTransformFileName))
                        {
                            SnifferTransformFileName = args[i++];
                        }
                        else
                        {
                            throw new Exception("Only one sniffer transform file name allowed.");
                        }
                        break;

                    case "-d":
                        if (i >= c)
                        {
                            throw new Exception("Missing program data folder.");
                        }

                        if (string.IsNullOrEmpty(ProgramDataFolder))
                        {
                            ProgramDataFolder = args[i++];
                        }
                        else
                        {
                            throw new Exception("Only one program data folder allowed.");
                        }
                        break;

                    case "-bs":
                        if (i >= c)
                        {
                            throw new Exception("Block size missing.");
                        }

                        if (!int.TryParse(args[i++], out BlockSize))
                        {
                            throw new Exception("Invalid block size");
                        }

                        break;

                    case "-bbs":
                        if (i >= c)
                        {
                            throw new Exception("Blob Block size missing.");
                        }

                        if (!int.TryParse(args[i++], out BlobBlockSize))
                        {
                            throw new Exception("Invalid blob block size");
                        }

                        break;

                    case "-enc":
                        if (i >= c)
                        {
                            throw new Exception("Text encoding missing.");
                        }

                        Encoding = Encoding.GetEncoding(args[i++]);
                        break;

                    case "-e":
                        Encryption = true;
                        break;

                    case "-mr":
                        if (i >= c)
                        {
                            throw new Exception("Missing markdown report file name.");
                        }

                        if (string.IsNullOrEmpty(MarkdownOutputFileName))
                        {
                            MarkdownOutputFileName = args[i++];
                        }
                        else
                        {
                            throw new Exception("Only one markdown report file name allowed.");
                        }
                        break;

                    case "-xr":
                        if (i >= c)
                        {
                            throw new Exception("Missing XML report file name.");
                        }

                        if (string.IsNullOrEmpty(XmlOutputFileName))
                        {
                            XmlOutputFileName = args[i++];
                        }
                        else
                        {
                            throw new Exception("Only one XML report file name allowed.");
                        }
                        break;

                    case "-master":
                        if (i >= c)
                        {
                            throw new Exception("Missing master file name.");
                        }

                        Master.AddLast(args[i++]);
                        break;

                    case "-css":
                        if (i >= c)
                        {
                            throw new Exception("Missing CSS file name.");
                        }

                        Css.AddLast(args[i++]);
                        break;

                    case "-ik":
                        if (i >= c)
                        {
                            throw new Exception("Missing key name.");
                        }

                        string KeyName = args[i++];

                        if (i >= c)
                        {
                            throw new Exception("Missing CSV filename.");
                        }

                        string CsvFileName = args[i++];
                        string Csv         = File.ReadAllText(CsvFileName);
                        Dictionary <string, string> Lookup;

                        if (!importedKeys.TryGetValue(KeyName, out Lookup))
                        {
                            Lookup = new Dictionary <string, string>();
                            importedKeys[KeyName] = Lookup;
                        }

                        string[][] Records = CSV.Parse(Csv);

                        foreach (string[] Record in Records)
                        {
                            if (Record.Length != 2)
                            {
                                throw new Exception("CSV file must contain records with two columns: Lookup, and value.");
                            }

                            Lookup[Record[0]] = Record[1];
                        }

                        break;

                    case "-af":
                        if (i >= c)
                        {
                            throw new Exception("Missing Assembly folder.");
                        }

                        AssemblyFolders.AddLast(args[i++]);
                        break;

                    case "-?":
                        Help = true;
                        break;

                    default:
                        throw new Exception("Unrecognized switch: " + s);
                    }
                }

                if (Help)
                {
                    Console.Out.WriteLine("The TAG Network Communication Simulator (or TAG ComSim) is a white-label console");
                    Console.Out.WriteLine("utility application written in C# provided by Trust Anchor Group (TAG for short).");
                    Console.Out.WriteLine("It can be used to simulate network communication traffic in large-scale networks.");
                    Console.Out.WriteLine();
                    Console.Out.WriteLine("Command-line arguments:");
                    Console.Out.WriteLine();
                    Console.Out.WriteLine("-i FILENAME           Specifies the filename of the model to use during simulation.");
                    Console.Out.WriteLine("                      The file must be an XML file that conforms to the");
                    Console.Out.WriteLine("                      http://trustanchorgroup.com/Schema/ComSim.xsd namespace.");
                    Console.Out.WriteLine("                      Schema is available at Schema/ComSim.xsd in the repository.");
                    Console.Out.WriteLine("-l LOG_FILENAME       Redirects logged events to a log file.");
                    Console.Out.WriteLine("-lt LOG_TRANSFORM     File name of optional XSLT transform for use with log file.");
                    Console.Out.WriteLine("-lc                   Log events to the console.");
                    Console.Out.WriteLine("-s SNIFFER_FOLDER     Optional folder for storing network sniff files.");
                    Console.Out.WriteLine("-st SNIFFER_TRANSFORM File name of optional XSLT transform for use with sniffers.");
                    Console.Out.WriteLine("-d APP_DATA_FOLDER    Points to the application data folder. Required if storage");
                    Console.Out.WriteLine("                      of data in a local database is necessary for the");
                    Console.Out.WriteLine("                      simulation. (Storage can include generated user credentials");
                    Console.Out.WriteLine("                      so that the same user identities can be used across");
                    Console.Out.WriteLine("                      simulations.)");
                    Console.Out.WriteLine("-af FOLDER            Adds an assembly folder. Assemblies can be loaded from this");
                    Console.Out.WriteLine("                      folder.");
                    Console.Out.WriteLine("-e                    If encryption is used by the database. Default=no encryption.");
                    Console.Out.WriteLine("-bs BLOCK_SIZE        Block size, in bytes. Default=8192.");
                    Console.Out.WriteLine("-bbs BLOB_BLOCK_SIZE  BLOB block size, in bytes. Default=8192.");
                    Console.Out.WriteLine("-enc ENCODING         Text encoding. Default=UTF-8");
                    Console.Out.WriteLine("-mr FILENAME          Generates a Markdown Report file after simulation.");
                    Console.Out.WriteLine("-xr FILENAME          Generates an XML report file after simulation.");
                    Console.Out.WriteLine("-master RELNAME       Adds a Master file declaration to the top of markdown");
                    Console.Out.WriteLine("                      reports. The reference must be relative to the generated");
                    Console.Out.WriteLine("                      report file.");
                    Console.Out.WriteLine("-css RELNAME          Adds a CSS file declaration to the top of markdown");
                    Console.Out.WriteLine("                      reports. The reference must be relative to the generated");
                    Console.Out.WriteLine("                      report file.");
                    Console.Out.WriteLine("-ik KEYNAME FILENAME  Import keys from a CSV file. The CSV file must consist of");
                    Console.Out.WriteLine("                      two columns. The first, contains lookup values, the second,");
                    Console.Out.WriteLine("                      the key values corresponding to the lookup values. The");
                    Console.Out.WriteLine("                      KEYNAME argument defines the key name to which the keys");
                    Console.Out.WriteLine("                      are associated. FILENAME must point to a CSV file.");
                    Console.Out.WriteLine("-?                    Displays command-line help.");
                    Console.Out.WriteLine();

                    if (args.Length <= 1)
                    {
                        return(1);
                    }
                }

                if (Model is null)
                {
                    throw new Exception("No simulation model specified.");
                }

                if (string.IsNullOrEmpty(ProgramDataFolder))
                {
                    throw new Exception("No program data folder set");
                }

                Console.Out.WriteLine("Loading modules.");

                foreach (XmlNode N in Model.DocumentElement.ChildNodes)
                {
                    if (N is XmlElement E && E.LocalName == "Assemblies")
                    {
                        Dictionary <string, Assembly> Loaded = new Dictionary <string, Assembly>();

                        foreach (Assembly A in AppDomain.CurrentDomain.GetAssemblies())
                        {
                            Loaded[A.GetName().Name] = A;
                        }

                        foreach (XmlNode N2 in E.ChildNodes)
                        {
                            if (N2 is XmlElement E2 && E2.LocalName == "Assembly")
                            {
                                string FileName = XML.Attribute(E2, "fileName");

                                if (!File.Exists(FileName))
                                {
                                    string FileName2 = null;

                                    foreach (string Folder in AssemblyFolders)
                                    {
                                        FileName2 = Path.Combine(Folder, FileName);
                                        if (File.Exists(FileName2))
                                        {
                                            break;
                                        }
                                        else
                                        {
                                            FileName2 = null;
                                        }
                                    }

                                    if (FileName2 is null)
                                    {
                                        throw new Exception("File not found: " + FileName);
                                    }
                                    else
                                    {
                                        FileName = FileName2;
                                    }
                                }

                                LinkedList <string> ToLoad = new LinkedList <string>();
                                ToLoad.AddLast(FileName);

                                while (!string.IsNullOrEmpty(FileName = ToLoad.First?.Value))
                                {
                                    ToLoad.RemoveFirst();

                                    Console.Out.WriteLine("Loading " + FileName);

                                    byte[] Bin = File.ReadAllBytes(FileName);
                                    try
                                    {
                                        Assembly A = AppDomain.CurrentDomain.Load(Bin);
                                        Loaded[A.GetName().Name] = A;

                                        AssemblyName[] Referenced = A.GetReferencedAssemblies();

                                        foreach (AssemblyName AN in Referenced)
                                        {
                                            if (Loaded.ContainsKey(AN.Name))
                                            {
                                                continue;
                                            }

                                            string RefFileName = Path.Combine(Path.GetDirectoryName(FileName), AN.Name + ".dll");

                                            if (!File.Exists(RefFileName))
                                            {
                                                continue;
                                            }

                                            if (!ToLoad.Contains(RefFileName))
                                            {
                                                ToLoad.AddLast(RefFileName);
                                            }
                                        }
                                    }
                                    catch (BadImageFormatException)
                                    {
                                        // Ignore
                                    }
                                }
                            }
                        }
                    }
                }

                if (!string.IsNullOrEmpty(SnifferFolder))
                {
                    if (!Directory.Exists(SnifferFolder))
                    {
                        Directory.CreateDirectory(SnifferFolder);
                    }

                    foreach (string FileName in Directory.GetFiles(SnifferFolder, "*.xml", SearchOption.TopDirectoryOnly))
                    {
                        File.Delete(FileName);
                    }
                }

                if (!Directory.Exists(ProgramDataFolder))
                {
                    Directory.CreateDirectory(ProgramDataFolder);
                }

                Console.Out.WriteLine("Initializing runtime inventory.");
                TypesLoader.Initialize();
                Factory.Initialize();

                Console.Out.WriteLine("Validating model.");

                Dictionary <string, XmlSchema> Schemas   = new Dictionary <string, XmlSchema>();
                LinkedList <XmlElement>        ToProcess = new LinkedList <XmlElement>();
                XmlElement Loop;
                string     Last = null;

                ToProcess.AddLast(Model.DocumentElement);

                while (!((Loop = ToProcess.First?.Value) is null))
                {
                    ToProcess.RemoveFirst();

                    foreach (XmlNode N in Loop.ChildNodes)
                    {
                        if (N is XmlElement E)
                        {
                            ToProcess.AddLast(E);
                        }
                    }

                    s = Loop.NamespaceURI;
                    if (s != Last)
                    {
                        Last = s;
                        if (!Schemas.ContainsKey(s))
                        {
                            if (Factory.TryGetSchemaResource(s, out KeyValuePair <string, Assembly> P))
                            {
                                Schemas[s] = XSL.LoadSchema(P.Key, P.Value);
                            }
                        }
                    }
                }

                XmlSchema[] Schemas2 = new XmlSchema[Schemas.Count];
                Schemas.Values.CopyTo(Schemas2, 0);

                XSL.Validate("Model", Model, "Model", TAG.Simulator.Model.ComSimNamespace, Schemas2);

                if (!string.IsNullOrEmpty(LogFileName))
                {
                    if (File.Exists(LogFileName))
                    {
                        File.Delete(LogFileName);
                    }

                    Log.Register(new XmlFileEventSink("XmlEventSink", LogFileName, LogTransformFileName, int.MaxValue));
                }

                if (LogConsole)
                {
                    Log.Register(new ConsoleEventSink(false));
                }

                TaskCompletionSource <bool> Done = new TaskCompletionSource <bool>(false);

                try
                {
                    SetConsoleCtrlHandler((ControlType) =>
                    {
                        switch (ControlType)
                        {
                        case CtrlTypes.CTRL_BREAK_EVENT:
                        case CtrlTypes.CTRL_CLOSE_EVENT:
                        case CtrlTypes.CTRL_C_EVENT:
                        case CtrlTypes.CTRL_SHUTDOWN_EVENT:
                            Done.TrySetResult(false);
                            break;

                        case CtrlTypes.CTRL_LOGOFF_EVENT:
                            break;
                        }

                        return(true);
                    }, true);
                }
                catch (Exception)
                {
                    Log.Error("Unable to register CTRL-C control handler.");
                }

                Console.Out.WriteLine("Initializing database.");

                bool Result = Run(Model, Done, SnifferFolder, SnifferTransformFileName, MarkdownOutputFileName,
                                  XmlOutputFileName, CommandLine.ToString(), Master, Css, !LogConsole,
                                  ProgramDataFolder, BlockSize, BlobBlockSize, Encoding, Encryption).Result;

                if (Result)
                {
                    Console.Out.WriteLine("Simulation completed.");
                    return(0);
                }
                else
                {
                    WriteLine("Simulation aborted.", ConsoleColor.White, ConsoleColor.Red);
                    return(1);
                }
            }
            catch (AggregateException ex)
            {
                foreach (Exception ex2 in ex.InnerExceptions)
                {
                    WriteLine(ex2.Message, ConsoleColor.White, ConsoleColor.Red);
                }

                return(1);
            }
            catch (Exception ex)
            {
                WriteLine(ex.Message, ConsoleColor.White, ConsoleColor.Red);
                return(1);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Starts the gateway.
        /// </summary>
        /// <param name="ConsoleOutput">If console output is permitted.</param>
        public static bool Start(bool ConsoleOutput)
        {
            gatewayRunning = new Semaphore(1, 1, "Waher.IoTGateway.Running");
            if (!gatewayRunning.WaitOne(1000))
            {
                return(false);                // Is running in another process.
            }
            Semaphore StartingServer = new Semaphore(1, 1, "Waher.IoTGateway.Starting");

            if (!StartingServer.WaitOne(1000))
            {
                gatewayRunning.Release();
                gatewayRunning.Dispose();
                gatewayRunning = null;

                StartingServer.Dispose();
                return(false);                // Being started in another process.
            }

            try
            {
                Initialize();

                appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
                if (!appDataFolder.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
                {
                    appDataFolder += Path.DirectorySeparatorChar;
                }

                appDataFolder += "IoT Gateway" + Path.DirectorySeparatorChar;

                Log.Register(new XmlFileEventSink("XML File Event Sink",
                                                  appDataFolder + "Events" + Path.DirectorySeparatorChar + "Event Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
                                                  appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "EventXmlToHtml.xslt", 7));

                Log.Informational("Server starting up.");

                beforeUninstallCommandNr = Gateway.RegisterServiceCommand(BeforeUninstall);

                rootFolder = appDataFolder + "Root" + Path.DirectorySeparatorChar;
                if (!Directory.Exists(rootFolder))
                {
                    appDataFolder = string.Empty;
                    rootFolder    = "Root" + Path.DirectorySeparatorChar;
                }

                Types.SetModuleParameter("AppData", appDataFolder);
                Types.SetModuleParameter("Root", rootFolder);

                scheduler = new Scheduler();
                rnd       = RandomNumberGenerator.Create();

                Task.Run(() => CodeContent.GraphViz.Init());

                XmlDocument Config = new XmlDocument();

                string GatewayConfigFileName = appDataFolder + "Gateway.config";
                if (!File.Exists(GatewayConfigFileName))
                {
                    GatewayConfigFileName = "Gateway.config";
                }

                Config.Load(GatewayConfigFileName);
                XSL.Validate("Gateway.config", Config, "GatewayConfiguration", "http://waher.se/Schema/GatewayConfiguration.xsd",
                             XSL.LoadSchema(typeof(Gateway).Namespace + ".Schema.GatewayConfiguration.xsd", typeof(Gateway).Assembly));

                domain = Config.DocumentElement["Domain"].InnerText;

                XmlElement DatabaseConfig = Config.DocumentElement["Database"];
                if (!CommonTypes.TryParse(DatabaseConfig.Attributes["encrypted"].Value, out bool Encrypted))
                {
                    Encrypted = true;
                }

                databaseProvider = new FilesProvider(appDataFolder + DatabaseConfig.Attributes["folder"].Value,
                                                     DatabaseConfig.Attributes["defaultCollectionName"].Value,
                                                     int.Parse(DatabaseConfig.Attributes["blockSize"].Value),
                                                     int.Parse(DatabaseConfig.Attributes["blocksInCache"].Value),
                                                     int.Parse(DatabaseConfig.Attributes["blobBlockSize"].Value), Encoding.UTF8,
                                                     int.Parse(DatabaseConfig.Attributes["timeoutMs"].Value),
                                                     Encrypted, false);
                Database.Register(databaseProvider);

                PersistedEventLog PersistedEventLog = new PersistedEventLog(7, new TimeSpan(4, 15, 0));
                Log.Register(PersistedEventLog);
                PersistedEventLog.Queue(new Event(EventType.Informational, "Server starting up.", string.Empty, string.Empty, string.Empty, EventLevel.Minor, string.Empty, string.Empty, string.Empty));

                xmppConfigFileName = Config.DocumentElement["XmppClient"].Attributes["configFileName"].Value;
                if (!File.Exists(xmppConfigFileName))
                {
                    xmppConfigFileName = appDataFolder + xmppConfigFileName;
                }

                if (ConsoleOutput)
                {
                    xmppConfiguration = SimpleXmppConfiguration.GetConfigUsingSimpleConsoleDialog(xmppConfigFileName,
                                                                                                  Guid.NewGuid().ToString().Replace("-", string.Empty), // Default user name.
                                                                                                  Guid.NewGuid().ToString().Replace("-", string.Empty), // Default password.
                                                                                                  FormSignatureKey, FormSignatureSecret, typeof(Gateway).Assembly);
                }
                else if (File.Exists(xmppConfigFileName))
                {
                    xmppConfiguration = new SimpleXmppConfiguration(xmppConfigFileName);
                    RuntimeSettings.Set("XMPP.CONFIG", xmppConfiguration.ExportSimpleXmppConfiguration());
                }
                else
                {
                    string      XmppConfig = RuntimeSettings.Get("XMPP.CONFIG", string.Empty);
                    XmlDocument Doc        = new XmlDocument();
                    Doc.LoadXml(XmppConfig);
                    xmppConfiguration = new SimpleXmppConfiguration(Doc);
                }

                xmppClient = xmppConfiguration.GetClient("en", typeof(Gateway).Assembly, false);
                xmppClient.AllowRegistration(FormSignatureKey, FormSignatureSecret);
                xmppClient.OnValidateSender += XmppClient_OnValidateSender;
                Types.SetModuleParameter("XMPP", xmppClient);

                if (xmppConfiguration.Sniffer)
                {
                    ISniffer Sniffer;

                    if (ConsoleOutput)
                    {
                        Sniffer = new ConsoleOutSniffer(BinaryPresentationMethod.ByteCount, LineEnding.PadWithSpaces);
                        xmppClient.Add(Sniffer);
                    }

                    Sniffer = new XmlFileSniffer(appDataFolder + "XMPP" + Path.DirectorySeparatorChar +
                                                 "XMPP Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
                                                 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
                                                 7, BinaryPresentationMethod.ByteCount);
                    xmppClient.Add(Sniffer);
                }

                if (!string.IsNullOrEmpty(xmppConfiguration.Events))
                {
                    Log.Register(new XmppEventSink("XMPP Event Sink", xmppClient, xmppConfiguration.Events, false));
                }

                if (!string.IsNullOrEmpty(xmppConfiguration.ThingRegistry))
                {
                    thingRegistryClient           = new ThingRegistryClient(xmppClient, xmppConfiguration.ThingRegistry);
                    thingRegistryClient.Claimed  += ThingRegistryClient_Claimed;
                    thingRegistryClient.Disowned += ThingRegistryClient_Disowned;
                    thingRegistryClient.Removed  += ThingRegistryClient_Removed;
                }

                if (!string.IsNullOrEmpty(xmppConfiguration.Provisioning))
                {
                    provisioningClient = new ProvisioningClient(xmppClient, xmppConfiguration.Provisioning);
                }

                DateTime Now      = DateTime.Now;
                int      MsToNext = 60000 - (Now.Second * 1000 + Now.Millisecond);

                connectionTimer                   = new Timer(CheckConnection, null, MsToNext, 60000);
                xmppClient.OnStateChanged        += XmppClient_OnStateChanged;
                xmppClient.OnPresenceSubscribe   += XmppClient_OnPresenceSubscribe;
                xmppClient.OnPresenceUnsubscribe += XmppClient_OnPresenceUnsubscribe;
                xmppClient.OnRosterItemUpdated   += XmppClient_OnRosterItemUpdated;

                ibbClient = new Networking.XMPP.InBandBytestreams.IbbClient(xmppClient, MaxChunkSize);
                Types.SetModuleParameter("IBB", ibbClient);

                socksProxy = new Networking.XMPP.P2P.SOCKS5.Socks5Proxy(xmppClient);
                Types.SetModuleParameter("SOCKS5", socksProxy);

                string CertificateLocalFileName = Config.DocumentElement["Certificate"].Attributes["configFileName"].Value;
                string CertificateFileName;
                string CertificateXml;
                string CertificatePassword;
                byte[] CertificateRaw;

                try
                {
                    CertificateRaw      = Convert.FromBase64String(RuntimeSettings.Get("CERTIFICATE.BASE64", string.Empty));
                    CertificatePassword = RuntimeSettings.Get("CERTIFICATE.PWD", string.Empty);

                    certificate = new X509Certificate2(CertificateRaw, CertificatePassword);
                }
                catch (Exception)
                {
                    certificate = null;
                }

                if (File.Exists(CertificateFileName = appDataFolder + CertificateLocalFileName))
                {
                    CertificateXml = File.ReadAllText(CertificateFileName);
                }
                else if (File.Exists(CertificateFileName = CertificateLocalFileName) && certificate == null)
                {
                    CertificateXml = File.ReadAllText(CertificateFileName);
                }
                else
                {
                    CertificateFileName = null;
                    CertificateXml      = null;
                }

                if (CertificateXml != null)
                {
                    XmlDocument CertificateConfig = new XmlDocument();
                    CertificateConfig.LoadXml(CertificateXml);

                    XSL.Validate(CertificateLocalFileName, CertificateConfig, "CertificateConfiguration", "http://waher.se/Schema/CertificateConfiguration.xsd",
                                 XSL.LoadSchema(typeof(Gateway).Namespace + ".Schema.CertificateConfiguration.xsd", typeof(Gateway).Assembly));

                    CertificateLocalFileName = CertificateConfig.DocumentElement["FileName"].InnerText;

                    if (File.Exists(appDataFolder + CertificateLocalFileName))
                    {
                        CertificateLocalFileName = appDataFolder + CertificateLocalFileName;
                    }

                    CertificateRaw      = File.ReadAllBytes(CertificateLocalFileName);
                    CertificatePassword = CertificateConfig.DocumentElement["Password"].InnerText;

                    certificate = new X509Certificate2(CertificateRaw, CertificatePassword);

                    RuntimeSettings.Set("CERTIFICATE.BASE64", Convert.ToBase64String(CertificateRaw));
                    RuntimeSettings.Set("CERTIFICATE.PWD", CertificatePassword);

                    if (CertificateLocalFileName != "certificate.pfx" || CertificatePassword != "testexamplecom")
                    {
                        try
                        {
                            File.Delete(CertificateLocalFileName);
                        }
                        catch (Exception)
                        {
                            Log.Warning("Unable to delete " + CertificateLocalFileName + " after importing it into the encrypted database.");
                        }

                        try
                        {
                            File.Delete(CertificateFileName);
                        }
                        catch (Exception)
                        {
                            Log.Warning("Unable to delete " + CertificateFileName + " after importing it into the encrypted database.");
                        }
                    }
                }

                foreach (XmlNode N in Config.DocumentElement["Ports"].ChildNodes)
                {
                    if (N.LocalName == "Port")
                    {
                        XmlElement E        = (XmlElement)N;
                        string     Protocol = XML.Attribute(E, "protocol");
                        if (!string.IsNullOrEmpty(Protocol) && int.TryParse(E.InnerText, out int Port))
                        {
                            ports.AddLast(new KeyValuePair <string, int>(Protocol, Port));
                        }
                    }
                }

                webServer = new HttpServer(GetConfigPorts("HTTP"), GetConfigPorts("HTTPS"), certificate);
                Types.SetModuleParameter("HTTP", webServer);

                StringBuilder sb = new StringBuilder();

                foreach (int Port in webServer.OpenPorts)
                {
                    sb.AppendLine(Port.ToString());
                }

                try
                {
                    File.WriteAllText(appDataFolder + "Ports.txt", sb.ToString());
                }
                catch (Exception ex)
                {
                    Log.Critical(ex);
                }

                HttpFolderResource HttpFolderResource;
                HttpxProxy         HttpxProxy;

                webServer.Register(new HttpFolderResource("/Graphics", "Graphics", false, false, true, false));                      // TODO: Add authentication mechanisms for PUT & DELETE.
                webServer.Register(new HttpFolderResource("/highlight", "Highlight", false, false, true, false));                    // Syntax highlighting library, provided by http://highlightjs.org
                webServer.Register(HttpFolderResource = new HttpFolderResource(string.Empty, rootFolder, false, false, true, true)); // TODO: Add authentication mechanisms for PUT & DELETE.
                webServer.Register(HttpxProxy         = new HttpxProxy("/HttpxProxy", xmppClient, MaxChunkSize));
                webServer.Register("/", (req, resp) =>
                {
                    throw new TemporaryRedirectException(Config.DocumentElement["DefaultPage"].InnerText);
                });
                webServer.Register(clientEvents = new ClientEvents());

                HttpFolderResource.AllowTypeConversion();
                MarkdownToHtmlConverter.EmojiSource = Emoji1_24x24;

                XmlElement FileFolders = Config.DocumentElement["FileFolders"];
                if (FileFolders != null)
                {
                    foreach (XmlNode N in FileFolders.ChildNodes)
                    {
                        if (N is XmlElement E && E.LocalName == "FileFolder")
                        {
                            string WebFolder  = XML.Attribute(E, "webFolder");
                            string FolderPath = XML.Attribute(E, "folderPath");

                            webServer.Register(new HttpFolderResource(WebFolder, FolderPath, false, false, true, true));
                        }
                    }
                }

                httpxServer = new HttpxServer(xmppClient, webServer, MaxChunkSize);
                Types.SetModuleParameter("HTTPX", HttpxProxy);
                Types.SetModuleParameter("HTTPXS", httpxServer);

                HttpxProxy.IbbClient  = ibbClient;
                httpxServer.IbbClient = ibbClient;

                HttpxProxy.Socks5Proxy  = socksProxy;
                httpxServer.Socks5Proxy = socksProxy;

                if (xmppConfiguration.Sniffer)
                {
                    ISniffer Sniffer;

                    Sniffer = new XmlFileSniffer(appDataFolder + "HTTP" + Path.DirectorySeparatorChar +
                                                 "HTTP Log %YEAR%-%MONTH%-%DAY%T%HOUR%.xml",
                                                 appDataFolder + "Transforms" + Path.DirectorySeparatorChar + "SnifferXmlToHtml.xslt",
                                                 7, BinaryPresentationMethod.ByteCount);
                    webServer.Add(Sniffer);
                }

                coapEndpoint = new CoapEndpoint();
                Types.SetModuleParameter("CoAP", coapEndpoint);

                concentratorServer = new ConcentratorServer(xmppClient, provisioningClient, new MeteringTopology());
                Types.SetModuleParameter("Concentrator", concentratorServer);
                Types.SetModuleParameter("Sensor", concentratorServer.SensorServer);
                Types.SetModuleParameter("Control", concentratorServer.ControlServer);
            }
            catch (Exception ex)
            {
                Log.Critical(ex);

                gatewayRunning.Release();
                gatewayRunning.Dispose();
                gatewayRunning = null;

                StartingServer.Release();
                StartingServer.Dispose();

                ExceptionDispatchInfo.Capture(ex).Throw();
            }

            Task.Run(async() =>
            {
                try
                {
                    try
                    {
                        string BinaryFolder    = AppDomain.CurrentDomain.BaseDirectory;
                        string[] LanguageFiles = Directory.GetFiles(BinaryFolder, "*.lng", SearchOption.AllDirectories);
                        string FileName;

                        if (LanguageFiles.Length > 0)
                        {
                            XmlSchema Schema = XSL.LoadSchema(Translator.SchemaResource, typeof(Translator).Assembly);

                            foreach (string LanguageFile in LanguageFiles)
                            {
                                try
                                {
                                    FileName = LanguageFile;
                                    if (FileName.StartsWith(BinaryFolder))
                                    {
                                        FileName = FileName.Substring(BinaryFolder.Length);
                                    }

                                    DateTime LastWriteTime    = File.GetLastWriteTime(LanguageFile);
                                    DateTime LastImportedTime = await RuntimeSettings.GetAsync(FileName, DateTime.MinValue);

                                    if (LastWriteTime > LastImportedTime)
                                    {
                                        Log.Informational("Importing language file.", FileName);

                                        string Xml      = File.ReadAllText(LanguageFile);
                                        XmlDocument Doc = new XmlDocument();
                                        Doc.LoadXml(Xml);

                                        XSL.Validate(FileName, Doc, Translator.SchemaRoot, Translator.SchemaNamespace, Schema);

                                        using (XmlReader r = new XmlNodeReader(Doc))
                                        {
                                            await Translator.ImportAsync(r);
                                        }

                                        RuntimeSettings.Set(FileName, LastWriteTime);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Log.Critical(ex, LanguageFile);
                                }
                            }
                        }

                        Types.StartAllModules(int.MaxValue);
                    }
                    finally
                    {
                        StartingServer.Release();
                        StartingServer.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    Log.Critical(ex);
                }
                finally
                {
                    xmppClient.Connect();
                }
            });

            return(true);
        }
        /// <summary>
        /// Initializes the setup object.
        /// </summary>
        /// <param name="WebServer">Current Web Server object.</param>
        public override async Task InitSetup(HttpServer WebServer)
        {
            XmlSchema       Schema       = XSL.LoadSchema(typeof(Gateway).Namespace + ".Schema.Theme.xsd", typeof(Gateway).Assembly);
            string          ThemesFolder = Path.Combine(Gateway.AppDataFolder, "Root", "Themes");
            ThemeDefinition Def;

            await base.InitSetup(WebServer);

            WebServer.ETagSalt = this.Updated.Ticks.ToString();

            if (Directory.Exists(ThemesFolder))
            {
                foreach (string FileName in Directory.GetFiles(ThemesFolder, "*.xml", SearchOption.AllDirectories))
                {
                    try
                    {
                        XmlDocument Doc = new XmlDocument();
                        Doc.Load(FileName);

                        XSL.Validate(FileName, Doc, "Theme", "http://waher.se/Schema/Theme.xsd", Schema);

                        Def = new ThemeDefinition(Doc);
                        themeDefinitions[Def.Id] = Def;
                    }
                    catch (Exception ex)
                    {
                        Log.Critical(ex, FileName);
                        continue;
                    }
                }
            }

            bool Update = false;

            if (!string.IsNullOrEmpty(this.themeId) && !themeDefinitions.ContainsKey(this.themeId))
            {
                this.themeId   = string.Empty;
                this.Step      = 0;
                this.Completed = DateTime.MinValue;
                this.Complete  = false;

                Update = true;
            }

            if (string.IsNullOrEmpty(this.themeId) && themeDefinitions.Count == 1)
            {
                foreach (ThemeDefinition Def2 in themeDefinitions.Values)
                {
                    this.themeId = Def2.Id;

                    await this.MakeCompleted();

                    Update = false;

                    break;
                }
            }

            if (Update)
            {
                this.Updated = DateTime.Now;
                await Database.Update(this);
            }

            if (!string.IsNullOrEmpty(this.themeId) && themeDefinitions.TryGetValue(this.themeId, out Def))
            {
                Theme.CurrerntTheme = Def;
            }
            else if (themeDefinitions.TryGetValue("CactusRose", out Def))
            {
                Theme.CurrerntTheme = Def;
            }
            else
            {
                foreach (ThemeDefinition Def2 in themeDefinitions.Values)
                {
                    Theme.CurrerntTheme = Def2;
                    break;
                }
            }

            this.setTheme = WebServer.Register("/Settings/SetTheme", null, this.SetTheme, true, false, true);
        }
Beispiel #5
0
        private static void Uninstall(string ManifestFile, string ServerApplication, string ProgramDataFolder, bool Remove)
        {
            // Same code as for custom action InstallManifest in Waher.IoTGateway.Installers

            if (string.IsNullOrEmpty(ManifestFile))
            {
                throw new Exception("Missing manifest file.");
            }

            if (string.IsNullOrEmpty(ServerApplication))
            {
                throw new Exception("Missing server application.");
            }

            if (string.IsNullOrEmpty(ProgramDataFolder))
            {
                ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "IoT Gateway");
                Log.Informational("Using default program data folder: " + ProgramDataFolder);
            }

            if (!File.Exists(ServerApplication))
            {
                throw new Exception("Server application not found: " + ServerApplication);
            }

            Log.Informational("Getting assembly name of server.");
            AssemblyName ServerName = AssemblyName.GetAssemblyName(ServerApplication);

            Log.Informational("Server assembly name: " + ServerName.ToString());

            string DepsJsonFileName;

            int i = ServerApplication.LastIndexOf('.');

            if (i < 0)
            {
                DepsJsonFileName = ServerApplication;
            }
            else
            {
                DepsJsonFileName = ServerApplication.Substring(0, i);
            }

            DepsJsonFileName += ".deps.json";

            Log.Informational("deps.json file name: " + DepsJsonFileName);

            if (!File.Exists(DepsJsonFileName))
            {
                throw new Exception("Invalid server executable. No corresponding deps.json file found.");
            }

            Log.Informational("Opening " + DepsJsonFileName);

            string s = File.ReadAllText(DepsJsonFileName);

            Log.Informational("Parsing " + DepsJsonFileName);

            Dictionary <string, object> Deps = JSON.Parse(s) as Dictionary <string, object>;

            if (Deps is null)
            {
                throw new Exception("Invalid deps.json file. Unable to install.");
            }

            Log.Informational("Loading manifest file.");

            XmlDocument Manifest = new XmlDocument();

            Manifest.Load(ManifestFile);

            Log.Informational("Validating manifest file.");

            XmlSchema Schema = XSL.LoadSchema(typeof(Program).Namespace + ".Schema.Manifest.xsd", Assembly.GetExecutingAssembly());

            XSL.Validate(ManifestFile, Manifest, "Module", "http://waher.se/Schema/ModuleManifest.xsd", Schema);

            XmlElement Module    = Manifest["Module"];
            string     AppFolder = Path.GetDirectoryName(ServerApplication);

            Log.Informational("App folder: " + AppFolder);

            foreach (XmlNode N in Module.ChildNodes)
            {
                if (N is XmlElement E && E.LocalName == "Assembly")
                {
                    string FileName    = XML.Attribute(E, "fileName");
                    string AppFileName = Path.Combine(AppFolder, FileName);

                    Assembly     A   = Assembly.LoadFrom(AppFileName);
                    AssemblyName AN  = A.GetName();
                    string       Key = AN.Name + "/" + AN.Version.ToString();

                    if (Deps != null && Deps.TryGetValue("targets", out object Obj) && Obj is Dictionary <string, object> Targets)
                    {
                        Targets.Remove(Key);

                        foreach (KeyValuePair <string, object> P in Targets)
                        {
                            if (P.Value is Dictionary <string, object> Target)
                            {
                                foreach (KeyValuePair <string, object> P2 in Target)
                                {
                                    if (P2.Key.StartsWith(ServerName.Name + "/") &&
                                        P2.Value is Dictionary <string, object> App &&
                                        App.TryGetValue("dependencies", out object Obj2) &&
                                        Obj2 is Dictionary <string, object> Dependencies)
                                    {
                                        Dependencies.Remove(AN.Name);
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    if (Deps != null && Deps.TryGetValue("libraries", out object Obj3) && Obj3 is Dictionary <string, object> Libraries)
                    {
                        foreach (KeyValuePair <string, object> P in Libraries)
                        {
                            if (P.Key.StartsWith(AN.Name + "/"))
                            {
                                Libraries.Remove(P.Key);
                                break;
                            }
                        }
                    }

                    if (Remove)
                    {
                        RemoveFile(AppFileName);
                        if (FileName.EndsWith(".dll", StringComparison.CurrentCultureIgnoreCase))
                        {
                            string PdbFileName = FileName.Substring(0, FileName.Length - 4) + ".pdb";
                            RemoveFile(PdbFileName);
                        }
                    }
                }
            }

            Log.Informational("Encoding JSON");
            s = JSON.Encode(Deps, true);

            Log.Informational("Writing " + DepsJsonFileName);
            File.WriteAllText(DepsJsonFileName, s, Encoding.UTF8);

            if (Path.GetDirectoryName(ManifestFile) == AppFolder)
            {
                RemoveFile(ManifestFile);
            }
        }