public String GeneratePowerSensorJSONOutput(PowerSensorOutputs OutputType, String ObjectName, DateTime StartDateTime, DateTime EndDateTime) { StringBuilder Output = new StringBuilder(); StringBuilder Output2 = new StringBuilder(); // TODO: there should be an appropriate caching algorithm in the sensor data... if (OutputType == PowerSensorOutputs.HourkWh) { #region Hour kWh Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); UInt64 SerializerCounter = 0; lock (sensor_data.InMemoryIndex) { foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { Output2.Clear(); Output2.Append("["); Output2.Append(dataobject.Timecode.JavaScriptTimestamp()); Output2.Append(","); //Double Value = dataobject.Value / 1000; Output2.Append(dataobject.Value.ToString().Replace(',', '.')); Output2.Append("]"); } } } } } } } } Output.Append(Output2.ToString()); #endregion } if (OutputType == PowerSensorOutputs.HourPeakkWh) { #region Hour Peak kWh Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); UInt64 SerializerCounter = 0; lock (sensor_data.InMemoryIndex) { foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_peak") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { Output2.Clear(); Output2.Append("["); Output2.Append(dataobject.Timecode.JavaScriptTimestamp()); Output2.Append(","); //Double Value = dataobject.Value / 1000; Output2.Append(dataobject.Value.ToString().Replace(',', '.')); Output2.Append("]"); } } } } } } } } Output.Append(Output2.ToString()); #endregion } if (OutputType == PowerSensorOutputs.CalculatedkWhCounterTotal) { #region Calculated kWh Counter (based on last known manual reading) UInt64 SerializerCounter = 0; // find the right sensor manual reading... foreach (PowerConsumptionSensor _manual_reading in PowerSensorConfiguration.PowerConsumptionSensors) { DateTime ManualMeasurementDate = DateTime.MinValue; Double ManualMeasurementValue = Double.MinValue; Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); if (_manual_reading.PowerSensorName == ObjectName) { ManualMeasurementDate = _manual_reading.InitialPowerSensorDate; ManualMeasurementValue = _manual_reading.InitialPowerSensorValue; // here comes the fun StartDateTime = ManualMeasurementDate; Double PowerSensorCalculatedValue = ManualMeasurementValue; DateTime CurrentHourStart = StartDateTime; Double CurrentHourMeanValue = Double.MinValue; #region the lock lock (sensor_data.InMemoryIndex) { foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { // okay, we got the right sensor data element type with the right name... // calculate the time difference between hour start and current data object TimeSpan ts = new TimeSpan(dataobject.Timecode.Ticks - CurrentHourStart.Ticks); if (ts.TotalMinutes >= 60) { // we have a full hour...add to the calculated value and reset hour values CurrentHourStart = dataobject.Timecode; PowerSensorCalculatedValue += CurrentHourMeanValue / 1000; //Console.WriteLine(" -> " + PowerSensorCalculatedValue + " : "+CurrentHourMeanValue + "("+dataobject.Timecode.ToShortDateString()+")"); CurrentHourMeanValue = Double.MinValue; } else { if (CurrentHourMeanValue == Double.MinValue) { CurrentHourMeanValue = dataobject.Value; } else { CurrentHourMeanValue = (CurrentHourMeanValue + dataobject.Value) / 2; } } } } } } } } } } #endregion // add the corrector value PowerSensorCalculatedValue = PowerSensorCalculatedValue + _manual_reading.Corrector; Output.Append("["); Output.Append(DateTime.Now.JavaScriptTimestamp()); Output.Append(","); Output.Append(PowerSensorCalculatedValue.ToString().Replace(',', '.')); Output.Append("]"); } } #endregion } if (OutputType == PowerSensorOutputs.CalculatedDailykWh) { #region Calculated Daily kWh Counter Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); bool firstdataset = true; UInt64 SerializerCounter = 0; DateTime CurrentHourStart = StartDateTime; Double CurrentHourMeanValue = Double.MinValue; // TODO: there should be an appropriate caching algorithm in the sensor data... lock (sensor_data.InMemoryIndex) { Double DailyMeanValue = Double.MinValue; Int32 HourNumber = 0; foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { // calculate the time difference between hour start and current data object TimeSpan ts = new TimeSpan(dataobject.Timecode.Ticks - CurrentHourStart.Ticks); if (ts.TotalMinutes >= 60) { // we have a full hour...add to the calculated value and reset hour values CurrentHourStart = dataobject.Timecode; HourNumber++; if (HourNumber >= 24) { if (!firstdataset) { Output.Append(","); } else { firstdataset = false; } // we have 24 hours completed Output.Append("["); Output.Append(dataobject.Timecode.JavaScriptTimestamp()); Output.Append(","); //CurrentHourMeanValue = CurrentHourMeanValue / 100; Output.Append(CurrentHourMeanValue.ToString().Replace(',', '.')); Output.Append("]"); HourNumber = 0; } else { if (DailyMeanValue == Double.MinValue) { DailyMeanValue = CurrentHourMeanValue; } else { DailyMeanValue = (DailyMeanValue + CurrentHourMeanValue) / 2; } } CurrentHourMeanValue = Double.MinValue; } else { if (CurrentHourMeanValue == Double.MinValue) { CurrentHourMeanValue = dataobject.Value; } else { CurrentHourMeanValue = (CurrentHourMeanValue + dataobject.Value) / 2; } } } } } } } } } ConsoleOutputLogger_.WriteLineToScreenOnly("Generated JSON Dataset with " + SerializerCounter + " Elements"); } #endregion } if (OutputType == PowerSensorOutputs.CalculatedHourlykWh) { #region Calculated Hourly kWh Counter Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); bool firstdataset = true; UInt64 SerializerCounter = 0; DateTime CurrentHourStart = StartDateTime; Double CurrentHourMeanValue = Double.MinValue; // TODO: there should be an appropriate caching algorithm in the sensor data... lock (sensor_data.InMemoryIndex) { foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { // calculate the time difference between hour start and current data object TimeSpan ts = new TimeSpan(dataobject.Timecode.Ticks - CurrentHourStart.Ticks); if (ts.TotalMinutes >= 60) { // we have a full hour...add to the calculated value and reset hour values CurrentHourStart = dataobject.Timecode; if (CurrentHourMeanValue > 0) { if (!firstdataset) { Output.Append(","); } else { firstdataset = false; } // we have 24 hours completed Output.Append("["); Output.Append(dataobject.Timecode.JavaScriptTimestamp()); Output.Append(","); //CurrentHourMeanValue = CurrentHourMeanValue / 100; Output.Append(CurrentHourMeanValue.ToString().Replace(',', '.')); Output.Append("]"); } CurrentHourMeanValue = Double.MinValue; } else { if (CurrentHourMeanValue == Double.MinValue) { CurrentHourMeanValue = dataobject.Value; } else { CurrentHourMeanValue = (CurrentHourMeanValue + dataobject.Value) / 2; } } } } } } } } } ConsoleOutputLogger_.WriteLineToScreenOnly("Generated JSON Dataset with " + SerializerCounter + " Elements"); } #endregion } if (OutputType == PowerSensorOutputs.CalculateWeeklykWh) { #region Calculated Weekly kWh Counter Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); bool firstdataset = true; UInt64 SerializerCounter = 0; DateTime CurrentHourStart = StartDateTime; Double CurrentHourMeanValue = Double.MinValue; // TODO: there should be an appropriate caching algorithm in the sensor data... lock (sensor_data.InMemoryIndex) { Double DailyMeanValue = Double.MinValue; Int32 HourNumber = 0; foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { // calculate the time difference between hour start and current data object TimeSpan ts = new TimeSpan(dataobject.Timecode.Ticks - CurrentHourStart.Ticks); if (ts.TotalMinutes >= 60) { // we have a full hour...add to the calculated value and reset hour values CurrentHourStart = dataobject.Timecode; HourNumber++; if (HourNumber >= 168) { if (!firstdataset) { Output.Append(","); } else { firstdataset = false; } // we have 24 hours completed Output.Append("["); Output.Append(dataobject.Timecode.JavaScriptTimestamp()); Output.Append(","); //CurrentHourMeanValue = CurrentHourMeanValue / 100; Output.Append(CurrentHourMeanValue.ToString().Replace(',', '.')); Output.Append("]"); HourNumber = 0; } else { if (DailyMeanValue == Double.MinValue) { DailyMeanValue = CurrentHourMeanValue; } else { DailyMeanValue = (DailyMeanValue + CurrentHourMeanValue) / 2; } } CurrentHourMeanValue = Double.MinValue; } else { if (CurrentHourMeanValue == Double.MinValue) { CurrentHourMeanValue = dataobject.Value; } else { CurrentHourMeanValue = (CurrentHourMeanValue + dataobject.Value) / 2; } } } } } } } } } ConsoleOutputLogger_.WriteLineToScreenOnly("Generated JSON Dataset with " + SerializerCounter + " Elements"); } #endregion } Output.Append("]}"); //ConsoleOutputLogger.WriteLineToScreenOnly("Generated JSON Dataset with " + SerializerCounter + " Elements"); return(Output.ToString()); }
public String GeneratePowerSensorJSONOutput(PowerSensorOutputs OutputType, String ObjectName, DateTime StartDateTime, DateTime EndDateTime) { StringBuilder Output = new StringBuilder(); StringBuilder Output2 = new StringBuilder(); // TODO: there should be an appropriate caching algorithm in the sensor data... if (OutputType == PowerSensorOutputs.HourkWh) { #region Hour kWh Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); UInt64 SerializerCounter = 0; lock (sensor_data.InMemoryIndex) { foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { Output2.Clear(); Output2.Append("["); Output2.Append(dataobject.Timecode.JavaScriptTimestamp()); Output2.Append(","); //Double Value = dataobject.Value / 1000; Output2.Append(dataobject.Value.ToString().Replace(',', '.')); Output2.Append("]"); } } } } } } } } Output.Append(Output2.ToString()); #endregion } if (OutputType == PowerSensorOutputs.HourPeakkWh) { #region Hour Peak kWh Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); UInt64 SerializerCounter = 0; lock (sensor_data.InMemoryIndex) { foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_peak") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { Output2.Clear(); Output2.Append("["); Output2.Append(dataobject.Timecode.JavaScriptTimestamp()); Output2.Append(","); //Double Value = dataobject.Value / 1000; Output2.Append(dataobject.Value.ToString().Replace(',', '.')); Output2.Append("]"); } } } } } } } } Output.Append(Output2.ToString()); #endregion } if (OutputType == PowerSensorOutputs.CalculatedkWhCounterTotal) { #region Calculated kWh Counter (based on last known manual reading) UInt64 SerializerCounter = 0; // find the right sensor manual reading... foreach (PowerConsumptionSensor _manual_reading in PowerSensorConfiguration.PowerConsumptionSensors) { DateTime ManualMeasurementDate = DateTime.MinValue; Double ManualMeasurementValue = Double.MinValue; Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); if (_manual_reading.PowerSensorName == ObjectName) { ManualMeasurementDate = _manual_reading.InitialPowerSensorDate; ManualMeasurementValue = _manual_reading.InitialPowerSensorValue; // here comes the fun StartDateTime = ManualMeasurementDate; Double PowerSensorCalculatedValue = ManualMeasurementValue; DateTime CurrentHourStart = StartDateTime; Double CurrentHourMeanValue = Double.MinValue; #region the lock lock (sensor_data.InMemoryIndex) { foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { // okay, we got the right sensor data element type with the right name... // calculate the time difference between hour start and current data object TimeSpan ts = new TimeSpan(dataobject.Timecode.Ticks - CurrentHourStart.Ticks); if (ts.TotalMinutes >= 60) { // we have a full hour...add to the calculated value and reset hour values CurrentHourStart = dataobject.Timecode; PowerSensorCalculatedValue += CurrentHourMeanValue / 1000; //Console.WriteLine(" -> " + PowerSensorCalculatedValue + " : "+CurrentHourMeanValue + "("+dataobject.Timecode.ToShortDateString()+")"); CurrentHourMeanValue = Double.MinValue; } else { if (CurrentHourMeanValue == Double.MinValue) CurrentHourMeanValue = dataobject.Value; else { CurrentHourMeanValue = (CurrentHourMeanValue + dataobject.Value) / 2; } } } } } } } } } } #endregion // add the corrector value PowerSensorCalculatedValue = PowerSensorCalculatedValue + _manual_reading.Corrector; Output.Append("["); Output.Append(DateTime.Now.JavaScriptTimestamp()); Output.Append(","); Output.Append(PowerSensorCalculatedValue.ToString().Replace(',', '.')); Output.Append("]"); } } #endregion } if (OutputType == PowerSensorOutputs.CalculatedDailykWh) { #region Calculated Daily kWh Counter Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); bool firstdataset = true; UInt64 SerializerCounter = 0; DateTime CurrentHourStart = StartDateTime; Double CurrentHourMeanValue = Double.MinValue; // TODO: there should be an appropriate caching algorithm in the sensor data... lock (sensor_data.InMemoryIndex) { Double DailyMeanValue = Double.MinValue; Int32 HourNumber = 0; foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { // calculate the time difference between hour start and current data object TimeSpan ts = new TimeSpan(dataobject.Timecode.Ticks - CurrentHourStart.Ticks); if (ts.TotalMinutes >= 60) { // we have a full hour...add to the calculated value and reset hour values CurrentHourStart = dataobject.Timecode; HourNumber++; if (HourNumber >= 24) { if (!firstdataset) Output.Append(","); else firstdataset = false; // we have 24 hours completed Output.Append("["); Output.Append(dataobject.Timecode.JavaScriptTimestamp()); Output.Append(","); //CurrentHourMeanValue = CurrentHourMeanValue / 100; Output.Append(CurrentHourMeanValue.ToString().Replace(',', '.')); Output.Append("]"); HourNumber = 0; } else { if (DailyMeanValue == Double.MinValue) DailyMeanValue = CurrentHourMeanValue; else DailyMeanValue = (DailyMeanValue + CurrentHourMeanValue) / 2; } CurrentHourMeanValue = Double.MinValue; } else { if (CurrentHourMeanValue == Double.MinValue) CurrentHourMeanValue = dataobject.Value; else { CurrentHourMeanValue = (CurrentHourMeanValue + dataobject.Value) / 2; } } } } } } } } } ConsoleOutputLogger_.WriteLineToScreenOnly("Generated JSON Dataset with " + SerializerCounter + " Elements"); } #endregion } if (OutputType == PowerSensorOutputs.CalculatedHourlykWh) { #region Calculated Hourly kWh Counter Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); bool firstdataset = true; UInt64 SerializerCounter = 0; DateTime CurrentHourStart = StartDateTime; Double CurrentHourMeanValue = Double.MinValue; // TODO: there should be an appropriate caching algorithm in the sensor data... lock (sensor_data.InMemoryIndex) { foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { // calculate the time difference between hour start and current data object TimeSpan ts = new TimeSpan(dataobject.Timecode.Ticks - CurrentHourStart.Ticks); if (ts.TotalMinutes >= 60) { // we have a full hour...add to the calculated value and reset hour values CurrentHourStart = dataobject.Timecode; if (CurrentHourMeanValue > 0) { if (!firstdataset) Output.Append(","); else firstdataset = false; // we have 24 hours completed Output.Append("["); Output.Append(dataobject.Timecode.JavaScriptTimestamp()); Output.Append(","); //CurrentHourMeanValue = CurrentHourMeanValue / 100; Output.Append(CurrentHourMeanValue.ToString().Replace(',', '.')); Output.Append("]"); } CurrentHourMeanValue = Double.MinValue; } else { if (CurrentHourMeanValue == Double.MinValue) CurrentHourMeanValue = dataobject.Value; else { CurrentHourMeanValue = (CurrentHourMeanValue + dataobject.Value) / 2; } } } } } } } } } ConsoleOutputLogger_.WriteLineToScreenOnly("Generated JSON Dataset with " + SerializerCounter + " Elements"); } #endregion } if (OutputType == PowerSensorOutputs.CalculateWeeklykWh) { #region Calculated Weekly kWh Counter Output.Append("{ \"label\": \"" + ObjectName + "\", \"data\": ["); bool firstdataset = true; UInt64 SerializerCounter = 0; DateTime CurrentHourStart = StartDateTime; Double CurrentHourMeanValue = Double.MinValue; // TODO: there should be an appropriate caching algorithm in the sensor data... lock (sensor_data.InMemoryIndex) { Double DailyMeanValue = Double.MinValue; Int32 HourNumber = 0; foreach (OnDiscAdress ondisc in sensor_data.InMemoryIndex) { if (ondisc.CreationTime >= StartDateTime.Ticks) { if (ondisc.CreationTime <= EndDateTime.Ticks) { XS1_DataObject dataobject = ReadFromCache(ondisc); SerializerCounter++; if (dataobject.Type == ObjectTypes.Sensor) { if (dataobject.TypeName == "pwr_consump") { if (dataobject.Name == ObjectName) { // only up to a certain amount we consider this a valid value... if (dataobject.Value < 15000) { // calculate the time difference between hour start and current data object TimeSpan ts = new TimeSpan(dataobject.Timecode.Ticks - CurrentHourStart.Ticks); if (ts.TotalMinutes >= 60) { // we have a full hour...add to the calculated value and reset hour values CurrentHourStart = dataobject.Timecode; HourNumber++; if (HourNumber >= 168) { if (!firstdataset) Output.Append(","); else firstdataset = false; // we have 24 hours completed Output.Append("["); Output.Append(dataobject.Timecode.JavaScriptTimestamp()); Output.Append(","); //CurrentHourMeanValue = CurrentHourMeanValue / 100; Output.Append(CurrentHourMeanValue.ToString().Replace(',', '.')); Output.Append("]"); HourNumber = 0; } else { if (DailyMeanValue == Double.MinValue) DailyMeanValue = CurrentHourMeanValue; else DailyMeanValue = (DailyMeanValue + CurrentHourMeanValue) / 2; } CurrentHourMeanValue = Double.MinValue; } else { if (CurrentHourMeanValue == Double.MinValue) CurrentHourMeanValue = dataobject.Value; else { CurrentHourMeanValue = (CurrentHourMeanValue + dataobject.Value) / 2; } } } } } } } } } ConsoleOutputLogger_.WriteLineToScreenOnly("Generated JSON Dataset with " + SerializerCounter + " Elements"); } #endregion } Output.Append("]}"); //ConsoleOutputLogger.WriteLineToScreenOnly("Generated JSON Dataset with " + SerializerCounter + " Elements"); return Output.ToString(); }