/// <summary> /// TimeSeriesLibrary is responsible for ensuring that a certain set of meta-parameters (which /// are saved as database fields) are coordinated with the BLOB of timeseries data. This method /// records all of the meta-parameters of an irregular timeseries into the fields of this /// TSParameters object, using the input parameters given to the method. /// </summary> public void SetParametersIrregular( int timeStepCount, DateTime blobStartDate, DateTime blobEndDate, int compressionCode) { // Most of the parameters are straightforward TimeStepUnit = TSDateCalculator.TimeStepUnitCode.Irregular; TimeStepQuantity = 0; BlobStartDate = blobStartDate; CompressionCode = compressionCode; }
/// <summary> /// TimeSeriesLibrary is responsible for ensuring that a certain set of meta-parameters (which /// are saved as database fields) are coordinated with the BLOB of timeseries data. This method /// records all of the meta-parameters of a regular timeseries into the fields of this TSParameters /// object, using the input parameters given to the method. /// </summary> public void SetParametersRegular( TSDateCalculator.TimeStepUnitCode timeStepUnit, short timeStepQuantity, int timeStepCount, DateTime blobStartDate, int compressionCode) { // Most of the parameters are straightforward TimeStepUnit = timeStepUnit; TimeStepQuantity = timeStepQuantity; BlobStartDate = blobStartDate; CompressionCode = compressionCode; }
/// <summary> /// This private method creates a List of TimeSeriesValue objects from the given BLOB (byte array) /// of time series values. The method takes parameters for a maximum number of values, /// an earliest date, and a latest date, so that only a portion of the BLOB might be /// converted to the List. This method is designed to do the operations that are common between /// the public methods ConvertBlobToListLimited() and ConvertBlobToListAll(). /// </summary> /// <param name="timeStepUnit">TSDateCalculator.TimeStepUnitCode value for Minute,Hour,Day,Week,Month, Year, or Irregular</param> /// <param name="timeStepQuantity">The number of the given unit that defines the time step. /// For instance, if the time step is 6 hours long, then this value is 6. If timeStepUnit is /// Irregular, then this value is ignored.</param> /// <param name="timeStepCount">the number of time steps that are stored in the blob</param> /// <param name="blobStartDate">The DateTime value of the first time step in the BLOB. If /// timeStepUnit is Irregular, then this value is ignored.</param> /// <param name="applyLimits">If value is true, then nReqValues, reqStartDate, and reqEndDate will be /// used to limit the portion of the BLOB that is converted to dateValueList. If the value is false, then /// nReqValues, reqStartDate, and reqEndDate will be ignored.</param> /// <param name="nReqValues">The maximum number of time steps that should be added to dateValueList. /// If applyLimits==false, then this value is ignored.</param> /// <param name="reqStartDate">The earliest date that will be added to dateValueList. /// If applyLimits==false, then this value is ignored.</param> /// <param name="reqEndDate">The latest date that will be added to dateValueList. /// If applyLimits==false, then this value is ignored.</param> /// <param name="blobData">The BLOB (byte array) that this method will convert into a List</param> /// <param name="dateValueList">The List of TimeSeriesValues that this method will create from the BLOB.</param> /// <param name="compressionCode">a generation number that indicates what compression technique to use</param> /// <returns>The number of time steps added to dateValueList</returns> private unsafe int ConvertBlobToList( TSDateCalculator.TimeStepUnitCode timeStepUnit, short timeStepQuantity, int timeStepCount, DateTime blobStartDate, Boolean applyLimits, int nReqValues, DateTime reqStartDate, DateTime reqEndDate, Byte[] blobData, ref List <TimeSeriesValue> dateValueList, int compressionCode) { int nValuesRead = 0; if (timeStepUnit == TSDateCalculator.TimeStepUnitCode.Irregular) { // IRREGULAR TIME SERIES // If we're not limiting the output list (i.e., we're returning every time step from // the BLOB), then set the size of the intermediate array to match the size of the BLOB. if (applyLimits == false) { nReqValues = timeStepCount; } // Allocate an array of date/value pairs that TSBlobCoder method will fill TSDateValueStruct[] dateValueArray = new TSDateValueStruct[nReqValues]; // Method in the TSBlobCoder class does the real work nValuesRead = TSBlobCoder.ConvertBlobToArrayIrregular(timeStepCount, applyLimits, nReqValues, reqStartDate, reqEndDate, blobData, dateValueArray, compressionCode); // resize the array so that the List that we make from it will have exactly the right size if (nValuesRead != nReqValues) { Array.Resize <TSDateValueStruct>(ref dateValueArray, nValuesRead); } // Convert the array of date/value pairs into the List that will be used by the caller dateValueList = dateValueArray .Select(tsv => (TimeSeriesValue)tsv).ToList <TimeSeriesValue>(); } else { // REGULAR TIME SERIES // If we're not limiting the output list (i.e., we're returning every time step from // the BLOB), then set the size of the intermediate array to match the size of the BLOB. if (applyLimits == false) { nReqValues = timeStepCount; } // Allocate an array of values that TSBlobCoder method will fill double[] valueArray = new double[nReqValues]; // Method in the TSBlobCoder class does the real work nValuesRead = TSBlobCoder.ConvertBlobToArrayRegular(timeStepUnit, timeStepQuantity, timeStepCount, blobStartDate, applyLimits, nReqValues, reqStartDate, reqEndDate, blobData, valueArray, compressionCode); // Allocate an array to hold the time series' date values DateTime[] dateArray = new DateTime[nValuesRead]; // Fill the array with the date values corresponding to the time steps defined // for this time series in the database. TSDateCalculator.FillDateArray(timeStepUnit, timeStepQuantity, nValuesRead, dateArray, reqStartDate); // Allocate a List of date/value pairs that will be used by the caller dateValueList = new List <TimeSeriesValue>(nValuesRead); // Loop through all values, building the List of date/value pairs out of the // primitive array of dates and primitive array of values. int i; for (i = 0; i < nValuesRead; i++) { dateValueList.Add(new TimeSeriesValue { Date = dateArray[i], Value = valueArray[i] }); } nValuesRead = i; } return(nValuesRead); }
/// <summary> /// This method converts a BLOB (byte array) to an array of regular time step timeseries /// values (double precision floats). The caller must give parameters of the /// time series, such as time step size and start date. The method will convert /// only a portion of the BLOB if the applyLimits parameter is true, according to /// the parameter values nReqValues, reqStartDate, and reqEndDate. If the /// applyLimits parameter is false, then the method converts the entire BLOB into /// the given array of values. The array of values must have been allocated /// large enough prior to calling this method. /// </summary> /// <param name="timeStepUnit">TSDateCalculator.TimeStepUnitCode value for Minute,Hour,Day,Week,Month, Year, or Irregular</param> /// <param name="timeStepQuantity">The number of the given unit that defines the time step. /// For instance, if the time step is 6 hours long, then this value is 6.</param> /// <param name="timeStepCount">the number of time steps that are stored in the blob</param> /// <param name="blobStartDate">Date of the first time step in the BLOB</param> /// <param name="applyLimits">If true, then the method will convert only a portion of the BLOB, /// according to the parameter values nReqValues, reqStartDate, and reqEndDate. If false, the method /// converts the entire BLOB into a value array.</param> /// <param name="nReqValues">The maximum number of elements that will be converted into the array of values. /// If applyLimits==false, then this value is ignored.</param> /// <param name="reqStartDate">The earliest date in the time series that will be written to the array of values. /// If applyLimits==false, then this value is ignored.</param> /// <param name="reqEndDate">The latest date in the time series that will be written to the array of values. /// If applyLimits==false, then this value is ignored.</param> /// <param name="blobData">the BLOB that will be converted</param> /// <param name="valueArray">the array of time series values that is produced from the BLOB</param> /// <param name="compressionCode">a generation number that indicates what compression technique to use</param> /// <returns>The number of time steps that were actually written to valueArray</returns> public static unsafe int ConvertBlobToArrayRegular( TSDateCalculator.TimeStepUnitCode timeStepUnit, short timeStepQuantity, int timeStepCount, DateTime blobStartDate, bool applyLimits, int nReqValues, DateTime reqStartDate, DateTime reqEndDate, Byte[] blobData, double[] valueArray, int compressionCode) { // The BLOB is kept in a compressed form, so our first step is to decompress it before // anything else can be done. Byte[] decompressedBlobData = DecompressBlob(blobData, timeStepCount * sizeof(double), compressionCode); // MemoryStream and BinaryReader objects enable bulk copying of data from the BLOB using (MemoryStream blobStream = new MemoryStream(decompressedBlobData)) using (BinaryReader blobReader = new BinaryReader(blobStream)) { // How many elements of size 'double' are in the BLOB? int numBlobBin = (int)blobStream.Length; int numBlobValues = numBlobBin / sizeof(double); int numReadValues = numBlobValues; int numSkipValues = 0; int numTruncValues = 0; // Values might be skipped from the front or truncated from the end of the array, // but only if this flag is 'true'. if (applyLimits) { // Do we skip any values at the front of the BLOB in order to fullfil the requested start date? if (reqStartDate > blobStartDate) { numSkipValues = TSDateCalculator.CountSteps(blobStartDate, reqStartDate, timeStepUnit, timeStepQuantity); } // compute the last date in the BLOB DateTime blobEndDate = TSDateCalculator.IncrementDate (blobStartDate, timeStepUnit, timeStepQuantity, numBlobValues - 1); // Do we truncate any values at the end of the BLOB in order to fulfill the requested end date? if (reqEndDate < blobEndDate) { numTruncValues = TSDateCalculator.CountSteps(reqEndDate, blobEndDate, timeStepUnit, timeStepQuantity); } // the number of values that can actually be read from the BLOB numReadValues = Math.Min(numBlobValues - numSkipValues - numTruncValues, nReqValues); } // the number of bytes that will actually be read int numReadBin = numReadValues * sizeof(double); // the number of bytes that will be skipped int numSkipBin = numSkipValues * sizeof(double); // If we've got zero values to read, then we're done early! if (numReadValues <= 0) { return(0); } // Transfer the entire array of data as a block Buffer.BlockCopy(blobReader.ReadBytes(numBlobBin), numSkipBin, valueArray, 0, numReadBin); return(numReadValues); } }
/// <summary> /// This method copies the parameters of a time series from a TSParameters object /// into this TSImport object. /// </summary> /// <param name="tsp">The TSParameters object that values will be copied from</param> public void RecordFromTSParameters(TSParameters tsp) { TimeStepUnit = tsp.TimeStepUnit; TimeStepQuantity = tsp.TimeStepQuantity; BlobStartDate = tsp.BlobStartDate; CompressionCode = tsp.CompressionCode; }
// Note--this test class was created primarily b/c we wanted to individually test the different // compression methods. Hence, we have created the test methods below. The class TSBlobCoder // certainly deserves full test coverage, but that can be developed later. In the meantime, // it is generally expected that TSBlobCoder has adequate test coverage via TSLibraryTest. #region Test Methods for ConvertBlobToListAll() and ConvertListToBlob() // The series of tests below is for ConvertBlobToListAll and ConvertListToBlob. // The tests take advantage of the fact that the methods are designed so that // the series that is put into the BLOB must be identical to the series that // comes out of the BLOB. // This method is re-used by the actual test methods that follow. public void ConvertBlobAll(List <TimeSeriesValue> inList, TSDateCalculator.TimeStepUnitCode timeStepUnit, short timeStepQuantity, DateTime blobStartDate, int compressionCode) { TSLibrary tsLib = new TSLibrary(); var traceObject = new TSTrace { TraceNumber = 1, TimeStepCount = inList.Count, EndDate = inList.Last().Date }; if (timeStepUnit == TSDateCalculator.TimeStepUnitCode.Irregular) { var inArray = inList.Select(v => (TSDateValueStruct)v).ToArray(); var outArray = new TSDateValueStruct[inList.Count]; byte[] blobData = TSBlobCoder.ConvertArrayToBlobIrregular(inArray, compressionCode, traceObject); int ret = TSBlobCoder.ConvertBlobToArrayIrregular(inList.Count, false, 0, blobStartDate, blobStartDate, blobData, outArray, compressionCode); // The return value of the function must match the number of items in the original list Assert.AreEqual(ret, inList.Count); // the count in both lists must match Assert.AreEqual(inArray.Length, outArray.Length); // now check each item in the two lists Boolean AreEqual = true; for (int i = 0; i < ret; i++) { if (outArray[i].Date != inArray[i].Date || outArray[i].Value != inArray[i].Value) { AreEqual = false; } } Assert.IsTrue(AreEqual); } else { var inArray = inList.Select(v => v.Value).ToArray(); var outArray = new Double[inList.Count]; byte[] blobData = TSBlobCoder.ConvertArrayToBlobRegular(inArray, compressionCode, traceObject); int ret = TSBlobCoder.ConvertBlobToArrayRegular(timeStepUnit, timeStepQuantity, inList.Count, blobStartDate, false, 0, blobStartDate, blobStartDate, blobData, outArray, compressionCode); // The return value of the function must match the number of items in the original list Assert.AreEqual(ret, inList.Count); // the count in both lists must match Assert.AreEqual(inArray.Length, outArray.Length); // now check each item in the two lists Boolean AreEqual = true; for (int i = 0; i < ret; i++) { if (outArray[i] != inArray[i]) { AreEqual = false; } } Assert.IsTrue(AreEqual); } }