Added new projects of new RPC core: MyClient and MtServivce

This commit is contained in:
Viacheslav Demydiuk
2024-01-04 18:35:15 +02:00
parent 2a13699900
commit feb0192b11
24 changed files with 2078 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+180
View File
@@ -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();
}
}
+184
View File
@@ -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);
}
}
+27
View File
@@ -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_;
};
+90
View File
@@ -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));
}
+37
View File
@@ -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);
};
+11
View File
@@ -0,0 +1,11 @@
#pragma once
enum class LogLevel
{
Fatal,
Error,
Warning,
Info,
Debug,
Trace
};
+90
View File
@@ -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);
}
+22
View File
@@ -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_;
};
+8
View File
@@ -0,0 +1,8 @@
#pragma once
class MetaTraderHandler
{
public:
virtual ~MetaTraderHandler() = default;
virtual void SendTickToMetaTrader() = 0;
};
+164
View File
@@ -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();
}
+49
View File
@@ -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_;
};
+73
View File
@@ -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() : "";
}
+38
View File
@@ -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_;
};
+204
View File
@@ -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_;
};
+295
View File
@@ -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);
}
+66
View File
@@ -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_;
};
+224
View File
@@ -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);
}
+32
View File
@@ -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_;
};
+175
View File
@@ -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>
+78
View File
@@ -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>
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+5
View File
@@ -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.
+14
View File
@@ -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