/// <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); }
/// <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> /// 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(); }