using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NLog;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MatplotlibCS
{
///
/// Обёртка над питоновским скриптом построения графиков
///
// ReSharper disable once InconsistentNaming
public class MatplotlibCS
{
#region Fields
private readonly ILogger _log;
///
/// Пусть к интерпрететору питона
///
private readonly string _pythonExePath;
///
/// Путь к скрипту dasPlot.py
///
private readonly string _dasPlotPyPath;
///
/// Path to build_figure.py
///
private readonly string _buildFigurePyPath;
///
/// Путь директории, в которой хранятся временные json-файлы, через которые передаются параметры задачи
///
private readonly string _jsonTempPath;
///
/// Python web service URL
///
private readonly string _serviceUrlCheckAliveMethod = "http://127.0.0.1:57123/";
///
/// Python web service URL
///
private readonly string _serviceUrlPlotMethod = "http://127.0.0.1:57123/plot";
///
/// Kill web service url
///
//private string _serviceUrlKillMethod = "http://127.0.0.1:57123/kill";
#endregion
#region .ctor
///
/// Обёртка над python скриптом, строящим matplotlib графики
///
/// Путь python.exe
/// Путь dasPlot.py
/// Path to build_figure.py
/// Опциональный путь директории, в которой хранятся временные json файлы, через которые передаются данные
public MatplotlibCS(string pythonExePath, string dasPlotPyPath, string buildFigurePyPath, string jsonTempPath = "c:\\MatplotlibCS")
{
_pythonExePath = pythonExePath;
_dasPlotPyPath = dasPlotPyPath;
_buildFigurePyPath = buildFigurePyPath;
_jsonTempPath = jsonTempPath;
_log = LogManager.GetLogger(typeof(MatplotlibCS).Name);
if (!Directory.Exists(_jsonTempPath))
Directory.CreateDirectory(_jsonTempPath);
}
#endregion
#region Public methods
public async Task BuildFigureViaFile(Figure task)
{
task.HealthCheck();
try
{
var json = BuildJson(task);
var fullpath = Path.Combine(AppContext.BaseDirectory, $"matplotlib_cs.input.json");
await File.WriteAllTextAsync(fullpath, json);
await RunPython(fullpath);
}
catch (Exception ex)
{
_log.Fatal($"Error while building figure: {ex.Message}\n{ex.StackTrace}");
}
}
///
/// Выполняет задачу построения графиков
///
/// Описание задачи
public async Task BuildFigure(Figure task)
{
task.HealthCheck();
try
{
await LaunchPythonWebServiceAsync();
var json = BuildJson(task);
JsonConvert.DefaultSettings = (() =>
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() });
return settings;
});
using var client = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(json), Encoding.UTF8, "application/json");
var response = await client.PostAsync(_serviceUrlPlotMethod, content);
var responseString = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
_log.Fatal($"Error while building figure: {ex.Message}\n{ex.StackTrace}");
}
}
#endregion
#region Private methods
private string BuildJson(Figure task)
{
if (!Path.IsPathRooted(task.FileName))
task.FileName = Path.Combine(_jsonTempPath, task.FileName);
var serializer = new JsonSerializer() { StringEscapeHandling = StringEscapeHandling.EscapeHtml };
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
serializer.Serialize(writer, task);
}
var json = sb.ToString();
return json;
}
private async Task RunPython(string inputfile)
{
var ps = new ProcessStartInfo
{
FileName = _pythonExePath,
Arguments = $"{_buildFigurePyPath} {inputfile}",
UseShellExecute = false,
RedirectStandardOutput = true
};
using Process process = Process.Start(ps);
await process.WaitForExitAsync();
}
///
/// Check if python web service is alive and if no, launches it
///
private async Task LaunchPythonWebServiceAsync()
{
if (!await CheckIfWebServiceIsUpAndRunningAsync())
{
var psi = new ProcessStartInfo(_pythonExePath, _dasPlotPyPath)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
_log.Info($"Starting python process {_pythonExePath}, {_dasPlotPyPath}");
Process.Start(psi);
// when starting python process, it's better to wait for some time to ensure, that
// web service started
await Task.Delay(2000);
}
}
///
/// Check if python web service is alive
///
/// true if service is up and running
private async Task CheckIfWebServiceIsUpAndRunningAsync()
{
try
{
_log.Info("Check if python web-service is already running");
var client = new HttpClient();
using var response = await client.GetAsync(_serviceUrlCheckAliveMethod);
_log.Info($"Service response status is {response.StatusCode}");
return (response.StatusCode == HttpStatusCode.OK);
}
catch (Exception)
{
_log.Info("Python web-service wasn't found");
//Any exception will returns false.
return false;
}
}
#endregion
}
}