Example #1
0
        /// <summary>
        /// Ctor for the Contoso topology.
        /// </summary>
        /// <param name="topologyDescriptionFilename"></param>
        public ContosoTopology(string topologyDescriptionFilename)
        {
            TopologyDescription topologyDescription;

            // Read the JSON equipment topology description.
            using (StreamReader topologyDescriptionStream = File.OpenText(topologyDescriptionFilename))
            {
                JsonSerializer serializer = new JsonSerializer();
                topologyDescription = (TopologyDescription)serializer.Deserialize(topologyDescriptionStream, typeof(TopologyDescription));
            }

            // Build the topology tree, start with the global level.
            ContosoTopologyNode rootNode = new ContosoTopologyNode("TopologyRoot", "Global", "Contoso", topologyDescription);

            TopologyRoot = rootNode;

            // There must be at least one factory.
            if (topologyDescription.Factories.Count == 0)
            {
                IndexOutOfRangeException indexOutOfRange = new IndexOutOfRangeException("There must be at least one factory defined.");
                throw indexOutOfRange;
            }

            // Iterate through the whole description level by level.
            foreach (var factoryDescription in topologyDescription.Factories)
            {
                // Add it to the tree.
                Factory factory = new Factory(factoryDescription);
                AddChild(TopologyRoot.Key, factory);

                // There must be at least one production line.
                if (factoryDescription.ProductionLines.Count == 0)
                {
                    string message = String.Format("There must be at least one production line defined for factory '{0}'.", factory.Name);
                    IndexOutOfRangeException indexOutOfRange = new IndexOutOfRangeException(message);
                    throw indexOutOfRange;
                }

                // Handle all production lines.
                foreach (var productionLineDescription in factoryDescription.ProductionLines)
                {
                    // Add it to the tree.
                    ProductionLine productionLine = new ProductionLine(productionLineDescription);
                    productionLine.Location = factory.Location;
                    AddChild(factory.Key, productionLine);

                    // There must be at least one station.
                    if (productionLineDescription.Stations.Count == 0)
                    {
                        string message = String.Format("There must be at least one station defined for production line '{0}' in factory '{1}'.", productionLine.Name, factory.Name);
                        IndexOutOfRangeException indexOutOfRange = new IndexOutOfRangeException(message);
                        throw indexOutOfRange;
                    }

                    // Handle all stations (a station is running an OPC UA server).
                    foreach (var stationDescription in productionLineDescription.Stations)
                    {
                        // Add it to the tree.
                        Station station = new Station(stationDescription);
                        station.Location = productionLine.Location;
                        AddChild(productionLine.Key, station);
                    }
                }
            }

            // configuration only contains settings for stations, calculate the remaining topology
            UpdateAllKPIAndOEEPerfItems();
        }
        /// <summary>
        /// Returns information for all children of the given topology node.
        /// This information will be shown in the UX.
        /// </summary>
        /// <param name="parentKey"></param>
        public List <ContosoChildInfo> GetChildrenInfo(string parentKey)
        {
            List <ContosoChildInfo> childrenInfo = new List <ContosoChildInfo>();

            // Update type of children for the given key.
            ContosoTopologyNode parent = (ContosoTopologyNode)TopologyTable[parentKey];
            string childrenType;
            Type   parentType = parent.GetType();

            childrenType = typeof(Factory).ToString();
            if (parentType == typeof(Factory))
            {
                childrenType = typeof(ProductionLine).ToString();
            }
            else if (parentType == typeof(ProductionLine))
            {
                childrenType = typeof(Station).ToString();
            }
            else if (parentType == typeof(Station))
            {
                childrenType = typeof(ContosoOpcUaNode).ToString();
            }

            // Prepare the list with the child objects for the view.
            if (childrenType == typeof(ContosoOpcUaNode).ToString())
            {
                Station station = (Station)TopologyTable[parentKey];
                foreach (ContosoOpcUaNode opcUaNode in station.NodeList)
                {
                    ContosoChildInfo childInfo = new ContosoChildInfo(station.Key,
                                                                      opcUaNode.NodeId,
                                                                      opcUaNode.Status.ToString(),
                                                                      opcUaNode.SymbolicName,
                                                                      opcUaNode.SymbolicName,
                                                                      station.Location.City,
                                                                      station.Location.Latitude,
                                                                      station.Location.Longitude,
                                                                      opcUaNode.Visible,
                                                                      opcUaNode.Last.Value.ToString("0.###", CultureInfo.InvariantCulture),
                                                                      opcUaNode.Units);
                    childrenInfo.Add(childInfo);
                }
            }
            else
            {
                var childrenKeys = ((ContosoTopologyNode)TopologyTable[parentKey]).GetChildren();
                foreach (string key in childrenKeys)
                {
                    if (childrenType == typeof(Factory).ToString())
                    {
                        Factory          factory        = (Factory)TopologyTable[key];
                        ContosoChildInfo dashboardChild = new ContosoChildInfo(factory.Key, factory.Status.ToString(), factory.Name, factory.Description,
                                                                               factory.Location.City, factory.Location.Latitude, factory.Location.Longitude, true);
                        childrenInfo.Add(dashboardChild);
                    }
                    if (childrenType == typeof(ProductionLine).ToString())
                    {
                        ProductionLine   productionLine = (ProductionLine)TopologyTable[key];
                        ContosoChildInfo dashboardChild = new ContosoChildInfo(productionLine.Key, productionLine.Status.ToString(), productionLine.Name, productionLine.Description,
                                                                               productionLine.Location.City, productionLine.Location.Latitude, productionLine.Location.Longitude, true);
                        childrenInfo.Add(dashboardChild);
                    }
                    if (childrenType == typeof(Station).ToString())
                    {
                        Station          station        = (Station)TopologyTable[key];
                        ContosoChildInfo dashboardChild = new ContosoChildInfo(station.Key, station.Status.ToString(), station.Name, station.Description,
                                                                               station.Location.City, station.Location.Latitude, station.Location.Longitude, true);
                        childrenInfo.Add(dashboardChild);
                    }
                }
            }
            return(childrenInfo);
        }
        /// <summary>
        /// Update the OEE and KPI values
        /// </summary>
        public void UpdateAllKPIAndOEEValues(int histogram = 0)
        {
            // topology is updated from bottom to top nodes, add Production Line view
            List <string> orderedlist = GetAllChildren(TopologyRoot.Key, typeof(ProductionLine));

            // add factory view
            orderedlist.AddRange(GetAllChildren(TopologyRoot.Key, typeof(Factory)));
            // add top view
            orderedlist.Add(TopologyRoot.Key);

            bool updateStatus = false;

            // update all, list is already in the right order to process sequentially
            foreach (string item in orderedlist)
            {
                ContosoTopologyNode actualNode = this[item] as ContosoTopologyNode;
                if (actualNode != null)
                {
                    ContosoAggregatedOeeKpiHistogram oeeKpiHistogram = actualNode[histogram];
                    for (int i = 0; i < oeeKpiHistogram.Intervals.Count; i++)
                    {
                        ContosoAggregatedOeeKpiTimeSpan oeeKpiNode = oeeKpiHistogram[i];
                        oeeKpiNode.Reset();

                        List <string> children = actualNode.GetChildren();
                        foreach (string child in children)
                        {
                            // special case for child as OPC UA server
                            Station childStation = this[child] as Station;
                            if (childStation != null)
                            {
                                ContosoAggregatedOeeKpiTimeSpan oeeKpiChild = childStation[histogram, i];
                                // add all Oee and KPI using the metric for stations
                                oeeKpiNode.AddStation(oeeKpiChild);
                                // update alerts for the station
                                ContosoAggregatedOeeKpiHistogram childOeeKpiHistogram = childStation[histogram];
                                if (childOeeKpiHistogram.CheckAlerts)
                                {
                                    UpdateKPIAndOeeAlerts(childStation);
                                }
                            }
                            else
                            {
                                ContosoTopologyNode childNode = this[child] as ContosoTopologyNode;
                                if (childNode != null)
                                {
                                    ContosoAggregatedOeeKpiTimeSpan oeeKpiChild = childNode[histogram, i];
                                    // add all Oee and KPI using the default metric
                                    oeeKpiNode.Add(oeeKpiChild);
                                }
                                else
                                {
                                    continue;
                                }
                            }
                        }
                        if (oeeKpiNode.EndTime > oeeKpiHistogram.EndTime)
                        {
                            oeeKpiHistogram.EndTime = oeeKpiNode.EndTime;
                        }
                    }

                    if (oeeKpiHistogram.CheckAlerts)
                    {
                        // check for alerts
                        UpdateKPIAndOeeAlerts(actualNode);
                        updateStatus = true;
                    }
                }
            }
            if (updateStatus)
            {
                UpdateAllStatusTopology();
            }
        }
        /// <summary>
        /// Gets alert information for the given topology node and all nodes below it.
        /// Multiple alerts with the same cause are consolidated and only the time information for the newest alert is returned.
        /// This information will be shown in the UX.
        /// </summary>
        public List <ContosoAlertInfo> GetAlerts(string key)
        {
            List <ContosoAlert>     alertList       = GetAllAlerts(key);
            List <ContosoAlertInfo> dashboardAlerts = new List <ContosoAlertInfo>();
            ContosoAlertCause       dummyAlertCause = new ContosoAlertCause();
            ContosoAlertInfo        dashboardAlert  = new ContosoAlertInfo();

            if (alertList.Count == 0)
            {
                return(dashboardAlerts);
            }

            List <string> allNodeKey = GetAllChildren(key);

            // Add key itself as well.
            allNodeKey.Add(key);
            foreach (var nodeKey in allNodeKey)
            {
                ContosoTopologyNode topologyNode = TopologyTable[nodeKey] as ContosoTopologyNode;
                List <ContosoAlert> childAlerts  = topologyNode.Alerts;
                if (childAlerts.Count > 0)
                {
                    if (topologyNode.GetType() == typeof(Station))
                    {
                        Station station = topologyNode as Station;
                        // The OPC UA nodes only generate value alerts.
                        foreach (var opcNode in station.NodeList)
                        {
                            foreach (ContosoAlertCause alertCause in Enum.GetValues(dummyAlertCause.GetType()))
                            {
                                if (alertCause == ContosoAlertCause.AlertCauseValueBelowMinimum ||
                                    alertCause == ContosoAlertCause.AlertCauseValueAboveMaximum)
                                {
                                    // Aggregate similar alerts.
                                    List <ContosoAlert> sameCauseAlerts = childAlerts.FindAll(x => (x.Cause == alertCause && x.Key == nodeKey && x.SubKey == opcNode.NodeId && x.Time != DateTime.MinValue));
                                    if (sameCauseAlerts.Count > 0)
                                    {
                                        // Find the newest alert.
                                        long         newestTicks = sameCauseAlerts.Max(x => x.Time.Ticks);
                                        ContosoAlert alert       = sameCauseAlerts.Find(x => x.Time.Ticks == newestTicks);

                                        // Create alert dashboard info and add it to the dashboard alert list.
                                        dashboardAlert = CreateDashboardAlertInfo(alert, sameCauseAlerts.Count, nodeKey);
                                        dashboardAlerts.Add(dashboardAlert);
                                    }
                                }
                            }
                        }

                        // For the station node we handle performance alerts.
                        foreach (ContosoAlertCause alertCause in Enum.GetValues(dummyAlertCause.GetType()))
                        {
                            if (alertCause == ContosoAlertCause.AlertCauseValueBelowMinimum ||
                                alertCause == ContosoAlertCause.AlertCauseValueAboveMaximum)
                            {
                                continue;
                            }
                            // Aggregate similar alerts.
                            List <ContosoAlert> sameCauseAlerts = childAlerts.FindAll(x => (x.Cause == alertCause && x.Key == nodeKey && x.Time != DateTime.MinValue));
                            if (sameCauseAlerts.Count > 0)
                            {
                                // Find the newest alert.
                                long         newestTicks = sameCauseAlerts.Max(x => x.Time.Ticks);
                                ContosoAlert alert       = sameCauseAlerts.Find(x => x.Time.Ticks == newestTicks);

                                // Create alert dashboard info and add it to the dashboard alert list.
                                dashboardAlert = CreateDashboardAlertInfo(alert, sameCauseAlerts.Count, nodeKey);
                                dashboardAlerts.Add(dashboardAlert);
                            }
                        }
                    }
                    else
                    {
                        foreach (ContosoAlertCause alertCause in Enum.GetValues(dummyAlertCause.GetType()))
                        {
                            // Aggregate similar alerts.
                            List <ContosoAlert> sameCauseAlerts = childAlerts.FindAll(x => (x.Cause == alertCause && x.Key == nodeKey && x.Time != DateTime.MinValue));
                            if (sameCauseAlerts.Count > 0)
                            {
                                // Find the newest alert.
                                long         newestTicks = sameCauseAlerts.Max(x => x.Time.Ticks);
                                ContosoAlert alert       = sameCauseAlerts.Find(x => x.Time.Ticks == newestTicks);

                                // Create alert dashboard info and add it to the dashboard alert list.
                                dashboardAlert = CreateDashboardAlertInfo(alert, sameCauseAlerts.Count, nodeKey);
                                dashboardAlerts.Add(dashboardAlert);
                            }
                        }
                    }
                }
            }

            dashboardAlerts.Sort(delegate(ContosoAlertInfo x, ContosoAlertInfo y)
            {
                if (x.Time < y.Time)
                {
                    return(1);
                }
                if (x.Time == y.Time)
                {
                    return(0);
                }
                return(-1);
            });
            return(dashboardAlerts);
        }