Crear nueva tarea

Hydra está construida con la posibilidad de crear extensiones personalizadas. Para añadir su propia extensión, debe copiar la dll a la subcarpeta Plugins donde está instalada Hydra. A continuación se muestra el proceso de creación de una utilidad para hacer copias de seguridad de datos existentes (se incluye de serie y se presenta como material de aprendizaje).

Cada utilidad debe implementar la interfaz IHydraTask (o heredar de las clases BaseHydraTask o ConnectorHydraTask`1):

	using System;
	using System.Collections.Generic;
	using System.ComponentModel.DataAnnotations;
	using System.IO;
	using System.Linq;
	using System.Security;
	using Amazon;
	using Ecng.Backup;
	using Ecng.Backup.Amazon;
	using Ecng.Backup.Yandex;
	using Ecng.Common;
	using Ecng.ComponentModel;
	using Ecng.Serialization;
	using StockSharp.Algo;
	using StockSharp.Algo.Storages;
	using StockSharp.Hydra.Core;
	using StockSharp.Localization;
	using Ecng.Logging;
	using StockSharp.Messages;
	using DataType = StockSharp.Algo.DataType;
	enum BackupServices
	{
		AwsS3,
		AwsGlacier,
		Yandex,
	}
	[DisplayNameLoc(LocalizedStrings.BackupKey)]
	[DescriptionLoc(LocalizedStrings.BackupDescriptionKey)]
	[Doc("5a056352-64c7-41ea-87a8-2e112935e3b9.htm")]
	[Icon("backup_logo.png")]
	[MessageAdapterCategory(MessageAdapterCategories.Tool)]
	class BackupTask : BaseHydraTask
	{
		private const string _sourceName = LocalizedStrings.BackupKey;
		public BackupTask()
		{
			Offset = 1;
			StartFrom = new DateTime(2000, 1, 1);
			Interval = TimeSpan.FromDays(1);
			Service = BackupServices.AwsS3;
			Address = RegionEndpoint.USEast1.SystemName;
			ServiceRepo = "stocksharp";
			Login = string.Empty;
			Password = new SecureString();
		}
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.Str3427Key,
			Description = LocalizedStrings.Str3427Key + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 0)]
		public BackupServices Service { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.AddressKey,
			Description = LocalizedStrings.ServerAddressKey + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 1)]
		public string Address { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.Str1405Key,
			Description = LocalizedStrings.Str1405Key + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 2)]
		public string ServiceRepo { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.LoginKey,
			Description = LocalizedStrings.LoginKey + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 3)]
		public string Login { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.PasswordKey,
			Description = LocalizedStrings.PasswordKey + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 4)]
		public SecureString Password { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.Str2282Key,
			Description = LocalizedStrings.Str3779Key,
			GroupName = _sourceName,
			Order = 5)]
		public DateTime StartFrom { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.Str2284Key,
			Description = LocalizedStrings.Str3778Key,
			GroupName = _sourceName,
			Order = 6)]
		public int Offset { get; set; }
		public override void Save(SettingsStorage storage)
		{
			base.Save(storage);
			storage.SetValue(nameof(Service), Service);
			storage.SetValue(nameof(Address), Address);
			storage.SetValue(nameof(ServiceRepo), ServiceRepo);
			storage.SetValue(nameof(Login), Login);
			storage.SetValue(nameof(Password), Password);
			storage.SetValue(nameof(StartFrom), StartFrom);
			storage.SetValue(nameof(Offset), Offset);
		}
		public override void Load(SettingsStorage storage)
		{
			base.Load(storage);
			Service = storage.GetValue(nameof(Service), Service);
			Address = storage.GetValue(nameof(Address), Address);
			ServiceRepo = storage.GetValue(nameof(ServiceRepo), ServiceRepo);
			Login = storage.GetValue(nameof(Login), Login);
			Password = storage.GetValue(nameof(Password), Password);
			StartFrom = storage.GetValue(nameof(StartFrom), StartFrom);
			Offset = storage.GetValue(nameof(Offset), Offset);
		}
		public override IEnumerable<DataType> SupportedDataTypes => Enumerable.Empty<DataType>();

Un paso importante al crear la utilidad es implementar el método BaseHydraTask.OnProcess, que contiene la lógica de la utilidad. En el caso de implementar copia de seguridad de datos, toda la lógica está dentro de la API:

	[	
		protected override TimeSpan OnProcess()
		{
			using (var service = CreateService())
			{
				var hasSecurities = false;
				this.AddInfoLog(LocalizedStrings.Str2306Params.Put(StartFrom));
				var startDate = StartFrom;
				var endDate = DateTime.Today - TimeSpan.FromDays(Offset);
				var allDates = startDate.Range(endDate, TimeSpan.FromDays(1)).ToArray();
				var pathEntry = ToEntry(new DirectoryInfo(Drive.Path));
				var workingSecurities = GetWorkingSecurities().ToArray();
				foreach (var date in allDates)
				{
					foreach (var security in workingSecurities)
					{
						hasSecurities = true;
						if (!CanProcess())
							break;
						var dateEntry = new BackupEntry
						{
							Name = LocalMarketDataDrive.GetDirName(date),
							Parent = new BackupEntry
							{
								Parent = new BackupEntry
								{
									Name = security.Security.Id.Substring(0, 1),
									Parent = pathEntry
								},
								Name = security.Security.Id,
							}
						};
						var dataTypes = Drive.GetAvailableDataTypes(security.Security.ToSecurityId(), StorageFormat);
						foreach (var dataType in dataTypes)
						{
							var storage = StorageRegistry.GetStorage(security.Security, dataType.MessageType, dataType.Arg, Drive, StorageFormat);
							var drive = storage.Drive;
							var stream = drive.LoadStream(date);
							if (stream == Stream.Null)
								continue;
							var entry = new BackupEntry
							{
								Name = LocalMarketDataDrive.GetFileName(dataType.MessageType, dataType.Arg, StorageFormat), // + LocalMarketDataDrive.GetExtension(StorageFormats.Binary),
								Parent = dateEntry,
							};
							var token = service.Upload(entry, stream, p => { });
							if (token.IsCancellationRequested)
							{
								this.AddWarningLog(LocalizedStrings.Cancelling);
								return TimeSpan.MaxValue;
							}
							this.AddInfoLog(LocalizedStrings.Str1580Params, GetPath(entry));
						}
					}
					if (CanProcess())
					{
						StartFrom += TimeSpan.FromDays(1);
						this.SaveSettings();
					}
				}
				if (!hasSecurities)
				{
					this.AddWarningLog(LocalizedStrings.Str2292);
					return TimeSpan.MaxValue;
				}
				if (CanProcess())
					this.AddInfoLog(LocalizedStrings.Str2300);
				return base.OnProcess();
			}
		}
		private static string GetPath(BackupEntry entry)
		{
			if (entry == null)
				return null;
			return GetPath(entry.Parent) + "/" + entry.Name;
		}
		private static BackupEntry ToEntry(DirectoryInfo di)
		{
			// is a disk
			if (di.Parent == null)
				return null;
			return new BackupEntry
			{
				Name = di.Name,
				Parent = di.Parent != null ? ToEntry(di.Parent) : null
			};
		}
		public override bool CanTestConnect => true;
		public override void TestConnect(Action<Exception> connectionChanged)
		{
			if (connectionChanged == null)
				throw new ArgumentNullException(nameof(connectionChanged));
			try
			{
				using (var service = CreateService())
					service.Find(null, string.Empty);
				connectionChanged(null);
			}
			catch (Exception ex)
			{
				connectionChanged(ex);
			}
		}
		private IBackupService CreateService()
		{
			switch (Service)
			{
				case BackupServices.AwsS3:
					return new AmazonS3Service(Address, ServiceRepo, Login, Password.To<string>());
				case BackupServices.AwsGlacier:
					return new AmazonGlacierService(Address, ServiceRepo, Login, Password.To<string>());
				case BackupServices.Yandex:
					return new YandexDiskService();
				default:
					throw new ArgumentOutOfRangeException();
			}
		}
	}