Refactor `NfcHanler` namespace to `NfcHandler` and enhance code consistency.
275 lines
8.8 KiB
C#
275 lines
8.8 KiB
C#
using System.Diagnostics;
|
|
using System.Reflection;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace NfcC7_DLL.NfcHandler.Utils
|
|
{
|
|
public class CliRunner
|
|
{
|
|
//static readonly Logger log = LogManager.GetLogger(typeof(CliRunner));
|
|
//private readonly ILog log;
|
|
private List<Task> taskPool = new List<Task>();
|
|
private long startTime;
|
|
|
|
public List<Task> TaskPool { get { return taskPool; } }
|
|
public void AddTask(Task task) { taskPool.Add(task); }
|
|
public void WaitAll() { Task.WaitAll(taskPool.ToArray()); }
|
|
|
|
/// <summary>
|
|
/// continue with WhenAny() to commpare results if time out is reached
|
|
/// </summary>
|
|
/// <param name="timeout"></param>
|
|
/// <returns></returns>
|
|
public Task AddTimeOut(int timeout)
|
|
{
|
|
Task timeOut = Task.Delay(timeout);
|
|
taskPool.Add(timeOut);
|
|
return timeOut;
|
|
}
|
|
/// <summary>
|
|
/// mainly used for time out additional task to compare results
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Task WhenAny() { return Task.WhenAny(taskPool.ToArray()); }
|
|
|
|
public void Clear() { taskPool.Clear(); }
|
|
public void CancelAll() { Task.WhenAll(taskPool).ContinueWith(t => { }); }
|
|
|
|
|
|
public void StartAll()
|
|
{
|
|
taskPool.ForEach(t => t.Start());
|
|
}
|
|
|
|
public bool AreTasksDone()
|
|
{
|
|
return taskPool.All(task => task.IsCompleted);
|
|
}
|
|
|
|
public long StartTime
|
|
{
|
|
get => startTime;
|
|
}
|
|
|
|
public bool TimeOutReceived(long timeout)
|
|
{
|
|
return (DateTime.Now.Ticks - startTime) > timeout;
|
|
}
|
|
|
|
public CliRunner(bool isCliLogging)
|
|
{
|
|
startTime = DateTime.Now.Ticks;
|
|
|
|
if (isCliLogging)
|
|
{
|
|
try
|
|
{
|
|
/*log = new ScopedLoggerFactory().CreateLogger<CliRunner>(
|
|
@"C:\TBF\Logs\CliRunner.txt",
|
|
10, // maxFileSizeMB
|
|
7, // maxBackups
|
|
log4net.Core.Level.Debug,
|
|
true, // zipRolledFiles
|
|
true, // singleZipPerDay
|
|
TimeSpan.FromMinutes(2) // zipScanInterval
|
|
);*/
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Fallback to console or handle gracefully
|
|
Console.WriteLine($"Failed to initialize logger: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="fileName"></param>
|
|
/// <param name="args"></param>
|
|
/// <typeparam name="T"></typeparam>
|
|
public Task AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg)
|
|
{
|
|
var task = SendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettingsRead(eMeterArg));
|
|
taskPool.Add(task);
|
|
return task;
|
|
}
|
|
|
|
public Task AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg, string arg)
|
|
{
|
|
var task = SendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettingsWrite(eMeterArg, arg));
|
|
taskPool.Add(task);
|
|
return task;
|
|
}
|
|
|
|
public void AddSendAsync(string fileName, string args)
|
|
{
|
|
var task = SendAsync(fileName, args);
|
|
taskPool.Add(task);
|
|
}
|
|
|
|
|
|
public async Task<string> SendAsync(string fileName, string args, CancellationToken ct = default)
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = fileName,
|
|
Arguments = args,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true
|
|
};
|
|
|
|
var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
|
try
|
|
{
|
|
process.Start();
|
|
|
|
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
|
|
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
|
|
|
|
try
|
|
{
|
|
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
if (!process.HasExited)
|
|
{
|
|
process.Kill();
|
|
}
|
|
|
|
throw;
|
|
}
|
|
|
|
string allOutput = (stdOutTask.Result ?? "") + (stdErrTask.Result ?? "");
|
|
//log?.Debug(allOutput);
|
|
return allOutput;
|
|
}
|
|
finally
|
|
{
|
|
process?.Dispose();
|
|
}
|
|
}
|
|
|
|
public Task<T> AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new()
|
|
{
|
|
var task = RunAndCaptureJsonAsync<T>(data.SerialPortCmdClientPath, data.DefaultArgSettingsRead(eMeterArg));
|
|
taskPool.Add(task);
|
|
return task;
|
|
}
|
|
|
|
public async Task<T> RunAndCaptureJsonAsync<T>(string fileName, string args, CancellationToken ct = default) where T : new()
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = fileName,
|
|
Arguments = args,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true
|
|
};
|
|
|
|
var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
|
try
|
|
{
|
|
process.Start();
|
|
|
|
var stdoutTask = process.StandardOutput.ReadToEndAsync();
|
|
var stderrTask = process.StandardError.ReadToEndAsync();
|
|
|
|
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct));
|
|
|
|
string allOutput = (stdoutTask.Result ?? "") + (stderrTask.Result ?? "");
|
|
//log?.Debug(allOutput);
|
|
|
|
string json = ExtractJson(allOutput);
|
|
if (TryJsonStringDeserialize(json, out T result)) return result;
|
|
return default;
|
|
}
|
|
finally
|
|
{
|
|
process?.Dispose();
|
|
}
|
|
}
|
|
|
|
public bool TryJsonStringDeserialize<T>(string json, out T runAndCaptureJsonAsync) where T : new()
|
|
{
|
|
if (json != null)
|
|
{
|
|
try
|
|
{
|
|
runAndCaptureJsonAsync = JsonConvert.DeserializeObject<T>(json);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// log.Debug(ex.Message);
|
|
runAndCaptureJsonAsync = TryConvert<T>(json);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
runAndCaptureJsonAsync = default;
|
|
return false;
|
|
}
|
|
|
|
|
|
private static T TryConvert<T>(string json) where T : new()
|
|
{
|
|
|
|
T obj = new T();
|
|
|
|
try
|
|
{
|
|
JObject jObject = JObject.Parse(json);
|
|
|
|
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
|
{
|
|
if (!prop.CanWrite) continue;
|
|
|
|
JToken token;
|
|
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
|
|
{
|
|
try
|
|
{
|
|
object value = token.ToObject(prop.PropertyType);
|
|
prop.SetValue(obj, value);
|
|
}
|
|
catch
|
|
{
|
|
// leave default if conversion fails
|
|
}
|
|
}
|
|
// else → keep default value
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"TryConvert failed: {ex.Message}");
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
public string ExtractJson(string text)
|
|
{
|
|
int start = text.IndexOf('{');
|
|
int end = text.LastIndexOf('}');
|
|
|
|
if (start >= 0 && end > start)
|
|
{
|
|
return text.Substring(start, end - start + 1);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
} |