当前位置:   article > 正文

程序员的量化交易之路(36)--Lean之数据读取SubscriptionDataReader4

mapfile交易码

转载需注明出处:http://blog.csdn.net/minimicall?viewmode=contentshttp://cloudtrade.top

数据读取需要定义一个读者。直接见下面代码:

  1. namespace QuantConnect.Lean.Engine.DataFeeds
  2. {
  3. /********************************************************
  4. * CLASS DEFINITIONS
  5. *********************************************************/
  6. /// <summary>
  7. /// Subscription data reader is a wrapper on the stream reader class to download, unpack and iterate over a data file.
  8. /// 订阅数据的读者是对数据流读取类的一个封装,负责下载、解压、遍历数据
  9. /// </summary>
  10. /// <remarks>The class accepts any subscription configuration and automatically makes it availble to enumerate</remarks>
  11. public class SubscriptionDataReader : IEnumerator<BaseData>
  12. {
  13. /********************************************************
  14. * CLASS PRIVATE VARIABLES
  15. *********************************************************/
  16. /// Source string to create memory stream:
  17. private string _source = "";
  18. ///Default true to fillforward for this subscription, take the previous result and continue returning it till the next time barrier.
  19. private bool _isFillForward = true;
  20. ///Date of this source file.
  21. private DateTime _date = new DateTime();
  22. ///End of stream from the reader
  23. private bool _endOfStream = false;
  24. /// Internal stream reader for processing data line by line:
  25. private IStreamReader _reader = null;
  26. /// All streams done async via web protocols:
  27. private WebClient _web = new WebClient();
  28. /// Configuration of the data-reader:订阅数据的配置
  29. private SubscriptionDataConfig _config;
  30. /// Subscription Securities Access证券
  31. private Security _security;
  32. /// true if we can find a scale factor file for the security of the form: ..\Lean\Data\equity\factor_files\{SYMBOL}.csv
  33. private bool _hasScaleFactors = false;
  34. // Subscription is for a QC type:
  35. private bool _isDynamicallyLoadedData = false;
  36. //Symbol Mapping:
  37. private string _mappedSymbol = "";
  38. /// Location of the datafeed - the type of this data. 数据类型
  39. private readonly DataFeedEndpoint _feedEndpoint;
  40. /// Object Activator - Fast create new instance of "Type":
  41. private readonly Func<object[], object> _objectActivator;
  42. ///Create a single instance to invoke all Type Methods:
  43. private readonly BaseData _dataFactory;
  44. /// Remember edge conditions as market enters/leaves open-closed.这是什么鬼
  45. private BaseData _lastBarOfStream = null;
  46. private BaseData _lastBarOutsideMarketHours = null;
  47. //Start finish times of the backtest:
  48. private readonly DateTime _periodStart;
  49. private readonly DateTime _periodFinish;
  50. private readonly FactorFile _factorFile;
  51. private readonly MapFile _mapFile;
  52. // we set the price factor ratio when we encounter a dividend in the factor file
  53. // and on the next trading day we use this data to produce the dividend instance
  54. private decimal? _priceFactorRatio;
  55. // we set the split factor when we encounter a split in the factor file
  56. // and on the next trading day we use this data to produce the split instance
  57. private decimal? _splitFactor;
  58. /********************************************************
  59. * CLASS PUBLIC VARIABLES
  60. *********************************************************/
  61. /// <summary>
  62. /// Last read BaseData object from this type and source
  63. /// 最近一个读取出来的数据
  64. /// </summary>
  65. public BaseData Current
  66. {
  67. get;
  68. private set;
  69. }
  70. /// <summary>
  71. /// Explicit Interface Implementation for Current
  72. /// </summary>
  73. object IEnumerator.Current
  74. {
  75. get { return Current; }
  76. }
  77. /// <summary>
  78. /// Provides a means of exposing extra data related to this subscription.
  79. /// For now we expose dividend data for equities through here
  80. /// 额外数据:提供证券订阅数据额外相关的数据的存储,这里指股息
  81. /// </summary>
  82. /// <remarks>
  83. /// It is currently assumed that whomever is pumping data into here is handling the
  84. /// time syncing issues. Dividends do this through the RefreshSource method
  85. /// </remarks>
  86. public Queue<BaseData> AuxiliaryData { get; private set; }
  87. /// <summary>
  88. /// Save an instance of the previous basedata we generated
  89. /// 上一个数据(比Current更前)
  90. /// </summary>
  91. public BaseData Previous
  92. {
  93. get;
  94. private set;
  95. }
  96. /// <summary>
  97. /// Source has been completed, load up next stream or stop asking for data.
  98. /// </summary>
  99. public bool EndOfStream
  100. {
  101. get
  102. {
  103. return _endOfStream || _reader == null;
  104. }
  105. set
  106. {
  107. _endOfStream = value;
  108. }
  109. }
  110. /********************************************************
  111. * CLASS CONSTRUCTOR
  112. *********************************************************/
  113. /// <summary>
  114. /// Subscription data reader takes a subscription request, loads the type, accepts the data source and enumerate on the results.
  115. /// </summary>
  116. /// <param name="config">Subscription configuration object 订阅配置</param>
  117. /// <param name="security">Security asset 证券</param>
  118. /// <param name="feed">Feed type enum 类型</param>
  119. /// <param name="periodStart">Start date for the data request/backtest 开始时间</param>
  120. /// <param name="periodFinish">Finish date for the data request/backtest 结束时间</param>
  121. public SubscriptionDataReader(SubscriptionDataConfig config, Security security, DataFeedEndpoint feed, DateTime periodStart, DateTime periodFinish)
  122. {
  123. Console.WriteLine("SubscriptionDataReader,SubscriptionDataConfig:<symbol:{0},resolution:{1}>" ,config.Symbol,config.Resolution);
  124. //Save configuration of data-subscription:
  125. _config = config;//配置
  126. AuxiliaryData = new Queue<BaseData>();//额外数据队列
  127. //Save access to fill foward flag:是否向前填充
  128. _isFillForward = config.FillDataForward;
  129. //Save Start and End Dates:起止时间
  130. _periodStart = periodStart;
  131. _periodFinish = periodFinish;
  132. //Save access to securities
  133. _security = security;//证券
  134. _isDynamicallyLoadedData = security.IsDynamicallyLoadedData;//是否动态加载数据
  135. // do we have factor tables?
  136. _hasScaleFactors = FactorFile.HasScalingFactors(config.Symbol);//变换因子
  137. //Save the type of data we'll be getting from the source.数据类型
  138. _feedEndpoint = feed;
  139. //Create the dynamic type-activators:提供一种创建给定类型对象的方法
  140. _objectActivator = ObjectActivator.GetActivator(config.Type);
  141. if (_objectActivator == null)
  142. {//创建失败
  143. Engine.ResultHandler.ErrorMessage("Custom data type '" + config.Type.Name + "' missing parameterless constructor E.g. public " + config.Type.Name + "() { }");
  144. _endOfStream = true;
  145. return;
  146. }
  147. //Create an instance of the "Type":
  148. var userObj = _objectActivator.Invoke(new object[] { });
  149. _dataFactory = userObj as BaseData;
  150. //If its quandl set the access token in data factory:
  151. var quandl = _dataFactory as Quandl;
  152. if (quandl != null)
  153. {
  154. if (!Quandl.IsAuthCodeSet)
  155. {
  156. Quandl.SetAuthCode(Config.Get("quandl-auth-token"));
  157. }
  158. }
  159. //Load the entire factor and symbol mapping tables into memory, we'll start with some defaults
  160. Console.WriteLine("load the entire factor and symbol mapping tables into memory");
  161. _factorFile = new FactorFile(config.Symbol, new List<FactorFileRow>());
  162. _mapFile = new MapFile(config.Symbol, new List<MapFileRow>());
  163. try
  164. {
  165. if (_hasScaleFactors)
  166. {
  167. _factorFile = FactorFile.Read(config.Symbol);
  168. _mapFile = MapFile.Read(config.Symbol);
  169. }
  170. }
  171. catch (Exception err)
  172. {
  173. Log.Error("SubscriptionDataReader(): Fetching Price/Map Factors: " + err.Message);
  174. }
  175. }
  176. /********************************************************
  177. * CLASS METHODS
  178. *********************************************************/
  179. /// <summary>
  180. /// Try and create a new instance of the object and return it using the MoveNext enumeration pattern ("Current" public variable).
  181. /// 试图创建一个实例,付给Current
  182. /// </summary>
  183. /// <remarks>This is a highly called method and should be kept lean as possible.</remarks>
  184. /// <returns>Boolean true on successful move next. Set Current public property.</returns>
  185. public bool MoveNext() {
  186. Console.WriteLine("MoveNext()");
  187. // yield the aux data first附加数据优先
  188. if (AuxiliaryData.Count != 0)
  189. {
  190. Previous = Current;//将现在这个付给Previous
  191. Current = AuxiliaryData.Dequeue();//将这个 附属数据弹出付给Current
  192. Console.WriteLine("AuxiliaryData:symbol:"+Current.Symbol+",price:"+Current.Price);
  193. return true;
  194. }
  195. BaseData instance = null;
  196. var instanceMarketOpen = false;
  197. Log.Debug("SubscriptionDataReader.MoveNext(): Starting MoveNext...");
  198. try
  199. {
  200. //Calls this when no file, first "moveNext()" in refresh source.
  201. if (_endOfStream || _reader == null || _reader.EndOfStream)
  202. {
  203. if (_reader == null)
  204. {
  205. //Handle the 1% of time:: getReader failed e.g. missing day so skip day:
  206. Current = null;
  207. }
  208. else
  209. {
  210. //This is a MoveNext() after reading the last line of file:
  211. _lastBarOfStream = Current;
  212. }
  213. _endOfStream = true;
  214. return false;
  215. }
  216. //Log.Debug("SubscriptionDataReader.MoveNext(): Launching While-InstanceNotNull && not EOS: " + reader.EndOfStream);
  217. //Keep looking until output's an instance:
  218. while (instance == null && !_reader.EndOfStream)
  219. {
  220. //Get the next string line from file, create instance of BaseData:读取一行
  221. var line = _reader.ReadLine();//这个_reader里面其实会调用系统的输入流的readLine()
  222. try
  223. {
  224. instance = _dataFactory.Reader(_config, line, _date, _feedEndpoint);//将字符行变成对象
  225. }
  226. catch (Exception err)
  227. {
  228. //Log.Debug("SubscriptionDataReader.MoveNext(): Error invoking instance: " + err.Message);
  229. Engine.ResultHandler.RuntimeError("Error invoking " + _config.Symbol + " data reader. Line: " + line + " Error: " + err.Message, err.StackTrace);
  230. _endOfStream = true;
  231. continue;
  232. }
  233. if (instance != null)
  234. {
  235. // we care if the market was open at any time over the bar判断是否在开市时间段内
  236. instanceMarketOpen = Exchange.IsOpenDuringBar(instance.Time, instance.EndTime, false);
  237. //Apply custom user data filters:
  238. try
  239. {
  240. if (!_security.DataFilter.Filter(_security, instance))
  241. {
  242. instance = null;
  243. continue;
  244. }
  245. }
  246. catch (Exception err)
  247. {
  248. Log.Error("SubscriptionDataReader.MoveNext(): Error applying filter: " + err.Message);
  249. Engine.ResultHandler.RuntimeError("Runtime error applying data filter. Assuming filter pass: " + err.Message, err.StackTrace);
  250. }
  251. if (instance == null)
  252. {
  253. // REVIEW -- Is this condition heuristically possible?
  254. Log.Trace("SubscriptionDataReader.MoveNext(): Instance null, continuing...");
  255. continue;
  256. }
  257. //Check if we're in date range of the data request
  258. if (instance.Time < _periodStart)//取出的数据不在订阅的数据范围内
  259. {
  260. _lastBarOutsideMarketHours = instance;
  261. instance = null;
  262. continue;
  263. }
  264. if (instance.Time > _periodFinish)//取出的数据比订阅的数据晚
  265. {
  266. // we're done with data from this subscription, finalize the reader
  267. Current = null;
  268. _endOfStream = true;
  269. return false;
  270. }
  271. //Save bar for extended market hours (fill forward).
  272. if (!instanceMarketOpen)
  273. {
  274. _lastBarOutsideMarketHours = instance;
  275. }
  276. //However, if we only want market hours data, don't return yet: Discard and continue looping.
  277. if (!_config.ExtendedMarketHours && !instanceMarketOpen)
  278. {
  279. instance = null;
  280. }
  281. }
  282. }
  283. //Handle edge conditions: First Bar Read:
  284. // -> Use previous bar from yesterday if available
  285. if (Current == null)
  286. {
  287. //Handle first loop where not set yet:
  288. if (_lastBarOfStream == null)
  289. {
  290. //For first bar, fill forward from premarket data where possible
  291. _lastBarOfStream = _lastBarOutsideMarketHours ?? instance;
  292. }
  293. //If current not set yet, set Previous to yesterday/last bar read.
  294. Previous = _lastBarOfStream;
  295. }
  296. else
  297. {
  298. Previous = Current;
  299. }
  300. Current = instance;
  301. //End of Stream: rewind reader to last
  302. if (_reader.EndOfStream && instance == null)
  303. {
  304. //Log.Debug("SubscriptionDataReader.MoveNext(): Reader EOS.");
  305. _endOfStream = true;
  306. if (_isFillForward && Previous != null)
  307. {
  308. //If instance == null, current is null, so clone previous to record the final sample:
  309. Current = Previous.Clone(true);
  310. //When market closes fastforward current bar to the last bar fill forwarded to close time.
  311. Current.Time = _security.Exchange.TimeOfDayClosed(Previous.Time);
  312. // Save the previous bar as last bar before next stream (for fill forwrd).
  313. _lastBarOfStream = Previous;
  314. }
  315. return false;
  316. }
  317. return true;
  318. }
  319. catch (Exception err)
  320. {
  321. Log.Error("SubscriptionDataReader.MoveNext(): " + err.Message);
  322. return false;
  323. }
  324. }
  325. /// <summary>
  326. /// For backwards adjusted data the price is adjusted by a scale factor which is a combination of splits and dividends.
  327. /// This backwards adjusted price is used by default and fed as the current price.
  328. /// </summary>
  329. /// <param name="date">Current date of the backtest.</param>
  330. private void UpdateScaleFactors(DateTime date)
  331. {
  332. switch (_config.DataNormalizationMode)
  333. {
  334. case DataNormalizationMode.Raw:
  335. case DataNormalizationMode.TotalReturn:
  336. return;
  337. case DataNormalizationMode.SplitAdjusted:
  338. _config.PriceScaleFactor = _factorFile.GetSplitFactor(date);
  339. break;
  340. case DataNormalizationMode.Adjusted:
  341. _config.PriceScaleFactor = _factorFile.GetPriceScaleFactor(date);
  342. break;
  343. default:
  344. throw new ArgumentOutOfRangeException();
  345. }
  346. }
  347. /// <summary>
  348. /// Check if this time is open for this subscription.
  349. /// </summary>
  350. /// <param name="time">Date and time we're checking to see if the market is open</param>
  351. /// <returns>Boolean true on market open</returns>
  352. public bool IsMarketOpen(DateTime time)
  353. {
  354. return _security.Exchange.DateTimeIsOpen(time);
  355. }
  356. /// <summary>
  357. /// Gets the associated exchange for this data reader/security
  358. /// </summary>
  359. public SecurityExchange Exchange
  360. {
  361. get { return _security.Exchange; }
  362. }
  363. /// <summary>
  364. /// Check if we're still in the extended market hours
  365. /// </summary>
  366. /// <param name="time">Time to scan</param>
  367. /// <returns>True on extended market hours</returns>
  368. public bool IsExtendedMarketOpen(DateTime time)
  369. {
  370. return _security.Exchange.DateTimeIsExtendedOpen(time);
  371. }
  372. /// <summary>
  373. /// Reset the IEnumeration
  374. /// </summary>
  375. /// <remarks>Not used</remarks>
  376. public void Reset()
  377. {
  378. throw new NotImplementedException("Reset method not implemented. Assumes loop will only be used once.");
  379. }
  380. /// <summary>
  381. /// Fetch and set the location of the data from the user's BaseData factory:
  382. /// </summary>
  383. /// <param name="date">Date of the source file.</param>
  384. /// <returns>Boolean true on successfully retrieving the data</returns>
  385. public bool RefreshSource(DateTime date)
  386. {
  387. //Update the source from the getSource method:
  388. _date = date;
  389. Log.Trace("RefreshSource:"+date);
  390. // if the map file is an empty instance this will always return true
  391. if (!_mapFile.HasData(date))
  392. {
  393. // don't even bother checking the disk if the map files state we don't have ze dataz
  394. return false;
  395. }
  396. // check for dividends and split for this security
  397. CheckForDividend(date);
  398. CheckForSplit(date);
  399. var newSource = "";
  400. //If we can find scale factor files on disk, use them. LiveTrading will aways use 1 by definition
  401. if (_hasScaleFactors)
  402. {
  403. // check to see if the symbol was remapped
  404. _mappedSymbol = _mapFile.GetMappedSymbol(date);
  405. _config.MappedSymbol = _mappedSymbol;
  406. // update our price scaling factors in light of the normalization mode
  407. UpdateScaleFactors(date);
  408. }
  409. //Make sure this particular security is trading today:
  410. if (!_security.Exchange.DateIsOpen(date))
  411. {
  412. _endOfStream = true;
  413. return false;
  414. }
  415. //Choose the new source file, hide the QC source file locations
  416. newSource = GetSource(date);
  417. //When stream over stop looping on this data.
  418. if (newSource == "")
  419. {
  420. _endOfStream = true;
  421. return false;
  422. }
  423. Log.Debug("SubscriptionDataReader.MoveNext(): Source Refresh: " + newSource,1);
  424. if (_source != newSource && newSource != "")
  425. {
  426. //If a new file, reset the EOS flag:
  427. _endOfStream = false;
  428. //Set the new source.
  429. _source = newSource;
  430. //Close out the last source file.
  431. Dispose();
  432. //Load the source:
  433. try
  434. {
  435. Log.Trace("SubscriptionDataReader.RefreshSource(): Created new reader for source: " + _source);
  436. _reader = GetReader(_source);
  437. }
  438. catch (Exception err)
  439. {
  440. Log.Error("SubscriptionDataReader.RefreshSource(): Failed to get reader: " + err.Message);
  441. //Engine.ResultHandler.DebugMessage("Failed to get a reader for the data source. There may be an error in your custom data source reader. Skipping date (" + date.ToShortDateString() + "). Err: " + err.Message);
  442. return false;
  443. }
  444. if (_reader == null)
  445. {
  446. Log.Error("Failed to get StreamReader for data source(" + _source + "), symbol(" + _mappedSymbol + "). Skipping date(" + date.ToShortDateString() + "). Reader is null.");
  447. //Engine.ResultHandler.DebugMessage("We could not find the requested data. This may be an invalid data request, failed download of custom data, or a public holiday. Skipping date (" + date.ToShortDateString() + ").");
  448. if (_isDynamicallyLoadedData)
  449. {
  450. Engine.ResultHandler.ErrorMessage("We could not fetch the requested data. This may not be valid data, or a failed download of custom data. Skipping source (" + _source + ").");
  451. }
  452. return false;
  453. }
  454. //Reset the public properties so we can explicitly set them with lastBar data.
  455. Current = null;
  456. Previous = null;
  457. //99% of time, populate the first "Current". 1% of of time no source file (getReader fails), so
  458. // method sets the Subscription properties as if no data.
  459. try
  460. {
  461. MoveNext();
  462. }
  463. catch (Exception err)
  464. {
  465. throw new Exception("SubscriptionDataReader.RefreshSource(): Could not MoveNext to init stream: " + _source + " " + err.Message + " >> " + err.StackTrace);
  466. }
  467. }
  468. //Success:
  469. return true;
  470. }
  471. /// <summary>
  472. /// Check for dividends and emit them into the aux data queue
  473. /// </summary>
  474. private void CheckForSplit(DateTime date)
  475. {
  476. if (_splitFactor != null)
  477. {
  478. var close = GetRawClose();
  479. var split = new Split(_config.Symbol, date, close, _splitFactor.Value);
  480. AuxiliaryData.Enqueue(split);
  481. _splitFactor = null;
  482. }
  483. decimal splitFactor;
  484. if (_factorFile.HasSplitEventOnNextTradingDay(date, out splitFactor))
  485. {
  486. _splitFactor = splitFactor;
  487. }
  488. }
  489. /// <summary>
  490. /// Check for dividends and emit them into the aux data queue
  491. /// 检查股息,并将它写到附加数据队列中
  492. /// </summary>
  493. private void CheckForDividend(DateTime date)
  494. {
  495. if (_priceFactorRatio != null)
  496. {
  497. var close = GetRawClose();
  498. var dividend = new Dividend(_config.Symbol, date, close, _priceFactorRatio.Value);
  499. // let the config know about it for normalization
  500. _config.SumOfDividends += dividend.Distribution;
  501. AuxiliaryData.Enqueue(dividend);
  502. _priceFactorRatio = null;
  503. }
  504. // check the factor file to see if we have a dividend event tomorrow
  505. decimal priceFactorRatio;
  506. if (_factorFile.HasDividendEventOnNextTradingDay(date, out priceFactorRatio))
  507. {
  508. _priceFactorRatio = priceFactorRatio;
  509. }
  510. }
  511. /// <summary>
  512. /// Un-normalizes the Previous.Value标准化什么鬼值
  513. /// </summary>
  514. private decimal GetRawClose()
  515. {
  516. if (Previous == null) return 0m;
  517. var close = Previous.Value;
  518. switch (_config.DataNormalizationMode)
  519. {
  520. case DataNormalizationMode.Raw:
  521. break;
  522. case DataNormalizationMode.SplitAdjusted:
  523. case DataNormalizationMode.Adjusted:
  524. // we need to 'unscale' the price
  525. close = close/_config.PriceScaleFactor;
  526. break;
  527. case DataNormalizationMode.TotalReturn:
  528. // we need to remove the dividends since we've been accumulating them in the price
  529. close -= _config.SumOfDividends;
  530. break;
  531. default:
  532. throw new ArgumentOutOfRangeException();
  533. }
  534. return close;
  535. }
  536. /// <summary>
  537. /// Using this source URL, download it to our cache and open a local reader.
  538. /// </summary>
  539. /// <param name="source">Source URL for the data:</param>
  540. /// <returns>StreamReader for the data source</returns>
  541. private IStreamReader GetReader(string source)
  542. {
  543. IStreamReader reader = null;
  544. if (_feedEndpoint == DataFeedEndpoint.LiveTrading)
  545. {
  546. // live trading currently always gets a rest endpoint,如果是实时交易,则只能用Rest读取
  547. return new RestSubscriptionStreamReader(source);
  548. }
  549. // determine if we're hitting the file system/backtest
  550. if (_feedEndpoint == DataFeedEndpoint.FileSystem || _feedEndpoint == DataFeedEndpoint.Backtesting)
  551. {
  552. // construct a uri to determine if we have a local or remote file
  553. var uri = new Uri(source, UriKind.RelativeOrAbsolute);
  554. if (uri.IsAbsoluteUri && !uri.IsLoopback)
  555. {//绝对地址切uri的IP不是环回地址,即远端
  556. reader = HandleRemoteSourceFile(source);
  557. }
  558. else
  559. {//本地
  560. reader = HandleLocalFileSource(source);
  561. }
  562. }
  563. // if the reader is already at end of stream, just set to null so we don't try to get data for today
  564. if (reader != null && reader.EndOfStream)
  565. {
  566. reader = null;
  567. }
  568. return reader;
  569. }
  570. /// <summary>
  571. /// Dispose of the Stream Reader and close out the source stream and file connections.
  572. /// 废弃该流
  573. /// </summary>
  574. public void Dispose()
  575. {
  576. if (_reader != null)
  577. {
  578. _reader.Close();
  579. _reader.Dispose();
  580. }
  581. if (_web != null)
  582. {
  583. _web.Dispose();
  584. }
  585. }
  586. /// <summary>
  587. /// Get the source URL string for this datetime from the users GetSource() method in BaseData.
  588. /// 获取这个时间对应的文件URL
  589. /// </summary>
  590. /// <param name="date">DateTime we're requesting.</param>
  591. /// <returns>URL string of the source file</returns>
  592. public string GetSource(DateTime date)
  593. {
  594. var newSource = "";
  595. //Invoke our instance of this method.
  596. if (_dataFactory != null)
  597. {
  598. try
  599. {
  600. newSource = _dataFactory.GetSource(_config, date, _feedEndpoint);
  601. }
  602. catch (Exception err)
  603. {
  604. Log.Error("SubscriptionDataReader.GetSource(): " + err.Message);
  605. Engine.ResultHandler.ErrorMessage("Error getting string source location for custom data source: " + err.Message, err.StackTrace);
  606. }
  607. }
  608. //Return the freshly calculated source URL.
  609. return newSource;
  610. }
  611. /// <summary>
  612. /// Opens up an IStreamReader for a local file source
  613. /// 打开一个本地文件数据流
  614. /// </summary>
  615. private IStreamReader HandleLocalFileSource(string source)
  616. {
  617. if (!File.Exists(source))
  618. {
  619. // the local uri doesn't exist, write an error and return null so we we don't try to get data for today
  620. Log.Trace("SubscriptionDataReader.GetReader(): Could not find QC Data, skipped: " + source);
  621. Engine.ResultHandler.SamplePerformance(_date.Date, 0);
  622. return null;
  623. }
  624. // handles zip or text files压缩文件或者文本文件都可以读取
  625. return new LocalFileSubscriptionStreamReader(source);
  626. }
  627. /// <summary>
  628. /// Opens up an IStreamReader for a remote file source
  629. /// 打开远端文件数据源
  630. /// </summary>
  631. private IStreamReader HandleRemoteSourceFile(string source)
  632. {
  633. // clean old files out of the cache
  634. if (!Directory.Exists(Constants.Cache)) Directory.CreateDirectory(Constants.Cache);
  635. foreach (var file in Directory.EnumerateFiles(Constants.Cache))
  636. {
  637. if (File.GetCreationTime(file) < DateTime.Now.AddHours(-24)) File.Delete(file);//一天前的文件都删除掉
  638. }
  639. try
  640. {
  641. // this will fire up a web client in order to download the 'source' file to the cache
  642. return new RemoteFileSubscriptionStreamReader(source, Constants.Cache);//创建一个远端文件读取流(先下载,后调用本地的读取流)
  643. }
  644. catch (Exception err)
  645. {
  646. Engine.ResultHandler.ErrorMessage("Error downloading custom data source file, skipped: " + source + " Err: " + err.Message, err.StackTrace);
  647. Engine.ResultHandler.SamplePerformance(_date.Date, 0);
  648. return null;
  649. }
  650. }
  651. }
  652. }


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/840175
推荐阅读
相关标签
  

闽ICP备14008679号