using System; using System.Collections.Generic; using System.Linq; using System.Text; using Deltares.Standard; using System.Reflection; using System.Xml.Linq; using System.Xml.Schema; using System.IO; using System.Xml; using System.Globalization; using Deltares.Standard.IO.DtoAssembler; namespace Deltares.Dam.Data.Assemblers { using Standard.Extensions; public class TimeSeriesAssembler : DtoAssembler { #region Constant declarations /// /// Holds the xml element name /// private const string XmlElementName = "TimeSeries"; /// /// Holds the name of the xml schema /// private const string XmlSchemaName = "pi_timeseries"; private const string XmlSchemaNameSharedTypes = "pi_sharedtypes"; /// /// Holds the xml namespace /// private const string XmlElementNamespace = "http://www.wldelft.nl/fews/PI"; /// /// Holds the common part of the path to the embedded resource /// private const string EmbeddedResourcePath = "Deltares.Dam.Data.Xsd"; /// /// Holds the xsd resource path /// private const string XsdEmbeddedResourcePath = EmbeddedResourcePath + "." + XmlSchemaName + ".xsd"; private const string XsdEmbeddedResourcePathSharedTypes = EmbeddedResourcePath + "." + XmlSchemaNameSharedTypes + ".xsd"; public const string XmlVersionAttributeName = "version"; public const string XmlTimeZoneElementName = "timeZone"; public const string XmlSeriesElementName = "series"; public const string XmlHeaderElementName = "header"; public const string XmlTypeElementName = "type"; public const string XmlLocationIdElementName = "locationId"; public const string XmlParameterIdElementName = "parameterId"; public const string XmlTimeStepElementName = "timeStep"; public const string XmlStartDateElementName = "startDate"; public const string XmlEndDateElementName = "endDate"; public const string XmlForecastDateElementName = "forecastDate"; public const string XmlMissValElementName = "missVal"; public const string XmlLongNameElementName = "longName"; public const string XmlStationNameElementName = "stationName"; public const string XmlUnitsElementName = "units"; public const string XmlSourceOrganisationElementName = "sourceOrganisation"; public const string XmlSourceSystemElementName = "sourceSystem"; public const string XmlFileDescriptionElementName = "fileDescription"; public const string XmlCreationDateElementName = "creationDate"; public const string XmlCreationTimeElementName = "creationTime"; public const string XmlRegionElementName = "region"; public const string XmlUnitAttributeName = "unit"; public const string XmlDividerAttributeName = "divider"; public const string XmlMultiplierAttributeName = "multiplier"; public const string XmlDateAttributeName = "date"; public const string XmlTimeAttributeName = "time"; public const string XmlEntryElementName = "event"; public const string XmlValueAttributeName = "value"; public const string XmlFlagAttributeName = "flag"; public const string XMLCommentElementName = "comment"; #endregion public TimeSeriesAssembler() : base(XmlElementName, XmlElementNamespace, Assembly.GetExecutingAssembly().GetEmbeddedFile(XsdEmbeddedResourcePath)) { AddOrUpdateMapping("Version", XmlVersionAttributeName); } public override TimeSerieCollection CreateDomainObject(XElement dtoObj) { TimeSerieCollection timeSerieCollection = base.CreateDomainObject(dtoObj); XNamespace tns = this.ElementNamespace; // Time zone XElement timeZoneElement = dtoObj.Element(tns + TimeSeriesAssembler.XmlTimeZoneElementName); if (timeZoneElement != null) { timeSerieCollection.TimeZone = Double.Parse(timeZoneElement.Value, CultureInfo.InvariantCulture); } // Series IEnumerable serieElementCollection = dtoObj.Elements(tns + TimeSeriesAssembler.XmlSeriesElementName); foreach (XElement serieElement in serieElementCollection) { var timeSerie = new TimeSerie(); // Header XElement headerElement = serieElement.Element(tns + TimeSeriesAssembler.XmlHeaderElementName); timeSerie.Type = GetElementValue(headerElement, tns + XmlTypeElementName); timeSerie.LocationId = GetElementValue(headerElement, tns + XmlLocationIdElementName); timeSerie.ParameterId = GetElementValue(headerElement, tns + XmlParameterIdElementName); XElement timeStepElement = headerElement.Element(tns + TimeSeriesAssembler.XmlTimeStepElementName); timeSerie.TimeStep.Unit = (TimeStepUnit)Enum.Parse(typeof(TimeStepUnit), timeStepElement.AttributeAs(TimeSeriesAssembler.XmlUnitAttributeName), true); timeSerie.TimeStep.Divider = timeStepElement.AttributeAs(TimeSeriesAssembler.XmlDividerAttributeName); timeSerie.TimeStep.Multiplier = timeStepElement.AttributeAs(TimeSeriesAssembler.XmlMultiplierAttributeName); XElement startDateElement = headerElement.Element(tns + TimeSeriesAssembler.XmlStartDateElementName); timeSerie.StartDateTime = startDateElement.AttributeAs(TimeSeriesAssembler.XmlDateAttributeName).Date + startDateElement.AttributeAs(TimeSeriesAssembler.XmlTimeAttributeName).TimeOfDay; XElement endDateElement = headerElement.Element(tns + TimeSeriesAssembler.XmlEndDateElementName); timeSerie.EndDateTime = endDateElement.AttributeAs(TimeSeriesAssembler.XmlDateAttributeName).Date + endDateElement.AttributeAs(TimeSeriesAssembler.XmlTimeAttributeName).TimeOfDay; XElement forecastDateElement = headerElement.Element(tns + TimeSeriesAssembler.XmlForecastDateElementName); if (forecastDateElement != null) { timeSerie.ForecastDateTime = forecastDateElement.AttributeAs(TimeSeriesAssembler.XmlDateAttributeName).Date + forecastDateElement.AttributeAs(TimeSeriesAssembler.XmlTimeAttributeName).TimeOfDay; } timeSerie.MissVal = GetElementValue(headerElement, tns + XmlMissValElementName); timeSerie.LongName = GetElementValue(headerElement, tns + XmlLongNameElementName); timeSerie.StationName = GetElementValue(headerElement, tns + XmlStationNameElementName); timeSerie.Units = GetElementValue(headerElement, tns + XmlUnitsElementName); timeSerie.SourceOrganisation = GetElementValue(headerElement, tns + XmlSourceOrganisationElementName); timeSerie.SourceSystem = GetElementValue(headerElement, tns + XmlSourceSystemElementName); timeSerie.FileDescription = GetElementValue(headerElement, tns + XmlFileDescriptionElementName); XElement creationDateElement = headerElement.Element(tns + XmlCreationDateElementName); XElement creationTimeElement = headerElement.Element(tns + XmlCreationTimeElementName); if (creationDateElement != null) { timeSerie.CreationDateTime = creationDateElement.Value.ToType().Date; if (creationTimeElement != null) { timeSerie.CreationDateTime += creationTimeElement.Value.ToType().TimeOfDay; } } timeSerie.Region = GetElementValue(headerElement, tns + XmlRegionElementName); // Entries IEnumerable entryElementCollection = serieElement.Elements(tns + TimeSeriesAssembler.XmlEntryElementName); foreach (XElement entryElement in entryElementCollection) { TimeSerieEntry entry = new TimeSerieEntry(); entry.DateTime = entryElement.AttributeAs(XmlDateAttributeName).Date + entryElement.AttributeAs(XmlTimeAttributeName).TimeOfDay; entry.Value = entryElement.AttributeAs(XmlValueAttributeName); entry.Flag = entryElement.AttributeAs(XmlFlagAttributeName); timeSerie.Entries.Add(entry); } // Comment XElement commentElement = serieElement.Element(tns + XMLCommentElementName); if (commentElement != null) timeSerie.Comment = commentElement.Value; timeSerieCollection.Series.Add(timeSerie); } return timeSerieCollection; } private static T GetElementValue(XContainer parent, XName name) { var element = parent.Element(name); if (element != null) return element.Value.ToType(); return default(T); } public override XElement CreateDataTransferObject(TimeSerieCollection timeSerieCollection) { XElement rootElement = base.CreateDataTransferObject(timeSerieCollection); XNamespace tns = ElementNamespace; // Time zone rootElement.Add(new XElement(tns + XmlTimeZoneElementName, timeSerieCollection.TimeZone)); // Series foreach (TimeSerie timeSerie in timeSerieCollection.Series) { XElement serieElement = new XElement(tns + XmlSeriesElementName); XElement headerElement = new XElement(tns + XmlHeaderElementName); headerElement.Add(new XElement(tns + XmlTypeElementName, timeSerie.Type)); headerElement.Add(new XElement(tns + XmlLocationIdElementName, timeSerie.LocationId)); headerElement.Add(new XElement(tns + XmlParameterIdElementName, timeSerie.ParameterId)); headerElement.Add(new XElement(tns + XmlTimeStepElementName, new XAttribute(XmlUnitAttributeName, timeSerie.TimeStep.Unit.ToString().ToLower()), (timeSerie.TimeStep.MultiplierSpecified ? new XAttribute(XmlMultiplierAttributeName, timeSerie.TimeStep.Multiplier) : null), (timeSerie.TimeStep.DividerSpecified ? new XAttribute(XmlDividerAttributeName, timeSerie.TimeStep.Divider) : null) )); headerElement.Add(new XElement(tns + XmlStartDateElementName, new XAttribute(XmlDateAttributeName, timeSerie.StartDateTime.ToString("yyyy-MM-dd")), new XAttribute(XmlTimeAttributeName, timeSerie.StartDateTime.ToString("HH:mm:ss")))); headerElement.Add(new XElement(tns + XmlEndDateElementName, new XAttribute(XmlDateAttributeName, timeSerie.EndDateTime.ToString("yyyy-MM-dd")), new XAttribute(XmlTimeAttributeName, timeSerie.EndDateTime.ToString("HH:mm:ss")))); if (!timeSerie.ForecastDateTime.Equals(DateTime.MinValue)) headerElement.Add(new XElement(tns + XmlForecastDateElementName, new XAttribute(XmlDateAttributeName, timeSerie.ForecastDateTime.ToString("yyyy-MM-dd")), new XAttribute(XmlTimeAttributeName, timeSerie.ForecastDateTime.ToString("HH:mm:ss")))); headerElement.Add(new XElement(tns + XmlMissValElementName, timeSerie.MissVal)); if (timeSerie.LongName != null) headerElement.Add(new XElement(tns + XmlLongNameElementName, timeSerie.LongName)); if (timeSerie.StationName != null) headerElement.Add(new XElement(tns + XmlStationNameElementName, timeSerie.StationName)); if (timeSerie.Units != null) headerElement.Add(new XElement(tns + XmlUnitsElementName, timeSerie.Units)); if (timeSerie.SourceOrganisation != null) headerElement.Add(new XElement(tns + XmlSourceOrganisationElementName, timeSerie.SourceOrganisation)); if (timeSerie.SourceSystem != null) headerElement.Add(new XElement(tns + XmlSourceSystemElementName, timeSerie.SourceSystem)); if (timeSerie.FileDescription != null) headerElement.Add(new XElement(tns + XmlFileDescriptionElementName, timeSerie.FileDescription)); if (timeSerie.CreationDateTime != null) { headerElement.Add(new XElement(tns + XmlCreationDateElementName, timeSerie.CreationDateTime.Value.ToString("yyyy-MM-dd"))); headerElement.Add(new XElement(tns + XmlCreationTimeElementName, timeSerie.CreationDateTime.Value.ToString("HH:mm:ss"))); } if (timeSerie.Region != null) headerElement.Add(new XElement(tns + XmlRegionElementName, timeSerie.Region)); serieElement.Add(headerElement); // Entries foreach (var entry in timeSerie.Entries) { XElement entryElement = new XElement(tns + XmlEntryElementName, new XAttribute(XmlDateAttributeName, entry.DateTime.ToString("yyyy-MM-dd")), new XAttribute(XmlTimeAttributeName, entry.DateTime.ToString("HH:mm:ss")), new XAttribute(XmlValueAttributeName, entry.Value), new XAttribute(XmlFlagAttributeName, entry.Flag)); serieElement.Add(entryElement); } // Comment if (timeSerie.Comment != null) { XElement commentElement = new XElement(tns + XMLCommentElementName, timeSerie.Comment); serieElement.Add(commentElement); } rootElement.Add(serieElement); } return rootElement; } public XDocument CreateDataTransferDocument(TimeSerieCollection timeSerieCollection) { XDocument doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance"; XElement root = CreateDataTransferObject(timeSerieCollection); doc.Add(root); root.Add(new XAttribute(XNamespace.Xmlns + "xsi", xsi.NamespaceName)); return doc; } public bool ValidateSchema(XDocument doc, out string message) { bool result = true; string errorMessage = String.Empty; XmlSchemaSet schemas = new XmlSchemaSet(); Stream xsdStream = Assembly.GetExecutingAssembly().GetEmbeddedFile(XsdEmbeddedResourcePath); schemas.Add(XmlElementNamespace, XmlReader.Create(xsdStream)); xsdStream = Assembly.GetExecutingAssembly().GetEmbeddedFile(XsdEmbeddedResourcePathSharedTypes); schemas.Add(XmlElementNamespace, XmlReader.Create(xsdStream)); doc.Validate(schemas, (o, e) => { result = false; errorMessage = e.Message; }, true); message = errorMessage; return result; } } }