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 using Newtonsoft.Json.Linq;
23 
24 namespace QuantConnect.Commands
25 {
26  /// <summary>
27  /// Represents a command handler that sources it's commands from a file on the local disk
28  /// </summary>
30  {
31  private readonly Queue<ICommand> _commands = new();
32  private const string _commandFilePattern = "command*.json";
33  private const string _resultFileBaseName = "result-command";
34 
35  /// <summary>
36  /// Initializes a new instance of the <see cref="FileCommandHandler"/> class
37  /// using the 'command-json-file' configuration value for the command json file
38  /// </summary>
40  {
41  }
42 
43  /// <summary>
44  /// Gets all the available command files
45  /// </summary>
46  /// <returns>Sorted enumerator of all the available command files</returns>
47  public static IEnumerable<FileInfo> GetCommandFiles()
48  {
49  var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
50  var filesFromPattern = currentDirectory.GetFiles(_commandFilePattern);
51  return filesFromPattern.OrderBy(file => file.Name);
52  }
53 
54  /// <summary>
55  /// Gets the next command in the queue
56  /// </summary>
57  /// <returns>The next command in the queue, if present, null if no commands present</returns>
58  protected override IEnumerable<ICommand> GetCommands()
59  {
60  foreach(var file in GetCommandFiles())
61  {
62  // update the queue by reading the command file
63  ReadCommandFile(file.FullName);
64 
65  while (_commands.Count != 0)
66  {
67  yield return _commands.Dequeue();
68  }
69  }
70  }
71 
72  /// <summary>
73  /// Acknowledge a command that has been executed
74  /// </summary>
75  /// <param name="command">The command that was executed</param>
76  /// <param name="commandResultPacket">The result</param>
77  protected override void Acknowledge(ICommand command, CommandResultPacket commandResultPacket)
78  {
79  if (string.IsNullOrEmpty(command.Id))
80  {
81  Log.Error($"FileCommandHandler.Acknowledge(): {Messages.FileCommandHandler.NullOrEmptyCommandId}");
82  return;
83  }
84  var resultFilePath = $"{_resultFileBaseName}-{command.Id}.json";
85  File.WriteAllText(resultFilePath, JsonConvert.SerializeObject(commandResultPacket));
86  }
87 
88  /// <summary>
89  /// Reads the commnd file on disk and populates the queue with the commands
90  /// </summary>
91  private void ReadCommandFile(string commandFilePath)
92  {
93  Log.Trace($"FileCommandHandler.ReadCommandFile(): {Messages.FileCommandHandler.ReadingCommandFile(commandFilePath)}");
94  string contents = null;
95  Exception exception = null;
96  object deserialized = null;
97  try
98  {
99  if (!File.Exists(commandFilePath))
100  {
101  Log.Error($"FileCommandHandler.ReadCommandFile(): {Messages.FileCommandHandler.CommandFileDoesNotExist(commandFilePath)}");
102  return;
103  }
104  contents = File.ReadAllText(commandFilePath);
105  deserialized = JsonConvert.DeserializeObject(contents, Settings);
106  }
107  catch (Exception err)
108  {
109  exception = err;
110  }
111 
112  // remove the file when we're done reading it
113  File.Delete(commandFilePath);
114 
115  // try it as an enumerable
116  var enumerable = deserialized as IEnumerable<ICommand>;
117  if (enumerable != null)
118  {
119  foreach (var command in enumerable)
120  {
121  _commands.Enqueue(command);
122  }
123  return;
124  }
125 
126  // try it as a single command
127  var item = deserialized as ICommand;
128  if (item != null)
129  {
130  _commands.Enqueue(item);
131  return;
132  }
133 
134  var callbackCommand = TryGetCallbackCommand(contents);
135  if (callbackCommand != null)
136  {
137  _commands.Enqueue(callbackCommand);
138  return;
139  }
140 
141  if (exception != null)
142  {
143  // if we are here we failed
144  Log.Error(exception);
145  }
146  }
147  }
148 }