mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-07-27 18:47:55 +00:00
Added new projects of new RPC core: MyClient and MtServivce
This commit is contained in:
Executable
+9
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Executable
+180
@@ -0,0 +1,180 @@
|
||||
namespace MtClient
|
||||
{
|
||||
public enum MessageType
|
||||
{
|
||||
Command = 0,
|
||||
Response = 1,
|
||||
Event = 2,
|
||||
ExpertList = 3,
|
||||
ExpertAdded = 4,
|
||||
ExpertRemoved = 5,
|
||||
}
|
||||
|
||||
public abstract class MtMessage
|
||||
{
|
||||
public abstract MessageType MsgType { get; }
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
return $"{(int)MsgType};{GetMessageBody()}";
|
||||
}
|
||||
|
||||
protected abstract string GetMessageBody();
|
||||
}
|
||||
|
||||
public class MtCommand(int expertHandle, int commandType, int commandId, string payload) : MtMessage
|
||||
{
|
||||
public override MessageType MsgType => MessageType.Command;
|
||||
|
||||
public int ExpertHandle { private set; get; } = expertHandle;
|
||||
public int CommandType { private set; get; } = commandType;
|
||||
public int CommandId { private set; get; } = commandId;
|
||||
public string Payload { private set; get; } = payload;
|
||||
|
||||
protected override string GetMessageBody()
|
||||
{
|
||||
return $"{ExpertHandle};{CommandId};{CommandType};{Payload}";
|
||||
}
|
||||
}
|
||||
|
||||
public class MtEvent(int expertHandle, int eventType, string payload) : MtMessage
|
||||
{
|
||||
public override MessageType MsgType => MessageType.Event;
|
||||
|
||||
public int ExpertHandle { private set; get; } = expertHandle;
|
||||
public int EventType { private set; get; } = eventType;
|
||||
public string Payload { private set; get; } = payload;
|
||||
|
||||
protected override string GetMessageBody()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static MtMessage? Parse(string payload)
|
||||
{
|
||||
var pieces = payload.Split(";", 3);
|
||||
if (pieces.Length == 3
|
||||
&& int.TryParse(pieces[0], out int expertHandle)
|
||||
&& int.TryParse(pieces[1], out int eventType))
|
||||
return new MtEvent(expertHandle, eventType, payload);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class MtExpertAddedMsg(int expertHandle) : MtMessage
|
||||
{
|
||||
public override MessageType MsgType => MessageType.ExpertAdded;
|
||||
|
||||
public int ExpertHandle { private set; get; } = expertHandle;
|
||||
|
||||
protected override string GetMessageBody()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static MtMessage? Parse(string payload)
|
||||
{
|
||||
if (int.TryParse(payload, out int expertHandle) == false)
|
||||
return null;
|
||||
|
||||
return new MtExpertAddedMsg(expertHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public class MtExpertRemovedMsg(int expertHandle) : MtMessage
|
||||
{
|
||||
public override MessageType MsgType => MessageType.ExpertRemoved;
|
||||
|
||||
public int ExpertHandle { private set; get; } = expertHandle;
|
||||
|
||||
protected override string GetMessageBody()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static MtMessage? Parse(string payload)
|
||||
{
|
||||
if (int.TryParse(payload, out int expertHandle) == false)
|
||||
return null;
|
||||
|
||||
return new MtExpertRemovedMsg(expertHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public class MtExpertListMsg(List<int> experts) : MtMessage
|
||||
{
|
||||
public override MessageType MsgType => MessageType.ExpertList;
|
||||
|
||||
public List<int> Experts { private set; get; } = experts;
|
||||
|
||||
protected override string GetMessageBody()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static MtMessage? Parse(string payload)
|
||||
{
|
||||
var pieces = payload.Split(",");
|
||||
List<int> handles = [];
|
||||
foreach (var p in pieces)
|
||||
{
|
||||
if (int.TryParse(p, out int expertHandle) == false)
|
||||
return null;
|
||||
handles.Add(expertHandle);
|
||||
}
|
||||
|
||||
return new MtExpertListMsg(handles);
|
||||
}
|
||||
}
|
||||
|
||||
public class MtResponse(int expertHandle, int commandId, string payload) : MtMessage
|
||||
{
|
||||
public override MessageType MsgType => MessageType.Response;
|
||||
|
||||
public int ExpertHandle { private set; get; } = expertHandle;
|
||||
public int CommandId { private set; get; } = commandId;
|
||||
public string Payload { private set; get; } = payload;
|
||||
|
||||
protected override string GetMessageBody()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static MtMessage? Parse(string payload)
|
||||
{
|
||||
var pieces = payload.Split(";", 3);
|
||||
if (pieces.Length != 3
|
||||
|| string.IsNullOrEmpty(pieces[0])
|
||||
|| string.IsNullOrEmpty(pieces[1])
|
||||
|| string.IsNullOrEmpty(pieces[2]))
|
||||
return null;
|
||||
|
||||
if (int.TryParse(pieces[0], out int expertHandle) == false)
|
||||
return null;
|
||||
|
||||
if (int.TryParse(pieces[1], out int commandId) == false)
|
||||
return null;
|
||||
|
||||
return new MtResponse(expertHandle, commandId, pieces[2]);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MtMessageParser
|
||||
{
|
||||
static MtMessageParser()
|
||||
{
|
||||
msgHandlers_[MessageType.Event] = MtEvent.Parse;
|
||||
msgHandlers_[MessageType.Response] = MtResponse.Parse;
|
||||
msgHandlers_[MessageType.ExpertList] = MtExpertListMsg.Parse;
|
||||
msgHandlers_[MessageType.ExpertAdded] = MtExpertAddedMsg.Parse;
|
||||
msgHandlers_[MessageType.ExpertRemoved] = MtExpertRemovedMsg.Parse;
|
||||
}
|
||||
|
||||
public static MtMessage? Parse(MessageType msgType, string payload)
|
||||
{
|
||||
return msgHandlers_[msgType](payload);
|
||||
}
|
||||
|
||||
private static readonly Dictionary<MessageType, Func<string, MtMessage?>> msgHandlers_ = new();
|
||||
}
|
||||
}
|
||||
Executable
+184
@@ -0,0 +1,184 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
|
||||
namespace MtClient
|
||||
{
|
||||
public class MtRpcClient
|
||||
{
|
||||
public MtRpcClient(string host, int port)
|
||||
{
|
||||
host_ = host;
|
||||
port_ = port;
|
||||
|
||||
receiveThread_ = new Thread(new ThreadStart(DoReceive));
|
||||
sendThread_ = new Thread(new ThreadStart(DoWrite));
|
||||
}
|
||||
|
||||
public async Task Connect()
|
||||
{
|
||||
Log($"Connect: started to {host_}:{port_}");
|
||||
|
||||
try
|
||||
{
|
||||
await ws_.ConnectAsync(new Uri($"ws://{host_}:{port_}/ws"), CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"Connect failed: {ex.Message}");
|
||||
throw new Exception($"Failed connection to {host_}:{port_}");
|
||||
}
|
||||
|
||||
receiveThread_.Start();
|
||||
sendThread_.Start();
|
||||
|
||||
Log($"Connect: success.");
|
||||
}
|
||||
|
||||
public async void Disconnect()
|
||||
{
|
||||
Log($"Disconnect: {host_}:{port_}");
|
||||
|
||||
try
|
||||
{
|
||||
await ws_.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
||||
ws_.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"Disconnect: {ex.Message}");
|
||||
}
|
||||
|
||||
sendWaiter_.Set();
|
||||
sendThread_.Join();
|
||||
receiveThread_.Join();
|
||||
|
||||
Log($"Disconnect: success");
|
||||
}
|
||||
|
||||
public void Send(MtMessage message)
|
||||
{
|
||||
pendingMessages_.Enqueue(message.Serialize());
|
||||
sendWaiter_.Set();
|
||||
}
|
||||
|
||||
private async void DoWrite()
|
||||
{
|
||||
while(ws_.State == WebSocketState.Open)
|
||||
{
|
||||
string? message = null;
|
||||
lock(pendingMessages_)
|
||||
{
|
||||
if (pendingMessages_.Count > 0)
|
||||
message = pendingMessages_.Dequeue();
|
||||
}
|
||||
|
||||
if (message == null)
|
||||
{
|
||||
sendWaiter_.WaitOne();
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Log($"DoWrite: sending message: {message}");
|
||||
byte[] bytes = Encoding.ASCII.GetBytes(message);
|
||||
await ws_.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log($"DoWrite: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void DoReceive()
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] recvBuffer = new byte[64 * 1024];
|
||||
while (ws_.State == WebSocketState.Open)
|
||||
{
|
||||
var result = await ws_.ReceiveAsync(new ArraySegment<byte>(recvBuffer), CancellationToken.None);
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
Log($"DoReceive: close signal {result.CloseStatusDescription}");
|
||||
await ws_.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = Encoding.ASCII.GetString(recvBuffer, 0, result.Count);
|
||||
OnReceive(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"Exception in receive - {ex.Message}");
|
||||
ConnectionFailed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReceive(string msg)
|
||||
{
|
||||
Log($"OnReceive: {msg}");
|
||||
|
||||
if (string.IsNullOrEmpty(msg))
|
||||
{
|
||||
Log("OnReceive: Invalid message (null or empty)");
|
||||
return;
|
||||
}
|
||||
|
||||
var pieces = msg.Split(";", 2);
|
||||
|
||||
if (pieces.Length != 2
|
||||
|| string.IsNullOrEmpty(pieces[0])
|
||||
|| string.IsNullOrEmpty(pieces[1]))
|
||||
{
|
||||
Log("OnReceive: Invalid message format.");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageType msgType;
|
||||
try
|
||||
{
|
||||
var msgTypeValue = int.Parse(pieces[0]);
|
||||
msgType = (MessageType)Enum.ToObject(typeof(MessageType), msgTypeValue);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log($"OnReceive: Parse MessageType failed. {e.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
var message = MtMessageParser.Parse(msgType, (pieces[1]));
|
||||
if (message == null)
|
||||
{
|
||||
Log("OnReceive: Failed parse message payload");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageReceived?.Invoke(this, message);
|
||||
}
|
||||
|
||||
private void Log(string msg)
|
||||
{
|
||||
Console.WriteLine($"[{Environment.CurrentManagedThreadId}] {msg}");
|
||||
}
|
||||
|
||||
public event EventHandler<MtMessage>? MessageReceived;
|
||||
public event EventHandler<EventArgs>? ConnectionFailed;
|
||||
|
||||
private readonly ClientWebSocket ws_ = new();
|
||||
private readonly string host_;
|
||||
private readonly int port_;
|
||||
private readonly byte[] buf_ = new byte[10000];
|
||||
private readonly Dictionary<MessageType, Func<string, MtMessage?>> msgHandlers_ = new();
|
||||
private readonly Queue<string> pendingMessages_ = [];
|
||||
|
||||
private readonly Thread receiveThread_;
|
||||
private readonly Thread sendThread_;
|
||||
private readonly EventWaitHandle sendWaiter_ = new AutoResetEvent(false);
|
||||
}
|
||||
}
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "MtMessage.h"
|
||||
|
||||
typedef std::function<void(const MtResponse&)> TaskCallback;
|
||||
|
||||
class CommandTask
|
||||
{
|
||||
public:
|
||||
CommandTask(std::unique_ptr<MtCommand> command, TaskCallback callback)
|
||||
: command_(std::move(command))
|
||||
, callback_(std::move(callback))
|
||||
{}
|
||||
|
||||
MtCommand& getCommand() const { return *command_; }
|
||||
|
||||
void ProcessResponse(int handle, const std::string& payload)
|
||||
{
|
||||
MtResponse respone(handle, command_->getCommandId(), payload);
|
||||
callback_(respone);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MtCommand> command_;
|
||||
TaskCallback callback_;
|
||||
};
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#include "pch.h"
|
||||
#include "LogConfigurator.h"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/utility/setup.hpp>
|
||||
#include <windows.h>
|
||||
|
||||
static const std::string DEFAULT_FILE_EXTENSION = ".log";
|
||||
|
||||
using namespace boost::log;
|
||||
|
||||
namespace
|
||||
{
|
||||
trivial::severity_level ToSeverityLevel(LogLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case LogLevel::Fatal:
|
||||
return trivial::fatal;
|
||||
case LogLevel::Error:
|
||||
return trivial::error;
|
||||
case LogLevel::Warning:
|
||||
return trivial::warning;
|
||||
case LogLevel::Info:
|
||||
return trivial::info;
|
||||
case LogLevel::Debug:
|
||||
return trivial::debug;
|
||||
case LogLevel::Trace:
|
||||
return trivial::trace;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
std::string GetTempDirectoryPathImpl()
|
||||
{
|
||||
std::string tmp_prefix;
|
||||
char char_path[MAX_PATH];
|
||||
if (auto s = GetTempPathA(MAX_PATH, char_path))
|
||||
{
|
||||
tmp_prefix = std::string(char_path, std::size_t(s - 1));
|
||||
}
|
||||
std::replace(tmp_prefix.begin(), tmp_prefix.end(), '\\', '/');
|
||||
return tmp_prefix;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void LogConfigurator::Setup(LogLevel level, OutputType output_type,
|
||||
const std::string& profile_name)
|
||||
{
|
||||
if (profile_name.empty())
|
||||
throw std::runtime_error("Invalid profile name");
|
||||
|
||||
static const std::string COMMON_FMT("[%TimeStamp%] [%ThreadID%] [%Severity%] [%ClassName%]: %Message%");
|
||||
|
||||
register_simple_formatter_factory<trivial::severity_level, char>("Severity");
|
||||
|
||||
if ((output_type & OutputType::Console) == OutputType::Console)
|
||||
{
|
||||
// Output message to console
|
||||
add_console_log(
|
||||
std::cout,
|
||||
keywords::format = COMMON_FMT,
|
||||
keywords::auto_flush = true);
|
||||
}
|
||||
|
||||
if ((output_type & OutputType::File) == OutputType::File)
|
||||
{
|
||||
std::string file_name = profile_name;
|
||||
file_name += "_%Y%m%d_%2N";
|
||||
file_name += DEFAULT_FILE_EXTENSION;
|
||||
|
||||
std::string file_path = GetTempDirectoryPathImpl() + "\\" + profile_name + "\\" + file_name;
|
||||
|
||||
// Output message to file, rotates when file reached 1mb or at midnight every day. Each log file
|
||||
// is capped at 1mb and total is 20mb
|
||||
add_file_log(
|
||||
keywords::file_name = file_path,
|
||||
keywords::rotation_size = 1 * 1024 * 1024,
|
||||
keywords::max_size = 20 * 1024 * 1024,
|
||||
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
|
||||
keywords::format = COMMON_FMT,
|
||||
keywords::auto_flush = true);
|
||||
}
|
||||
|
||||
add_common_attributes();
|
||||
|
||||
core::get()->set_filter(trivial::severity >= ToSeverityLevel(level));
|
||||
}
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "LogLevel.h"
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
|
||||
enum class OutputType : int
|
||||
{
|
||||
Console = 0x1,
|
||||
File = 0x2
|
||||
};
|
||||
|
||||
inline OutputType operator|(OutputType lhs, OutputType rhs)
|
||||
{
|
||||
using T = std::underlying_type_t<OutputType>;
|
||||
return static_cast<OutputType>(static_cast<T>(lhs) | static_cast<T>(rhs));
|
||||
}
|
||||
|
||||
inline OutputType& operator|=(OutputType& lhs, OutputType rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline OutputType operator&(OutputType lhs, OutputType rhs)
|
||||
{
|
||||
using T = std::underlying_type_t<OutputType>;
|
||||
return static_cast<OutputType>(static_cast<T>(lhs) & static_cast<T>(rhs));
|
||||
}
|
||||
|
||||
|
||||
class LogConfigurator
|
||||
{
|
||||
public:
|
||||
static void Setup(LogLevel level, OutputType output_type,
|
||||
const std::string& profile_name);
|
||||
};
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
enum class LogLevel
|
||||
{
|
||||
Fatal,
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
Debug,
|
||||
Trace
|
||||
};
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#include "pch.h"
|
||||
#include "Logger.h"
|
||||
#include "LogLevel.h"
|
||||
#include "boost/log/common.hpp"
|
||||
#include "boost/log/trivial.hpp"
|
||||
#include "boost/log/sources/severity_logger.hpp"
|
||||
#include <stdarg.h>
|
||||
|
||||
using namespace boost::log;
|
||||
|
||||
class Logger::LoggerImpl
|
||||
{
|
||||
public:
|
||||
LoggerImpl(const char* class_name)
|
||||
{
|
||||
boost_logger_.add_attribute("ClassName", attributes::constant<std::string>(class_name));
|
||||
}
|
||||
|
||||
void LogSev(trivial::severity_level level, const char* msg)
|
||||
{
|
||||
BOOST_LOG_SEV(boost_logger_, level) << msg;
|
||||
}
|
||||
|
||||
void LogSev(trivial::severity_level level, const char* fmt, va_list va)
|
||||
{
|
||||
char buf[8192];
|
||||
vsnprintf(buf, sizeof(buf), fmt, va);
|
||||
LogSev(level, buf);
|
||||
}
|
||||
|
||||
private:
|
||||
sources::severity_logger<trivial::severity_level> boost_logger_;
|
||||
};
|
||||
|
||||
Logger::Logger(const char* class_name)
|
||||
: logger_impl_(new Logger::LoggerImpl(class_name))
|
||||
{
|
||||
}
|
||||
|
||||
Logger::~Logger()
|
||||
{
|
||||
}
|
||||
|
||||
void Logger::Fatal(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
logger_impl_->LogSev(trivial::fatal, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void Logger::Error(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
logger_impl_->LogSev(trivial::error, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void Logger::Warning(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
logger_impl_->LogSev(trivial::warning, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void Logger::Info(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
logger_impl_->LogSev(trivial::info, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void Logger::Debug(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
logger_impl_->LogSev(trivial::debug, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void Logger::Trace(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
logger_impl_->LogSev(trivial::trace, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
Logger(const char* class_name);
|
||||
~Logger();
|
||||
|
||||
void Fatal(const char* fmt, ...);
|
||||
void Error(const char* fmt, ...);
|
||||
void Warning(const char* fmt, ...);
|
||||
void Info(const char* fmt, ...);
|
||||
void Debug(const char* fmt, ...);
|
||||
void Trace(const char* fmt, ...);
|
||||
|
||||
private:
|
||||
class LoggerImpl;
|
||||
std::unique_ptr<LoggerImpl> logger_impl_;
|
||||
};
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
class MetaTraderHandler
|
||||
{
|
||||
public:
|
||||
virtual ~MetaTraderHandler() = default;
|
||||
virtual void SendTickToMetaTrader() = 0;
|
||||
};
|
||||
Executable
+164
@@ -0,0 +1,164 @@
|
||||
#include "pch.h"
|
||||
#include "MtConnection.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
MtConnection::MtConnection(WebsocketStream&& ws)
|
||||
: ws_(std::move(ws))
|
||||
, resolver_(ws_.get_executor())
|
||||
{
|
||||
log_.Debug("%s: connection %p created.", __FUNCTION__, this);
|
||||
}
|
||||
|
||||
MtConnection::~MtConnection()
|
||||
{
|
||||
log_.Debug("%s: connection %p destroyed.", __FUNCTION__, this);
|
||||
}
|
||||
|
||||
void MtConnection::Accept()
|
||||
{
|
||||
// Set suggested timeout settings for the websocket
|
||||
ws_.set_option(
|
||||
boost::beast::websocket::stream_base::timeout::suggested(
|
||||
boost::beast::role_type::server));
|
||||
|
||||
// Set a decorator to change the Server of the handshake
|
||||
ws_.set_option(boost::beast::websocket::stream_base::decorator(
|
||||
[](boost::beast::websocket::response_type& res)
|
||||
{
|
||||
res.set(boost::beast::http::field::server,
|
||||
std::string(BOOST_BEAST_VERSION_STRING) +
|
||||
" websocket-server-async");
|
||||
}));
|
||||
// Accept the websocket handshake
|
||||
ws_.async_accept(
|
||||
std::bind(
|
||||
&MtConnection::OnAccept,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1));
|
||||
}
|
||||
|
||||
void MtConnection::Close()
|
||||
{
|
||||
log_.Debug("%s: entry.", __FUNCTION__);
|
||||
|
||||
ws_.async_close(boost::beast::websocket::close_code::normal,
|
||||
[](boost::beast::error_code const ec){ });
|
||||
}
|
||||
|
||||
void MtConnection::Send(const std::string& msg)
|
||||
{
|
||||
log_.Trace("%s: msg = %s", __FUNCTION__, msg.c_str());
|
||||
|
||||
send_queue_.push(msg);
|
||||
if (send_queue_.size() == 1)
|
||||
DoWrite();
|
||||
}
|
||||
|
||||
void MtConnection::OnAccept(boost::beast::error_code ec)
|
||||
{
|
||||
log_.Trace("%s: entry.", __FUNCTION__);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s: %s", __FUNCTION__, ec.what().c_str());
|
||||
OnConnectionFailed(ec.message());
|
||||
}
|
||||
else
|
||||
DoRead();
|
||||
|
||||
OnConnected();
|
||||
}
|
||||
|
||||
void MtConnection::DoRead()
|
||||
{
|
||||
log_.Trace("%s: enter.", __FUNCTION__);
|
||||
|
||||
// Clear the buffer
|
||||
read_text_.clear();
|
||||
|
||||
// Read a message into our buffer
|
||||
ws_.async_read(
|
||||
read_buffer_,
|
||||
std::bind(
|
||||
&MtConnection::OnRead,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
}
|
||||
|
||||
void MtConnection::OnRead(
|
||||
boost::beast::error_code ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
boost::ignore_unused(bytes_transferred);
|
||||
|
||||
// This indicates that the session was closed
|
||||
if (ec == boost::beast::websocket::error::closed)
|
||||
{
|
||||
log_.Info("%s: session was closed.", __FUNCTION__);
|
||||
OnDisconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec == boost::asio::error::operation_aborted)
|
||||
{
|
||||
log_.Info("%s: session was aborted.", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s: %s", __FUNCTION__, ec.message().c_str());
|
||||
OnConnectionFailed(ec.message());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto msg = boost::beast::buffers_to_string(read_buffer_.data());
|
||||
log_.Trace("%s: %s", __FUNCTION__, msg.c_str());
|
||||
|
||||
OnMessageReceived(msg);
|
||||
|
||||
// Clear the buffer
|
||||
read_buffer_.consume(read_buffer_.size());
|
||||
|
||||
boost::asio::dispatch(ws_.get_executor(),
|
||||
std::bind(
|
||||
&MtConnection::DoRead,
|
||||
shared_from_this()));
|
||||
}
|
||||
}
|
||||
|
||||
void MtConnection::DoWrite()
|
||||
{
|
||||
if (send_queue_.empty())
|
||||
return;
|
||||
|
||||
log_.Trace("%s: msg = %s", __FUNCTION__, send_queue_.front().c_str());
|
||||
|
||||
ws_.text(ws_.got_text());
|
||||
ws_.async_write(
|
||||
boost::asio::buffer(send_queue_.front()),
|
||||
std::bind(
|
||||
&MtConnection::OnWrite,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
}
|
||||
|
||||
void MtConnection::OnWrite(
|
||||
boost::beast::error_code ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
boost::ignore_unused(bytes_transferred);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s, %s", __FUNCTION__, ec.what().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
send_queue_.pop();
|
||||
DoWrite();
|
||||
}
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/beast.hpp>
|
||||
#include <boost/signals2.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "MtMessage.h"
|
||||
|
||||
typedef boost::beast::websocket::stream<boost::asio::ip::tcp::socket> WebsocketStream;
|
||||
|
||||
class MtConnection : public std::enable_shared_from_this<MtConnection>
|
||||
{
|
||||
public:
|
||||
MtConnection(WebsocketStream&& ws);
|
||||
~MtConnection();
|
||||
|
||||
void Accept();
|
||||
void Close();
|
||||
void Send(const std::string& msg);
|
||||
|
||||
boost::signals2::signal<void()> OnConnected;
|
||||
boost::signals2::signal<void(const std::string&)> OnConnectionFailed;
|
||||
boost::signals2::signal<void()> OnDisconnected;
|
||||
boost::signals2::signal<void(const std::string&)> OnMessageReceived;
|
||||
|
||||
private:
|
||||
void OnAccept(boost::beast::error_code ec);
|
||||
void DoRead();
|
||||
void OnRead(
|
||||
boost::beast::error_code ec,
|
||||
std::size_t bytes_transferred);
|
||||
void DoWrite();
|
||||
void OnWrite(
|
||||
boost::beast::error_code ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
Logger log_{ "MtConnection" };
|
||||
WebsocketStream ws_;
|
||||
boost::asio::ip::tcp::resolver resolver_;
|
||||
std::string host_;
|
||||
std::string read_text_;
|
||||
boost::beast::flat_buffer read_buffer_;
|
||||
//std::string send_text_;
|
||||
std::queue<std::string> send_queue_;
|
||||
};
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "MtExpert.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int MT_COMMAND_TYPE_EMPTY = 0;
|
||||
}
|
||||
|
||||
MtExpert::MtExpert(int handle, std::unique_ptr<MetaTraderHandler> mt_handler)
|
||||
: handle_(handle)
|
||||
, mt_handler_(std::move(mt_handler))
|
||||
{
|
||||
log_.Debug("%s: expert %d created.", __FUNCTION__, handle_);
|
||||
}
|
||||
|
||||
MtExpert::~MtExpert()
|
||||
{
|
||||
log_.Debug("%s: expert %d destroyed.", __FUNCTION__, handle_);
|
||||
}
|
||||
|
||||
void MtExpert::Deinit()
|
||||
{
|
||||
log_.Debug("%s: handle = %d", __FUNCTION__, handle_);
|
||||
|
||||
OnDeinit(handle_);
|
||||
}
|
||||
|
||||
void MtExpert::SendEvent(int event_type, const std::string& payload)
|
||||
{
|
||||
log_.Trace("%s: handle = %d, event_type = %d, payload = %s", __FUNCTION__, handle_, event_type, payload.c_str());
|
||||
|
||||
OnEvent(MtEvent(handle_, event_type, payload));
|
||||
}
|
||||
|
||||
void MtExpert::SendResponse(const std::string& payload)
|
||||
{
|
||||
log_.Trace("%s: handle = %d, payload = %s", __FUNCTION__, handle_, payload.c_str());
|
||||
|
||||
if (current_task_)
|
||||
{
|
||||
current_task_->ProcessResponse(handle_, payload);
|
||||
current_task_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void MtExpert::Process(std::unique_ptr<MtCommand> command, TaskCallback callback)
|
||||
{
|
||||
log_.Debug("%s: handle = %d, command type = %d, command id = %d", __FUNCTION__, handle_,
|
||||
command->getCommandType(), command->getCommandId());
|
||||
|
||||
auto task = std::make_unique<CommandTask>(std::move(command), std::move(callback));
|
||||
tasks_.push(std::move(task));
|
||||
|
||||
mt_handler_->SendTickToMetaTrader();
|
||||
}
|
||||
|
||||
int MtExpert::GetCommandType()
|
||||
{
|
||||
if (tasks_.size() == 0)
|
||||
return MT_COMMAND_TYPE_EMPTY;
|
||||
|
||||
current_task_ = std::move(tasks_.front());
|
||||
tasks_.pop();
|
||||
|
||||
return current_task_->getCommand().getCommandType();
|
||||
}
|
||||
|
||||
std::string MtExpert::GetCommandPayload()
|
||||
{
|
||||
return current_task_ ? current_task_->getCommand().getPayload() : "";
|
||||
}
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "CommandTask.h"
|
||||
#include "Logger.h"
|
||||
#include "MetaTraderHandler.h"
|
||||
#include "MtMessage.h"
|
||||
|
||||
class MtExpert
|
||||
{
|
||||
public:
|
||||
MtExpert(int handle, std::unique_ptr<MetaTraderHandler> mt_handler);
|
||||
~MtExpert();
|
||||
|
||||
void Deinit();
|
||||
|
||||
void SendEvent(int event_type, const std::string& payload);
|
||||
void SendResponse(const std::string& payload);
|
||||
|
||||
void Process(std::unique_ptr<MtCommand> command, TaskCallback callback);
|
||||
|
||||
int GetCommandType();
|
||||
std::string GetCommandPayload();
|
||||
|
||||
boost::signals2::signal<void(const MtEvent& event)> OnEvent;
|
||||
boost::signals2::signal<void(int)> OnDeinit;
|
||||
|
||||
private:
|
||||
Logger log_{ "MtExpert" };
|
||||
int handle_;
|
||||
std::unique_ptr<MetaTraderHandler> mt_handler_;
|
||||
std::queue<std::unique_ptr<CommandTask>> tasks_;
|
||||
std::unique_ptr<CommandTask> current_task_;
|
||||
};
|
||||
Executable
+204
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
const std::string MT_MESSAGE_DELIMETER{ ";" };
|
||||
|
||||
enum MessageType
|
||||
{
|
||||
COMMAND = 0,
|
||||
RESPONSE = 1,
|
||||
EVENT = 2,
|
||||
EXPERT_LIST = 3,
|
||||
EXPERT_ADDED = 4,
|
||||
EXPERT_REMOVED = 5
|
||||
};
|
||||
|
||||
class MtMessage
|
||||
{
|
||||
public:
|
||||
std::string Serialize() const
|
||||
{
|
||||
return std::to_string(GetType()) + MT_MESSAGE_DELIMETER + GetBody();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual MessageType GetType() const = 0;
|
||||
virtual std::string GetBody() const = 0;
|
||||
};
|
||||
|
||||
class MtResponse : public MtMessage
|
||||
{
|
||||
public:
|
||||
MtResponse(int expert_handle, int id, const std::string& payload)
|
||||
: expert_handle_(expert_handle)
|
||||
, id_(id)
|
||||
, payload_(payload)
|
||||
{
|
||||
}
|
||||
|
||||
int getExpertHandle() const { return expert_handle_; }
|
||||
|
||||
int getCommandId() const { return id_; }
|
||||
|
||||
std::string getPayload() const { return payload_; }
|
||||
|
||||
private:
|
||||
MessageType GetType() const override
|
||||
{
|
||||
return MessageType::RESPONSE;
|
||||
}
|
||||
|
||||
std::string GetBody() const override
|
||||
{
|
||||
return std::to_string(expert_handle_)
|
||||
+ MT_MESSAGE_DELIMETER + std::to_string(id_)
|
||||
+ MT_MESSAGE_DELIMETER + payload_;
|
||||
}
|
||||
|
||||
int expert_handle_;
|
||||
int id_;
|
||||
std::string payload_;
|
||||
};
|
||||
|
||||
class MtCommand : public MtMessage
|
||||
{
|
||||
public:
|
||||
MtCommand(int expert_handle, int command_id, int command_type, const std::string& payload)
|
||||
: expert_handle_(expert_handle)
|
||||
, command_id_(command_id)
|
||||
, command_type_(command_type)
|
||||
, payload_(payload)
|
||||
{
|
||||
}
|
||||
|
||||
int getExpertHandle() const { return expert_handle_; }
|
||||
|
||||
int getCommandId() const { return command_id_; }
|
||||
|
||||
int getCommandType() const { return command_type_; }
|
||||
|
||||
std::string getPayload() const { return payload_; }
|
||||
|
||||
private:
|
||||
MessageType GetType() const override
|
||||
{
|
||||
return MessageType::COMMAND;
|
||||
}
|
||||
|
||||
std::string GetBody() const override
|
||||
{
|
||||
return std::to_string(expert_handle_)
|
||||
+ MT_MESSAGE_DELIMETER + std::to_string(command_id_)
|
||||
+ MT_MESSAGE_DELIMETER + std::to_string(command_type_)
|
||||
+ MT_MESSAGE_DELIMETER + payload_;
|
||||
}
|
||||
|
||||
int expert_handle_;
|
||||
int command_id_;
|
||||
int command_type_;
|
||||
std::string payload_;
|
||||
};
|
||||
|
||||
class MtEvent : public MtMessage
|
||||
{
|
||||
public:
|
||||
MtEvent(int expert_handle, int event_type, const std::string& payload)
|
||||
: expert_handle_(expert_handle)
|
||||
, event_type_(event_type)
|
||||
, payload_(payload)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
MessageType GetType() const override
|
||||
{
|
||||
return MessageType::EVENT;
|
||||
}
|
||||
|
||||
std::string GetBody() const override
|
||||
{
|
||||
return std::to_string(expert_handle_)
|
||||
+ MT_MESSAGE_DELIMETER + std::to_string(event_type_)
|
||||
+ MT_MESSAGE_DELIMETER + payload_;
|
||||
}
|
||||
|
||||
int expert_handle_;
|
||||
int event_type_;
|
||||
std::string payload_;
|
||||
};
|
||||
|
||||
class MtExpertListMsg : public MtMessage
|
||||
{
|
||||
public:
|
||||
MtExpertListMsg(std::vector<int> experts)
|
||||
: experts_(std::move(experts))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
MessageType GetType() const override
|
||||
{
|
||||
return MessageType::EXPERT_LIST;
|
||||
}
|
||||
|
||||
std::string GetBody() const override
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < experts_.size(); ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
ss << ",";
|
||||
ss << experts_[i];
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int expert_handle_;
|
||||
std::vector<int> experts_;
|
||||
};
|
||||
|
||||
class MtExpertAddedMsg : public MtMessage
|
||||
{
|
||||
public:
|
||||
MtExpertAddedMsg(int handle)
|
||||
: handle_(handle)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
MessageType GetType() const override
|
||||
{
|
||||
return MessageType::EXPERT_ADDED;
|
||||
}
|
||||
|
||||
std::string GetBody() const override
|
||||
{
|
||||
return std::to_string(handle_);
|
||||
}
|
||||
|
||||
int handle_;
|
||||
};
|
||||
|
||||
class MtExpertRemovedMsg : public MtMessage
|
||||
{
|
||||
public:
|
||||
MtExpertRemovedMsg(int handle)
|
||||
: handle_(handle)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
MessageType GetType() const override
|
||||
{
|
||||
return MessageType::EXPERT_REMOVED;
|
||||
}
|
||||
|
||||
std::string GetBody() const override
|
||||
{
|
||||
return std::to_string(handle_);
|
||||
}
|
||||
|
||||
int handle_;
|
||||
};
|
||||
Executable
+295
@@ -0,0 +1,295 @@
|
||||
#include "pch.h"
|
||||
#include "MtServer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/phoenix/core.hpp>
|
||||
#include <boost/phoenix/operator.hpp>
|
||||
|
||||
#include "MtConnection.h"
|
||||
#include "MtExpert.h"
|
||||
|
||||
const std::string ADDRESS = "0.0.0.0";
|
||||
constexpr int STOP_EXPERT_INTERVAL = 1; // 1 sec
|
||||
|
||||
MtServer::MtServer(
|
||||
boost::asio::io_context& context,
|
||||
unsigned short port)
|
||||
: context_(context)
|
||||
, port_(port)
|
||||
, acceptor_(context)
|
||||
, stop_timer_(context)
|
||||
{
|
||||
boost::beast::error_code ec;
|
||||
|
||||
boost::asio::ip::tcp::endpoint endpoint{boost::asio::ip::make_address(ADDRESS), port};
|
||||
|
||||
// Open the acceptor
|
||||
acceptor_.open(endpoint.protocol(), ec);
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s: open failed! %s", __FUNCTION__, ec.what().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow address reuse
|
||||
acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec);
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s: set_option failed! %s", __FUNCTION__, ec.what().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind to the server address
|
||||
acceptor_.bind(endpoint, ec);
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s: bind failed! %s", __FUNCTION__, ec.what().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Start listening for connections
|
||||
acceptor_.listen(
|
||||
boost::asio::socket_base::max_listen_connections, ec);
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s: listen failed! %s", __FUNCTION__, ec.what().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MtServer::~MtServer()
|
||||
{
|
||||
log_.Debug("%s: destructor called.", __FUNCTION__);
|
||||
}
|
||||
|
||||
void MtServer::AddExpert(int handle, MtExpert* expert)
|
||||
{
|
||||
log_.Debug("%s: handle = %d", __FUNCTION__, handle);
|
||||
|
||||
if (experts_.count(handle) == 0)
|
||||
{
|
||||
if (experts_.size() == 0)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
|
||||
auto slot = std::make_unique<MtExpertSlot>();
|
||||
slot->expert = expert;
|
||||
slot->event_connection = expert->OnEvent.connect(boost::bind(&MtServer::OnEvent, this, std::placeholders::_1));
|
||||
slot->deinit_connection = expert->OnDeinit.connect(boost::bind(&MtServer::OnDeinitExpert, this, std::placeholders::_1));
|
||||
experts_[handle] = std::move(slot);
|
||||
|
||||
Send(MtExpertAddedMsg(handle));
|
||||
}
|
||||
}
|
||||
|
||||
void MtServer::DoAccept()
|
||||
{
|
||||
// The new connection gets its own strand
|
||||
acceptor_.async_accept(
|
||||
boost::asio::make_strand(context_),
|
||||
boost::beast::bind_front_handler(
|
||||
&MtServer::OnAccept,
|
||||
shared_from_this()));
|
||||
}
|
||||
|
||||
void MtServer::OnAccept(boost::beast::error_code ec,
|
||||
boost::asio::ip::tcp::socket socket)
|
||||
{
|
||||
log_.Trace("%s: enter", __FUNCTION__);
|
||||
|
||||
if (ec == boost::asio::error::operation_aborted)
|
||||
{
|
||||
log_.Info("%s: session was aborted.", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s: %s", __FUNCTION__, ec.what().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
WebsocketStream ws(std::move(socket));
|
||||
auto connection = std::make_shared<MtConnection>(std::move(ws));
|
||||
connection->OnConnected.connect([con = connection->weak_from_this(), this]() {
|
||||
auto connection = con.lock();
|
||||
if (connection)
|
||||
{
|
||||
auto it = std::find(pending_connections_.begin(), pending_connections_.end(), connection);
|
||||
if (it != pending_connections_.end())
|
||||
{
|
||||
pending_connections_.erase(it);
|
||||
connections_.push_back(connection);
|
||||
log_.Trace("%s: Pushed connection %p to collection", __FUNCTION__, connection.get());
|
||||
|
||||
std::vector<int> expert_list;
|
||||
for (const auto& e : experts_)
|
||||
expert_list.push_back(e.first);
|
||||
|
||||
MtExpertListMsg msg(std::move(expert_list));
|
||||
connection->Send(msg.Serialize());
|
||||
}
|
||||
}
|
||||
});
|
||||
connection->OnConnectionFailed.connect([con = connection->weak_from_this(), this](const std::string& msg) {
|
||||
log_.Warning("MtServer::OnConnectionFailed: %s", msg.c_str());
|
||||
auto connection = con.lock();
|
||||
if (connection)
|
||||
{
|
||||
if (pending_connections_.size() > 0)
|
||||
{
|
||||
auto it = std::find(pending_connections_.begin(), pending_connections_.end(), connection);
|
||||
if (it != pending_connections_.end())
|
||||
{
|
||||
pending_connections_.erase(it);
|
||||
log_.Trace("MtServer::OnConnectionFailed: Removed connection %p from pending collection", connection.get());
|
||||
}
|
||||
}
|
||||
if (connections_.size() > 0)
|
||||
{
|
||||
auto it = std::find(connections_.begin(), connections_.end(), connection);
|
||||
if (it != connections_.end())
|
||||
{
|
||||
connections_.erase(it);
|
||||
log_.Trace("MtServer::OnConnectionFailed: Removed connection %p from collection", connection.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
connection->OnDisconnected.connect([con = connection->weak_from_this(), this]() {
|
||||
auto connection = con.lock();
|
||||
if (connection)
|
||||
{
|
||||
auto it = std::find(connections_.begin(), connections_.end(), connection);
|
||||
if (it != connections_.end())
|
||||
{
|
||||
connections_.erase(it);
|
||||
log_.Trace("MtServer::OnDisconnected: Removed connection %p from collection", connection.get());
|
||||
}
|
||||
}
|
||||
});
|
||||
connection->OnMessageReceived.connect([con = connection->weak_from_this(), this](const std::string& msg) {
|
||||
log_.Trace("MtServer::OnMessageReceived: %s", msg.c_str());
|
||||
auto command = ParseMessage(msg);
|
||||
if (command)
|
||||
{
|
||||
if (experts_.count(command->getExpertHandle()) > 0)
|
||||
{
|
||||
experts_[command->getExpertHandle()]->expert->Process(std::move(command), [&](const MtResponse& response) {
|
||||
auto connection = con.lock();
|
||||
if (connection)
|
||||
connection->Send(response.Serialize());
|
||||
else
|
||||
log_.Warning("MtServer::OnMessageReceived: connection is not valid (null)");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
log_.Warning("MtServer::OnMessageReceived: Expert is not found with handle %d", command->getExpertHandle());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_.Warning("MtServer::OnMessageReceived: Failed to parse command from message: %s", msg.c_str());
|
||||
}
|
||||
});
|
||||
pending_connections_.push_back(connection);
|
||||
connection->Accept();
|
||||
|
||||
// Accept another connection
|
||||
DoAccept();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<MtCommand> MtServer::ParseMessage(const std::string& msg)
|
||||
{
|
||||
std::unique_ptr<MtCommand> command;
|
||||
|
||||
using namespace boost::spirit;
|
||||
using rule_s = qi::rule<std::string::const_iterator, std::string()>;
|
||||
using rule = qi::rule<std::string::const_iterator>;
|
||||
|
||||
std::string message_type;
|
||||
std::string expert_handle;
|
||||
std::string command_id;
|
||||
std::string command_type;
|
||||
std::string payload;
|
||||
|
||||
rule_s word_p = qi::as_string[+(qi::char_ - qi::char_(';'))];
|
||||
rule message_type_p = word_p[boost::phoenix::ref(message_type) = qi::_1];
|
||||
rule expert_handle_p = +qi::char_(';') >> word_p[boost::phoenix::ref(expert_handle) = qi::_1];
|
||||
rule command_id_p = +qi::char_(';') >> word_p[boost::phoenix::ref(command_id) = qi::_1];
|
||||
rule command_type_p = +qi::char_(';') >> word_p[boost::phoenix::ref(command_type) = qi::_1];
|
||||
rule payload_p = +qi::char_(';') >> word_p[boost::phoenix::ref(payload) = qi::_1];
|
||||
|
||||
bool ok = qi::parse(msg.begin(), msg.end(), message_type_p >> -expert_handle_p >> -command_id_p >> -command_type_p >> -payload_p);
|
||||
if (ok)
|
||||
{
|
||||
command = std::make_unique<MtCommand>(std::stoi(expert_handle),
|
||||
std::stoi(command_id), std::stoi(command_type), std::move(payload));
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
// Start accepting incoming connections
|
||||
void MtServer::Start()
|
||||
{
|
||||
log_.Debug("%s: entry. port = %d", __FUNCTION__, port_);
|
||||
|
||||
DoAccept();
|
||||
}
|
||||
|
||||
void MtServer::Stop()
|
||||
{
|
||||
log_.Debug("%s: entry. port = %d", __FUNCTION__, port_);
|
||||
|
||||
for(auto& c : connections_)
|
||||
c->Close();
|
||||
|
||||
connections_.clear();
|
||||
pending_connections_.clear();
|
||||
|
||||
acceptor_.cancel();
|
||||
|
||||
OnStopped(port_);
|
||||
}
|
||||
|
||||
void MtServer::OnEvent(const MtEvent& event)
|
||||
{
|
||||
log_.Trace("%s: entry.", __FUNCTION__);
|
||||
|
||||
Send(event);
|
||||
}
|
||||
|
||||
void MtServer::OnDeinitExpert(int handle)
|
||||
{
|
||||
log_.Debug("%s: entry. handle = %d", __FUNCTION__, handle);
|
||||
|
||||
if (experts_.count(handle) > 0)
|
||||
{
|
||||
log_.Debug("%s: remove expert %d from collection", __FUNCTION__, handle);
|
||||
experts_.erase(handle);
|
||||
Send(MtExpertRemovedMsg(handle));
|
||||
}
|
||||
|
||||
if (experts_.size() == 0)
|
||||
{
|
||||
log_.Debug("%s: expert count = 0. Starting stop timer (1 sec) ...", __FUNCTION__, handle);
|
||||
stop_timer_.expires_from_now(boost::asio::chrono::seconds(STOP_EXPERT_INTERVAL));
|
||||
stop_timer_.async_wait([this](const boost::system::error_code& /*ec*/) {
|
||||
if (experts_.size() == 0)
|
||||
Stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void MtServer::Send(const MtMessage& message)
|
||||
{
|
||||
auto msg = message.Serialize();
|
||||
log_.Trace("%s: %s", __FUNCTION__, msg.c_str());
|
||||
for (auto& c : connections_)
|
||||
c->Send(msg);
|
||||
}
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <boost/signals2/connection.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/beast.hpp>
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "MtMessage.h"
|
||||
|
||||
class MtConnection;
|
||||
class MtExpert;
|
||||
|
||||
struct MtExpertSlot
|
||||
{
|
||||
MtExpert* expert;
|
||||
boost::signals2::connection event_connection;
|
||||
boost::signals2::connection deinit_connection;
|
||||
|
||||
~MtExpertSlot()
|
||||
{
|
||||
event_connection.disconnect();
|
||||
deinit_connection.disconnect();
|
||||
}
|
||||
};
|
||||
|
||||
class MtServer : public std::enable_shared_from_this<MtServer>
|
||||
{
|
||||
public:
|
||||
MtServer(
|
||||
boost::asio::io_context& context,
|
||||
unsigned short port);
|
||||
~MtServer();
|
||||
|
||||
void AddExpert(int handle, MtExpert* expert);
|
||||
|
||||
boost::signals2::signal<void(unsigned short)> OnStopped;
|
||||
|
||||
private:
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
void DoAccept();
|
||||
void OnAccept(boost::beast::error_code ec,
|
||||
boost::asio::ip::tcp::socket socket);
|
||||
|
||||
std::unique_ptr<MtCommand> ParseMessage(const std::string& msg);
|
||||
|
||||
void OnEvent(const MtEvent& event);
|
||||
void OnDeinitExpert(int handle);
|
||||
|
||||
void Send(const MtMessage& message);
|
||||
|
||||
Logger log_{ "MtServer" };
|
||||
boost::asio::io_context& context_;
|
||||
unsigned short port_;
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
std::vector<std::shared_ptr<MtConnection>> pending_connections_;
|
||||
std::vector<std::shared_ptr<MtConnection>> connections_;
|
||||
std::unordered_map<int, std::unique_ptr<MtExpertSlot>> experts_;
|
||||
boost::asio::steady_timer stop_timer_;
|
||||
};
|
||||
Executable
+224
@@ -0,0 +1,224 @@
|
||||
// MtService.cpp : Defines the functions for the static library.
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
#include "framework.h"
|
||||
#include "MtService.h"
|
||||
|
||||
#include "MtExpert.h"
|
||||
#include "LogConfigurator.h"
|
||||
#include "MtServer.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// MtService
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
class MtServiceImpl
|
||||
{
|
||||
public:
|
||||
MtServiceImpl();
|
||||
~MtServiceImpl();
|
||||
|
||||
void InitExpert(int port, int handle, std::unique_ptr<MetaTraderHandler> mt_handler);
|
||||
void DeinitExpert(int handle);
|
||||
|
||||
void SendEvent(int handle, int event_type, const std::string& payload);
|
||||
void SendResponse(int handle, const std::string& payload);
|
||||
|
||||
int GetCommandType(int handle);
|
||||
std::string GetCommandPayload(int handle);
|
||||
|
||||
void LogError(const std::string& error);
|
||||
|
||||
private:
|
||||
void OnServerStopped(unsigned short port);
|
||||
void ThreadProc();
|
||||
|
||||
Logger log_{ "MtService" };
|
||||
boost::asio::io_context context_;
|
||||
std::unique_ptr<boost::asio::io_context::work> work_;
|
||||
std::thread thread_;
|
||||
|
||||
std::unordered_map<int, std::unique_ptr<MtExpert>> experts_;
|
||||
std::unordered_map<unsigned short, std::shared_ptr<MtServer>> servers_;
|
||||
};
|
||||
|
||||
MtServiceImpl::MtServiceImpl()
|
||||
: work_(new boost::asio::io_context::work(context_))
|
||||
, thread_(&MtServiceImpl::ThreadProc, this)
|
||||
{
|
||||
log_.Debug("%s: service created", __FUNCTION__);
|
||||
}
|
||||
|
||||
MtServiceImpl::~MtServiceImpl()
|
||||
{
|
||||
log_.Trace("%s: destructor started", __FUNCTION__);
|
||||
|
||||
boost::asio::post(context_, [this]() {
|
||||
if (experts_.size() > 0)
|
||||
{
|
||||
for (auto& e : experts_)
|
||||
e.second->Deinit();
|
||||
experts_.clear();
|
||||
}
|
||||
});
|
||||
work_.reset();
|
||||
if (thread_.joinable())
|
||||
thread_.join();
|
||||
|
||||
log_.Debug("%s: service destroyed", __FUNCTION__);
|
||||
}
|
||||
|
||||
void MtServiceImpl::InitExpert(int port, int handle, std::unique_ptr<MetaTraderHandler> mt_handler)
|
||||
{
|
||||
log_.Debug("%s: port = %d, handle = %d", __FUNCTION__, port, handle);
|
||||
|
||||
boost::asio::post(context_, [port, handle, mt_h = std::move(mt_handler), this]() mutable {
|
||||
auto expert = std::make_unique<MtExpert>(handle, std::move(mt_h));
|
||||
if (servers_.count(port) == 0)
|
||||
{
|
||||
auto server = std::make_shared<MtServer>(context_, port);
|
||||
server->OnStopped.connect(boost::bind(&MtServiceImpl::OnServerStopped, this, std::placeholders::_1));
|
||||
servers_[port] = server;
|
||||
}
|
||||
|
||||
servers_[port]->AddExpert(handle, expert.get());
|
||||
experts_[handle] = std::move(expert);
|
||||
});
|
||||
}
|
||||
|
||||
void MtServiceImpl::DeinitExpert(int handle)
|
||||
{
|
||||
log_.Debug("%s: handle = %d", __FUNCTION__, handle);
|
||||
|
||||
boost::asio::post(context_, [handle, this]() {
|
||||
if (experts_.count(handle) > 0)
|
||||
{
|
||||
experts_[handle]->Deinit();
|
||||
experts_.erase(handle);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MtServiceImpl::SendEvent(int handle, int event_type, const std::string& payload)
|
||||
{
|
||||
log_.Trace("%s: handle = %d, event_type = %d, payload = %s", __FUNCTION__, handle, event_type, payload.c_str());
|
||||
|
||||
boost::asio::post(context_, [handle, event_type, payload = payload, this]() {
|
||||
if (experts_.count(handle) > 0)
|
||||
experts_[handle]->SendEvent(event_type, payload);
|
||||
});
|
||||
}
|
||||
|
||||
void MtServiceImpl::SendResponse(int handle, const std::string& payload)
|
||||
{
|
||||
log_.Trace("%s: handle = %d, payload = %s", __FUNCTION__, handle, payload.c_str());
|
||||
|
||||
boost::asio::post(context_, [handle, payload = payload, this]() {
|
||||
if (experts_.count(handle) > 0)
|
||||
experts_[handle]->SendResponse(payload);
|
||||
});
|
||||
}
|
||||
|
||||
int MtServiceImpl::GetCommandType(int handle)
|
||||
{
|
||||
log_.Trace("%s: handle = %d", __FUNCTION__, handle);
|
||||
|
||||
std::packaged_task<int()> task([handle, this]() {
|
||||
return (experts_.count(handle) > 0) ? experts_[handle]->GetCommandType() : 0;
|
||||
});
|
||||
auto f = task.get_future();
|
||||
boost::asio::post(context_, std::bind(std::move(task)));
|
||||
return f.get();
|
||||
}
|
||||
|
||||
std::string MtServiceImpl::GetCommandPayload(int handle)
|
||||
{
|
||||
log_.Trace("%s: handle = %d", __FUNCTION__, handle);
|
||||
|
||||
std::packaged_task<std::string()> task([handle, this]() {
|
||||
return (experts_.count(handle) > 0) ? experts_[handle]->GetCommandPayload() : 0;
|
||||
});
|
||||
auto f = task.get_future();
|
||||
boost::asio::post(context_, std::bind(std::move(task)));
|
||||
return f.get();
|
||||
}
|
||||
|
||||
void MtServiceImpl::LogError(const std::string& error)
|
||||
{
|
||||
log_.Error("%s: %s", __FUNCTION__, error.c_str());
|
||||
}
|
||||
|
||||
void MtServiceImpl::OnServerStopped(unsigned short port)
|
||||
{
|
||||
log_.Trace("%s: port = %d", __FUNCTION__, port);
|
||||
|
||||
if (servers_.count(port) > 0)
|
||||
servers_.erase(port);
|
||||
}
|
||||
|
||||
void MtServiceImpl::ThreadProc()
|
||||
{
|
||||
log_.Debug("%s: started", __FUNCTION__);
|
||||
context_.run();
|
||||
log_.Debug("%s: stopped", __FUNCTION__);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// MtService
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
MtService::MtService()
|
||||
{
|
||||
LogConfigurator::Setup(LogLevel::Trace, OutputType::Console | OutputType::File, "MtApiService");
|
||||
impl_ = std::make_unique<MtServiceImpl>();
|
||||
}
|
||||
|
||||
MtService::~MtService()
|
||||
{
|
||||
}
|
||||
|
||||
MtService& MtService::GetInstance()
|
||||
{
|
||||
static MtService instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void MtService::InitExpert(int port, int handle, std::unique_ptr<MetaTraderHandler> mt_handler)
|
||||
{
|
||||
impl_->InitExpert(port, handle, std::move(mt_handler));
|
||||
}
|
||||
|
||||
void MtService::DeinitExpert(int handle)
|
||||
{
|
||||
impl_->DeinitExpert(handle);
|
||||
}
|
||||
|
||||
void MtService::SendEvent(int handle, int event_type, const std::string& payload)
|
||||
{
|
||||
impl_->SendEvent(handle, event_type, payload);
|
||||
}
|
||||
|
||||
void MtService::SendResponse(int handle, const std::string& payload)
|
||||
{
|
||||
impl_->SendResponse(handle, payload);
|
||||
}
|
||||
|
||||
int MtService::GetCommandType(int handle)
|
||||
{
|
||||
return impl_->GetCommandType(handle);
|
||||
}
|
||||
|
||||
std::string MtService::GetCommandPayload(int handle)
|
||||
{
|
||||
return impl_->GetCommandPayload(handle);
|
||||
}
|
||||
|
||||
void MtService::LogError(const std::string& error)
|
||||
{
|
||||
impl_->LogError(error);
|
||||
}
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "MetaTraderHandler.h"
|
||||
|
||||
class MtServiceImpl;
|
||||
|
||||
class MtService
|
||||
{
|
||||
private:
|
||||
MtService();
|
||||
~MtService();
|
||||
|
||||
public:
|
||||
static MtService& GetInstance();
|
||||
|
||||
void InitExpert(int port, int handle, std::unique_ptr<MetaTraderHandler> mt_handler);
|
||||
void DeinitExpert(int handle);
|
||||
|
||||
void SendEvent(int handle, int event_type, const std::string& payload);
|
||||
void SendResponse(int handle, const std::string& payload);
|
||||
|
||||
int GetCommandType(int handle);
|
||||
std::string GetCommandPayload(int handle);
|
||||
|
||||
void LogError(const std::string& error);
|
||||
|
||||
private:
|
||||
std::unique_ptr<MtServiceImpl> impl_;
|
||||
};
|
||||
Executable
+175
@@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{3effff03-2a1d-48aa-a49a-9f90889f31e0}</ProjectGuid>
|
||||
<RootNamespace>MtService</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>C:\Users\vdemy\source\repos\MtService\MtService;C:\Users\vdemy\source\repos\MtService\thirdparty\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>Default</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<Lib />
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CommandTask.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="LogConfigurator.h" />
|
||||
<ClInclude Include="Logger.h" />
|
||||
<ClInclude Include="LogLevel.h" />
|
||||
<ClInclude Include="MetaTraderHandler.h" />
|
||||
<ClInclude Include="MtConnection.h" />
|
||||
<ClInclude Include="MtExpert.h" />
|
||||
<ClInclude Include="MtMessage.h" />
|
||||
<ClInclude Include="MtServer.h" />
|
||||
<ClInclude Include="MtService.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="LogConfigurator.cpp" />
|
||||
<ClCompile Include="Logger.cpp" />
|
||||
<ClCompile Include="MtConnection.cpp" />
|
||||
<ClCompile Include="MtExpert.cpp" />
|
||||
<ClCompile Include="MtServer.cpp" />
|
||||
<ClCompile Include="MtService.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MtService.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MetaTraderHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MtExpert.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MtMessage.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CommandTask.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MtConnection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MtServer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LogConfigurator.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Logger.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LogLevel.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="MtService.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MtExpert.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MtConnection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MtServer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LogConfigurator.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Logger.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
// pch.h: This is a precompiled header file.
|
||||
// Files listed below are compiled only once, improving build performance for future builds.
|
||||
// This also affects IntelliSense performance, including code completion and many code browsing features.
|
||||
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
|
||||
// Do not add files here that you will be updating frequently as this negates the performance advantage.
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
// add headers that you want to pre-compile here
|
||||
#include "framework.h"
|
||||
#include <SDKDDKVer.h>
|
||||
|
||||
#endif //PCH_H
|
||||
Reference in New Issue
Block a user