public int ValidateTagNames(string pitag1, string pitag2, string pitag3, string pitag4, string pitag5, int NumTags) { try { MyTag1 = PIPoint.FindPIPoint(MyPiServer, pitag1); MyAtrbTag1 = AFAttribute.FindAttribute(@"\\" + PIServerName + @"\" + MyTag1.Name, null); if (NumTags > 1) { MyTag2 = PIPoint.FindPIPoint(MyPiServer, pitag2); MyAtrbTag2 = AFAttribute.FindAttribute(@"\\" + PIServerName + @"\" + MyTag2.Name, null); } if (NumTags > 2) { MyTag3 = PIPoint.FindPIPoint(MyPiServer, pitag3); MyAtrbTag3 = AFAttribute.FindAttribute(@"\\" + PIServerName + @"\" + MyTag3.Name, null); } if (NumTags > 3) { MyTag4 = PIPoint.FindPIPoint(MyPiServer, pitag4); MyAtrbTag4 = AFAttribute.FindAttribute(@"\\" + PIServerName + @"\" + MyTag4.Name, null); } if (NumTags > 4) { MyTag5 = PIPoint.FindPIPoint(MyPiServer, pitag5); MyAtrbTag5 = AFAttribute.FindAttribute(@"\\" + PIServerName + @"\" + MyTag5.Name, null); } } catch { return(1); } return(0); }
public LimitCalculation(CalculationPreference preference, string calculationName) { this.calculationName = calculationName; try { logger.Info($"Starting calculations for {calculationName}"); string afattributepath = preference.sensorPath; string eventQuery = preference.eventFrameQuery; calculationsToPerform = preference.getTraitDictionary(); offset = preference.offset; this.preference = preference; sensor = AFAttribute.FindAttribute(afattributepath, null); pisystem = sensor.PISystem; afdatabase = sensor.Database; foreach (KeyValuePair <AFAttributeTrait, string> pair in calculationsToPerform) { bounds[pair.Key] = new AFValues(); AFAttribute possibleAttribute = sensor.GetAttributeByTrait(pair.Key); boundAttributes[pair.Key] = possibleAttribute; logger.Info($"Will perform calculation for limit: {pair.Key}"); if (possibleAttribute == null) { logger.Error($"{calculationName}: The limit {pair.Key} is not defined yet is used."); } } eventFrameQuery = new AFEventFrameSearch(afdatabase, "eventFrameSearch", eventQuery); } catch (System.Exception e) { logger.Error($"{calculationName} the following error occured: {e.Message}"); } logger.Info($"{calculationName}: Doing the initial run"); InitialRun(); }
private AFAttributeList LoadParameters() { if (Attribute == null || Attribute.Element == null) { throw new ApplicationException("Attribute and/or element is null"); } var paramAttributes = new AFAttributeList(); if (!string.IsNullOrEmpty(fPointAttribute)) { AFDatabase db = Attribute.Database; if (db == null) { throw new ApplicationException("No database found"); } // find Attribute's object by it name from parameters var ptAttr = AFAttribute.FindAttribute(fPointAttribute, Attribute); if (ptAttr == null) { throw new ApplicationException(string.Format(Resources.ERR_AttributeHasNotBeenFound, fPointAttribute)); } paramAttributes.Add(ptAttr); } else { throw new ApplicationException("Name of PI Point attribute is null or empty"); } return paramAttributes; }
public LimitCalculation(CalculationPreference preference) { this.preference = preference; string afattributepath = preference.sensorPath; string eventQuery = preference.eventFrameQuery; calculationsToPerform = preference.getTraitDictionary(); foreach (KeyValuePair <AFAttributeTrait, string> pair in calculationsToPerform) { bounds[pair.Key] = new AFValues(); } sensor = AFAttribute.FindAttribute(afattributepath, null); pisystem = sensor.PISystem; afdatabse = sensor.Database; foreach (KeyValuePair <AFAttributeTrait, string> pair in calculationsToPerform) { boundAttributes[pair.Key] = sensor.GetAttributeByTrait(pair.Key); } eventFrameQuery = new AFEventFrameSearch(afdatabse, "eventFrameSearch", eventQuery); List <AFSearchToken> tokens = eventFrameQuery.Tokens.ToList(); tokens.RemoveAll(t => t.Filter == AFSearchFilter.InProgress || t.Filter == AFSearchFilter.Start || t.Filter == AFSearchFilter.End); timeLessQuery = new AFEventFrameSearch(afdatabse, "AllEventFrames", tokens); InitialRun(); }
private void configurationTreeView_AfterSelect(object sender, TreeViewEventArgs e) { AFTreeNode node = (AFTreeNode)e.Node; AFElement selectedCalculation = (AFElement)node.AFObject; if (!selectedCalculation.IsRoot) { calculationName.Text = selectedCalculation.Name; CalculationPreference preference = CalculationPreference.CalculationPreferenceFromJSON((string)selectedCalculation.Attributes["configuration"].GetValue().Value); queryTextBox.Text = preference.eventFrameQuery; Dictionary <AFAttributeTrait, string> limitCalculations = preference.getTraitDictionary(); foreach (AFAttributeTrait trait in AFAttributeTrait.AllLimits) { ComboBox comboBox = (ComboBox)panel1.Controls[trait.Name]; comboBox.Text = "None"; } foreach (KeyValuePair <AFAttributeTrait, string> pair in limitCalculations) { ComboBox comboBox = (ComboBox)panel1.Controls[pair.Key.Name]; comboBox.Text = pair.Value; } offsetSetting.Text = preference.offset.ToString(); AFAttribute sensor = AFAttribute.FindAttribute(preference.sensorPath, db); afDatabasePicker.AFDatabase = sensor.Database; afTreeView.AFRoot = sensor.Database.Elements; afTreeView.AFSelect(sensor, db, preference.sensorPath); afTreeView.SelectedNode.EnsureVisible(); afTreeView.Focus(); } }
static void PrintInterpolated(AFDatabase database, string meterName, string startTime, string endTime, TimeSpan timeSpan) { Console.WriteLine(string.Format("Print Interpolated Values - Meter: {0}, Start: {1}, End: {2}", meterName, startTime, endTime)); AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database); AFTime start = new AFTime(startTime); AFTime end = new AFTime(endTime); AFTimeRange timeRange = new AFTimeRange(start, end); AFTimeSpan interval = new AFTimeSpan(timeSpan); AFValues vals = attr.Data.InterpolatedValues( timeRange: timeRange, interval: interval, desiredUOM: null, filterExpression: null, includeFilteredValues: false); foreach (AFValue val in vals) { Console.WriteLine("Timestamp (Local): {0}, Value: {1:0.00} {2}", val.Timestamp.LocalTime, val.Value, val?.UOM.Abbreviation); } Console.WriteLine(); }
public static void PrintHistorical(AFDatabase database, string meterName, string startTime, string endTime) { if (database == null) { throw new ArgumentNullException(nameof(database)); } Console.WriteLine(string.Format("Print Historical Values - Meter: {0}, Start: {1}, End: {2}", meterName, startTime, endTime)); AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database); AFTime start = new AFTime(startTime); AFTime end = new AFTime(endTime); AFTimeRange timeRange = new AFTimeRange(start, end); AFValues vals = attr.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: database.PISystem.UOMDatabase.UOMs["kilojoule"], filterExpression: null, includeFilteredValues: false); foreach (AFValue val in vals) { Console.WriteLine("Timestamp (UTC): {0}, Value (kJ): {1}", val.Timestamp.UtcTime, val.Value); } Console.WriteLine(); }
static void PrintHourlyAverage(AFDatabase database, string meterName, string startTime, string endTime) { Console.WriteLine(string.Format("Print Hourly Average - Meter: {0}, Start: {1}, End: {2}", meterName, startTime, endTime)); AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database); AFTime start = new AFTime(startTime); AFTime end = new AFTime(endTime); AFTimeRange timeRange = new AFTimeRange(start, end); IDictionary <AFSummaryTypes, AFValues> vals = attr.Data.Summaries( timeRange: timeRange, summaryDuration: new AFTimeSpan(TimeSpan.FromHours(1)), summaryType: AFSummaryTypes.Average, calcBasis: AFCalculationBasis.TimeWeighted, timeType: AFTimestampCalculation.EarliestTime); foreach (AFValue val in vals[AFSummaryTypes.Average]) { Console.WriteLine("Timestamp (Local): {0:yyyy-MM-dd HH\\h}, Value: {1:0.00} {2}", val.Timestamp.LocalTime, val.Value, val?.UOM.Abbreviation); } Console.WriteLine(); }
private void ImplicitConnection() { PISystems piSystems = new PISystems(); PISystem piSystem = piSystems["<AFSERVER>"]; // At this point, no connection is made. AFAttribute afAttribute = AFAttribute.FindAttribute(@"NuGreen\NuGreen\Houston|Environment", piSystem); // Now a connection is made by first data access. }
static void PrintInterpolated(AFDatabase database, string meterName, string start, string end, TimeSpan interval) { AFTime startTime = new AFTime(start); AFTime endTime = new AFTime(end); AFTimeRange timeRange = new AFTimeRange(startTime, endTime); AFAttribute att = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database); AFTimeSpan intervalNew = new AFTimeSpan(interval); AFValues values = att.Data.InterpolatedValues(timeRange: timeRange, interval: intervalNew, desiredUOM: null, filterExpression: null, includeFilteredValues: false); }
private void ExplicitConnection() { PISystems piSystems = new PISystems(); PISystem piSystem = piSystems["<AFSERVER>"]; // At this point, no connection is made. piSystem.Connect(); // Now a connection is made by explicit call. AFAttribute afAttribute = AFAttribute.FindAttribute(@"NuGreen\NuGreen\Houston|Environment", piSystem); }
public static void SwapValues(AFDatabase database, string meter1, string meter2, string startTime, string endTime) { Console.WriteLine(string.Format("Swap values for meters: {0}, {1} between {2} and {3}", meter1, meter2, startTime, endTime)); // NOTE: This method does not ensure that there is no data loss if there is failure. // Persist the data first in case you need to rollback. AFAttribute attr1 = AFAttribute.FindAttribute(@"\Meters\" + meter1 + @"|Energy Usage", database); AFAttribute attr2 = AFAttribute.FindAttribute(@"\Meters\" + meter2 + @"|Energy Usage", database); AFTime start = new AFTime(startTime); AFTime end = new AFTime(endTime); AFTimeRange timeRange = new AFTimeRange(start, end); // Get values to delete for meter1 AFValues valsToRemove1 = attr1.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: false); // Get values to delete for meter2 AFValues valsToRemove2 = attr2.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: false); if (valsToRemove1.Count == 0 && valsToRemove2.Count == 0) { throw new Exception("There are no values to swap."); } List <AFValue> valsToRemove = valsToRemove1.ToList(); valsToRemove.AddRange(valsToRemove2.ToList()); // Remove the values AFListData.UpdateValues(valsToRemove, AFUpdateOption.Remove); // Create new AFValues from the other meter and assign them to this meter List <AFValue> valsToAdd1 = valsToRemove2.Select(v => new AFValue(attr1, v.Value, v.Timestamp)).ToList(); List <AFValue> valsToAdd2 = valsToRemove1.Select(v => new AFValue(attr2, v.Value, v.Timestamp)).ToList(); List <AFValue> valsCombined = valsToAdd1; valsCombined.AddRange(valsToAdd2); AFListData.UpdateValues(valsCombined, AFUpdateOption.Insert); Console.WriteLine(); }
public void SwapValuesEx3() { var meter1 = "Meter001"; var meter2 = "Meter002"; var startDateRelative = "*-5m"; var endDateRelative = "*"; var startDate = new AFTime(startDateRelative).ToString(); var endDate = new AFTime(endDateRelative).ToString(); AFTimeRange timeRange = new AFTimeRange(startDate, endDate); AFAttribute attr1 = AFAttribute.FindAttribute(@"\Meters\" + meter1 + @"|Energy Usage", Database); AFAttribute attr2 = AFAttribute.FindAttribute(@"\Meters\" + meter2 + @"|Energy Usage", Database); var valAtt1Before = attr1.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: true).First(); var valAtt2Before = attr2.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: true).First(); using (StringWriter sw = new StringWriter()) { Console.SetOut(sw); Ex3ReadingAndWritingDataSln.Program3.SwapValues(Database, meter1, meter2, startDate, endDate); var actual = sw.ToString(); Assert.Contains("Swap values for meters: ", actual); } var valAtt1After = attr1.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: true).First(); var valAtt2After = attr2.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: true).First(); Assert.Equal(valAtt1Before.Value.ToString(), valAtt2After.Value.ToString()); Assert.Equal(valAtt2Before.Value.ToString(), valAtt1After.Value.ToString()); }
// Attribute level public static AFAttribute FindAttribute(AFDatabase database, string attributePath) { AFAttribute attribute = AFAttribute.FindAttribute(attributePath, database); try { PIPoint tag = attribute.PIPoint; return(attribute); } catch { Console.WriteLine($"{attributePath} PIPoint could not be found, returning null"); return(null); } }
static void PrintHistorical(AFDatabase database, string meterName, string startTime, string endTime) { Console.WriteLine("Print Interpolated Values - Meter: {0}, Start: {1}, End: {2}", meterName, startTime, endTime); AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database); AFTime start = new AFTime(startTime); AFTime end = new AFTime(endTime); AFTimeRange timeRange = new AFTimeRange(start, end); AFValues vals = attr.Data.RecordedValues(timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: false); foreach (AFValue val in vals) { Console.WriteLine("Timestamp (UTC): {0}, value (kJ): {1}", val.Timestamp.UtcTime, val.Value); } }
private AFAttributeList LoadParameters() { if (Attribute == null || Attribute.Element == null) { throw new ApplicationException("Attribute and/or element is null"); } AFDatabase afDB = base.Database; AFTable afTable = afDB.Tables[fTableName]; if (afTable == null) { throw new ArgumentException("Table not found"); } fDataRows = afTable.Table.Select(); var paramAttributes = new AFAttributeList(); if (!string.IsNullOrEmpty(fAttributeName)) { // find Attribute's object by it name from parameters (this attribute contains key values) var refAttr = AFAttribute.FindAttribute(fAttributeName, Attribute); if (refAttr == null) { throw new ApplicationException(string.Format(Resources.ERR_AttributeHasNotBeenFound, fAttributeName)); } if (!Extensions.IsNumericType(refAttr.Type)) { throw new ApplicationException(string.Format("The attribute `{0}` has no numeric type ", fAttributeName)); } paramAttributes.Add(refAttr); } else { throw new ApplicationException("Name of lookup attribute is null or empty"); } return(paramAttributes); }
private void LoadParameters() { if (Attribute == null || Attribute.Element == null) { return; } if (!string.IsNullOrEmpty(fSourceAttributeName) && fParamAttributes == null) { fParamAttributes = new AFAttributeList(); var attr = AFAttribute.FindAttribute(fSourceAttributeName, Attribute); if (attr == null) { throw new ArgumentException(string.Format(Resources.ERR_AttributeHasNotBeenFound, SourceAttributeName)); } else { fParamAttributes.Add(attr); } fLastLoadAttribute = DateTime.UtcNow; } }
private AFAttributeList LoadParameters() { if (Attribute == null || Attribute.Element == null) { throw new ApplicationException("Attribute and/or element is null"); } var paramAttributes = new AFAttributeList(); if (!string.IsNullOrEmpty(fAttributeName)) { var attr = AFAttribute.FindAttribute(fAttributeName, Attribute); if (attr == null) { throw new ArgumentException(string.Format(Resources.ERR_AttributeHasNotBeenFound, fAttributeName)); } else { paramAttributes.Add(attr); } } return(paramAttributes); }
static void PrintInterpolated(AFDatabase database, string meterName, string startTime, string endTime, TimeSpan timeSpan) { AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database); // Your code here }
static void PrintHourlyAverage(AFDatabase database, string meterName, string startTime, string endTime) { AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database); // Your code here }
private AFValue Calculate(AFValues inputValues) { MethodEnum method; if (!Enum.TryParse(ConvertMethod, out method)) { throw new ArgumentException(Resources.ERR_NoBitSpecified); } if (inputValues == null || inputValues.Count == 0) { throw new ArgumentException(Resources.ERR_NoInputValues); } AFValue inVal = inputValues[0]; AFDatabase db = inVal.Attribute.Database; if (db == null) { throw new ArgumentException("No db found"); } object objVal = inVal.Value; if (inVal.IsGood && objVal != null) { if (TransformerCore.IsNumericVal(objVal)) { AFValue newVal; switch (method) { default: case MethodEnum.None: newVal = new AFValue(objVal, inVal.Timestamp, inVal.UOM); break; case MethodEnum.Simple: var targetUOMAttrS = AFAttribute.FindAttribute("UOM", Attribute); newVal = Convert(inVal, db, targetUOMAttrS); break; case MethodEnum.Full: var sourceUOMAttr = AFAttribute.FindAttribute("UOM", inVal.Attribute); newVal = Convert(inVal, db, sourceUOMAttr); var targetUOMAttrF = AFAttribute.FindAttribute("UOM", Attribute); newVal = Convert(newVal, db, targetUOMAttrF); break; } return(newVal); } else { throw new ArgumentException(Resources.ERR_SourceAttributeMustBeAnIntegerType); } } else { return(AFValue.CreateSystemStateValue(Attribute, AFSystemStateCode.BadInput, inVal.Timestamp)); } }
private AFAttributeList LoadParameters(object context) { if (Attribute == null || Attribute.Element == null) { throw new ApplicationException("Attribute and/or element is null"); } var paramAttributes = new AFAttributeList(); if (!string.IsNullOrEmpty(AttributeName)) { AFDatabase db = Attribute.Database; if (db == null) { throw new ApplicationException("No database found"); } // find Attribute's object by it name from parameters (this attribute contains link to other attribute with real values) string name; if (IsAttributeSubstituted(AttributeName)) { string value = SubstituteParameters(AttributeName, this, null, null); name = SubstituteAttributeValues(context, null, value); } else { name = AttributeName; } var refAttr = AFAttribute.FindAttribute(name, Attribute); if (refAttr == null) { throw new ApplicationException(string.Format(Resources.ERR_AttributeHasNotBeenFound, name)); } if (MethodType == RefAttributeType.DynamicPath) { AFValue inVal = refAttr.GetValue(); object objVal = inVal.Value; if (inVal.IsGood && objVal != null) { if (Extensions.IsStringVal(objVal)) { string attrName; try { attrName = objVal.ToString(); } catch (Exception ex) { throw new ArgumentException(string.Format(Resources.ERR_UnrecognizedRefValue, inVal.ToString(), name)); } var lookupAttr = AFAttribute.FindAttribute(attrName, db); if (lookupAttr == null) { throw new ArgumentException(string.Format(Resources.ERR_AttributeLookupHasNotBeenFound, attrName)); } else { paramAttributes.Add(lookupAttr); } } else { throw new ApplicationException(Resources.ERR_SourceAttributeMustBeStringType); } } else { throw new ApplicationException(string.Format(Resources.ERR_UnrecognizedRefValue, "null or bad", name)); } } else { paramAttributes.Add(refAttr); } if (TimestampType == "Attribute") { var tsAttr = AFAttribute.FindAttribute(TimestampSource, Attribute); if (tsAttr == null) { throw new ApplicationException(string.Format(Resources.ERR_AttributeHasNotBeenFound, TimestampSource)); } paramAttributes.Add(tsAttr); } } else { throw new ApplicationException("Name of lookup attribute is null or empty"); } return(paramAttributes); }
// 2 shifts (2), 3 shifts (3), Unknown (0), Day (-1), Month (-2), Year (-3) public override AFValue GetValue(object context, object timeContext, AFAttributeList inputAttributes, AFValues inputValues) { AFTime time; if (timeContext is AFTime) { time = (AFTime)timeContext; } else if (timeContext is AFTimeRange) { var timeRange = (AFTimeRange)timeContext; time = (Attribute.Element is AFEventFrame) ? timeRange.StartTime : timeRange.EndTime; } else if (timeContext is DateTime) { time = new AFTime((DateTime)timeContext); } else { time = AFTime.NowInWholeSeconds; } int shiftMode = SRHelper.TryGetShiftMode(fShiftMode); if (shiftMode == 0) { try { var attr = AFAttribute.FindAttribute(fShiftMode, Attribute); if (attr == null) { throw new ArgumentException(string.Format(Resources.ERR_AttributeHasNotBeenFound, ShiftMode)); } else { object val = attr.GetValue().Value; if (SRHelper.IsIntVal(val)) { shiftMode = (int)SRHelper.ConvertToType(val, TypeCode.Int32); } else { shiftMode = 0; } } } catch { shiftMode = 0; } } if (shiftMode == 0) { throw new ArgumentException(Resources.ERR_SourceAttributeMustBeAnIntegerType); } DateTime localTime = time.LocalTime; string[] wsParams; if (shiftMode < 0) { wsParams = SRHelper.GetWorkMacroParams(localTime, shiftMode, fStartOffset); } else { wsParams = SRHelper.GetWorkShiftParams(localTime, shiftMode, fStartOffset); } wsParams[3] = SRHelper.GetPITimeStampStr(localTime); return(new AFValue(base.Attribute, wsParams, time, Attribute.DefaultUOM)); }
public void SwapValuesEx3Sln() { var meter1 = "Meter001"; var meter2 = "Meter002"; var startDateRelative = "*-5m"; var endDateRelative = "*"; var startDate = new AFTime(startDateRelative).ToString(); var endDate = new AFTime(endDateRelative).ToString(); AFTimeRange timeRange = new AFTimeRange(startDate, endDate); AFAttribute attr1 = AFAttribute.FindAttribute(@"\Meters\" + meter1 + @"|Energy Usage", Database); AFAttribute attr2 = AFAttribute.FindAttribute(@"\Meters\" + meter2 + @"|Energy Usage", Database); var valAtt1Before = attr1.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: true).First(); var valAtt2Before = attr2.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: true).First(); using (StringWriter sw = new StringWriter()) { Console.SetOut(sw); Ex3ReadingAndWritingDataSln.Program3.SwapValues(Database, meter1, meter2, startDate, endDate); var actual = sw.ToString(); Assert.Contains("Swap values for meters: ", actual); } var valsAtt1After = attr1.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: true); var valAtt1After = valsAtt1After.First(); var valsAtt2After = attr2.Data.RecordedValues( timeRange: timeRange, boundaryType: AFBoundaryType.Inside, desiredUOM: null, filterExpression: null, includeFilteredValues: true); var valAtt2After = valsAtt2After.First(); // adding console logging to see what is going on here, because this fails occassionally { var standardOutput = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true, }; Console.SetOut(standardOutput); Console.WriteLine($"{valAtt1Before.Value}, {valAtt1After.Value}"); Console.WriteLine($"{valAtt2Before.Value}, {valAtt2After.Value}"); Console.WriteLine("All vals att1 after:"); foreach (var val in valsAtt1After) { Console.WriteLine($"{val.Timestamp} | {val.Value}"); } Console.WriteLine("All vals att2 after:"); foreach (var val in valsAtt2After) { Console.WriteLine($"{val.Timestamp} | {val.Value}"); } } Assert.Equal(valAtt1Before.Value.ToString(), valAtt2After.Value.ToString()); Assert.Equal(valAtt2Before.Value.ToString(), valAtt1After.Value.ToString()); }