Lean  $LEAN_TAG$
FileCommandHandler.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.IO;
18 using Newtonsoft.Json;
19 using QuantConnect.Logging;
20 using System.Collections.Generic;
21 using System.Linq;
22 
23 namespace QuantConnect.Commands
24 {
25  /// <summary>
26  /// Represents a command handler that sources it's commands from a file on the local disk
27  /// </summary>
29  {
30  private readonly Queue<ICommand> _commands = new();
31  private const string _commandFilePattern = "command*.json";
32  private const string _resultFileBaseName = "result-command";
33 
34  /// <summary>
35  /// Initializes a new instance of the <see cref="FileCommandHandler"/> class
36  /// using the 'command-json-file' configuration value for the command json file
37  /// </summary>
39  {
40  }
41 
42  /// <summary>
43  /// Gets all the available command files
44  /// </summary>
45  /// <returns>Sorted enumerator of all the available command files</returns>
46  public static IEnumerable<FileInfo> GetCommandFiles()
47  {
48  var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
49  var filesFromPattern = currentDirectory.GetFiles(_commandFilePattern);
50  return filesFromPattern.OrderBy(file => file.Name);
51  }
52 
53  /// <summary>
54  /// Gets the next command in the queue
55  /// </summary>
56  /// <returns>The next command in the queue, if present, null if no commands present</returns>
57  protected override IEnumerable<ICommand> GetCommands()
58  {
59  foreach(var file in GetCommandFiles())
60  {
61  // update the queue by reading the command file
62  ReadCommandFile(file.FullName);
63 
64  while (_commands.Count != 0)
65  {
66  yield return _commands.Dequeue();
67  }
68  }
69  }
70 
71  /// <summary>
72  /// Acknowledge a command that has been executed
73  /// </summary>
74  /// <param name="command">The command that was executed</param>
75  /// <param name="commandResultPacket">The result</param>
76  protected override void Acknowledge(ICommand command, CommandResultPacket commandResultPacket)
77  {
78  if (string.IsNullOrEmpty(command.Id))
79  {
80  Log.Error($"FileCommandHandler.Acknowledge(): {Messages.FileCommandHandler.NullOrEmptyCommandId}");
81  return;
82  }
83  var resultFilePath = $"{_resultFileBaseName}-{command.Id}.json";
84  File.WriteAllText(resultFilePath, JsonConvert.SerializeObject(commandResultPacket));
85  }
86 
87  /// <summary>
88  /// Reads the commnd file on disk and populates the queue with the commands
89  /// </summary>
90  private void ReadCommandFile(string commandFilePath)
91  {
92  Log.Trace($"FileCommandHandler.ReadCommandFile(): {Messages.FileCommandHandler.ReadingCommandFile(commandFilePath)}");
93  object deserialized;
94  try
95  {
96  if (!File.Exists(commandFilePath))
97  {
98  Log.Error($"FileCommandHandler.ReadCommandFile(): {Messages.FileCommandHandler.CommandFileDoesNotExist(commandFilePath)}");
99  return;
100  }
101  var contents = File.ReadAllText(commandFilePath);
102  deserialized = JsonConvert.DeserializeObject(contents, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
103  }
104  catch (Exception err)
105  {
106  Log.Error(err);
107  deserialized = null;
108  }
109 
110  // remove the file when we're done reading it
111  File.Delete(commandFilePath);
112 
113  // try it as an enumerable
114  var enumerable = deserialized as IEnumerable<ICommand>;
115  if (enumerable != null)
116  {
117  foreach (var command in enumerable)
118  {
119  _commands.Enqueue(command);
120  }
121  return;
122  }
123 
124  // try it as a single command
125  var item = deserialized as ICommand;
126  if (item != null)
127  {
128  _commands.Enqueue(item);
129  }
130  }
131  }
132 }