initial commit
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
namespace RTCSync.Options;
|
||||
|
||||
public interface IOption
|
||||
{
|
||||
string Description { get; }
|
||||
List<string> OptionNames { get; }
|
||||
string OptionValues { get; }
|
||||
void Execute(OptionArgs args);
|
||||
}
|
||||
|
||||
public class OptionArgs
|
||||
{
|
||||
public Dictionary<string, string> OptionValues { get; set; }
|
||||
public List<string> Arguments { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
namespace RTCSync.Options;
|
||||
|
||||
public class OptionDispatcher
|
||||
{
|
||||
private readonly Dictionary<string, IOption> _registredOptions = new();
|
||||
|
||||
public void Register(IOption option)
|
||||
{
|
||||
foreach (var optionName in option.OptionNames)
|
||||
{
|
||||
_registredOptions[optionName] = option;
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
PrintHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
var flag = args[0];
|
||||
|
||||
if (!_registredOptions.TryGetValue(flag, out var command))
|
||||
{
|
||||
Console.WriteLine($"Неизвестный параметр: {flag}");
|
||||
PrintHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Парсим все остальные аргументы как опции
|
||||
var commandArgs = ParseArguments(args.Skip(1).ToArray());
|
||||
command.Execute(commandArgs);
|
||||
}
|
||||
|
||||
private static OptionArgs ParseArguments(string[] args)
|
||||
{
|
||||
var optionValues = new Dictionary<string, string>();
|
||||
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (!args[i].StartsWith("--"))
|
||||
{
|
||||
optionValues.Add("unnamed", args[i]);
|
||||
continue;
|
||||
}
|
||||
optionValues[args[i]] = args[i + 1];
|
||||
i++;
|
||||
}
|
||||
|
||||
return new OptionArgs
|
||||
{
|
||||
OptionValues = optionValues,
|
||||
Arguments = []
|
||||
};
|
||||
}
|
||||
|
||||
private static void PrintOption(string optionLine, string description)
|
||||
{
|
||||
var terminalWidth = Console.WindowWidth;
|
||||
// Console.WriteLine(terminalWidth);
|
||||
// for (var i = 0; i < terminalWidth; i++)
|
||||
// Console.Write("_");
|
||||
// Console.WriteLine();
|
||||
const int columnWidth = 44;
|
||||
var descriptionWidth = terminalWidth - columnWidth;
|
||||
|
||||
// Если название опции слишком длиное, перенести описание на следующую строку
|
||||
if (optionLine.Length > columnWidth)
|
||||
{
|
||||
Console.WriteLine(optionLine);
|
||||
var lines = SplitText(description, descriptionWidth);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
Console.WriteLine($"{new string(' ', 2)}{line}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var formatted = optionLine.PadRight(columnWidth);
|
||||
var descLines = SplitText(description, descriptionWidth);
|
||||
|
||||
// Первая строка с опцией и началом описания
|
||||
Console.WriteLine($"{formatted}{descLines[0]}");
|
||||
|
||||
// Остальные строки описания с выравниванием
|
||||
for (var i = 1; i < descLines.Count; i++)
|
||||
{
|
||||
Console.WriteLine($"{new string(' ', columnWidth)}{descLines[i]}");
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
private static List<string> SplitText(string text, int maxWidth)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
|
||||
if (maxWidth < 1) return [text];
|
||||
|
||||
var words = text.Split(' ');
|
||||
var current = "";
|
||||
|
||||
foreach (var word in words)
|
||||
{
|
||||
if ((current + word + " ").Length <= maxWidth)
|
||||
{
|
||||
current += word + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(current))
|
||||
lines.Add(current.TrimEnd());
|
||||
current = word + " ";
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(current))
|
||||
lines.Add(current.TrimEnd());
|
||||
|
||||
return lines.Count > 0 ? lines : [text];
|
||||
}
|
||||
|
||||
private void PrintHelp()
|
||||
{
|
||||
Console.WriteLine("RTCSync - утилита для работы с часами реального времени DS3231, подключенными по шине I^2C через CH431. Требует winusb драйвер.\n");
|
||||
Console.WriteLine("Использование: RTCSync.cli.exe <параметр> [опции]\n");
|
||||
Console.WriteLine("Параметры:");
|
||||
|
||||
foreach (var cmd in _registredOptions.Values.Distinct())
|
||||
{
|
||||
var optionNames = cmd.OptionNames.Aggregate((current, next) => current + ", " + next);
|
||||
var optionLine = $" {optionNames} {cmd.OptionValues}";
|
||||
PrintOption(optionLine, cmd.Description);
|
||||
|
||||
// Console.WriteLine($" {cmd.OptionNames.Aggregate((current, next) => current + $" {cmd.OptionValues}, " + next + $" {cmd.OptionValues}"), -54} {cmd.Description}");
|
||||
}
|
||||
|
||||
Console.WriteLine("\nПримеры:");
|
||||
Console.WriteLine(" RTCSync.cli.exe --set-time");
|
||||
Console.WriteLine(" RTCSync.cli.exe -s");
|
||||
Console.WriteLine(" RTCSync.cli.exe --set-time 0");
|
||||
Console.WriteLine(" RTCSync.cli.exe -s 0");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using RTCSync.Services;
|
||||
using RTCSync.Utils;
|
||||
|
||||
namespace RTCSync.Options;
|
||||
|
||||
public class ReadStatusOption : IOption
|
||||
{
|
||||
public string Description =>
|
||||
"Чтение регистров status, control и температуры с часов реального времени.";
|
||||
public List<string> OptionNames => ["-i", "--read-status"];
|
||||
|
||||
public string OptionValues => "";
|
||||
|
||||
public void Execute(OptionArgs args)
|
||||
{
|
||||
// bind and init CH431
|
||||
var device = DeviceDispatcher.SetUpDevice();
|
||||
if (device == null)
|
||||
return;
|
||||
DeviceReaders.PrintControlValues(device);
|
||||
DeviceReaders.PrintStatusValues(device);
|
||||
DeviceReaders.PrintTemperatureValue(device);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using RTCSync.Models;
|
||||
using RTCSync.Services;
|
||||
using RTCSync.Utils;
|
||||
|
||||
namespace RTCSync.Options;
|
||||
|
||||
public class ReadTimeOption : IOption
|
||||
{
|
||||
public string Description =>
|
||||
"Чтение времени с часов реального времени.";
|
||||
|
||||
public List<string> OptionNames => ["-r", "--read-time"];
|
||||
|
||||
public string OptionValues => "";
|
||||
|
||||
public void Execute(OptionArgs args)
|
||||
{
|
||||
// bind and init CH431
|
||||
var device = DeviceDispatcher.SetUpDevice();
|
||||
if (device == null)
|
||||
return;
|
||||
while (true)
|
||||
{
|
||||
DeviceReaders.PrintTimeRTC(device);
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
|
||||
device.ReleaseInterface(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using RTCSync.Models;
|
||||
using RTCSync.Services;
|
||||
using RTCSync.Utils;
|
||||
using static RTCSync.Utils.BinaryUtils;
|
||||
|
||||
namespace RTCSync.Options;
|
||||
|
||||
public class SetTimeOption : IOption
|
||||
{
|
||||
public string Description =>
|
||||
"Установка времени как в системе, на часах реального времени. Часы будут хранить локальное время (не UTC!). Можно сбросить время до минимального значения используя опцию \"0\".";
|
||||
|
||||
public List<string> OptionNames => ["-s", "--set-time"];
|
||||
|
||||
public string OptionValues => "[0]";
|
||||
|
||||
|
||||
public void Execute(OptionArgs args)
|
||||
{
|
||||
// Console.WriteLine("Вы уверены, что хотите перенастроить время на модуле реального времени? (Д/н)\n");
|
||||
// var isConfirmed = false;
|
||||
// while (!isConfirmed)
|
||||
// {
|
||||
// var proof = Console.ReadLine();
|
||||
// switch (proof)
|
||||
// {
|
||||
// case "Н" or "н" or "N" or "n":
|
||||
// return;
|
||||
// case "Д" or "д" or "Y" or "y":
|
||||
// isConfirmed = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
var device = DeviceDispatcher.SetUpDevice();
|
||||
if (device == null)
|
||||
return;
|
||||
|
||||
// ставим текущее время в RTC модуль. Записывается не с первого раза, поэтому хак
|
||||
// важно каждый раз пытаться забить актуальное время
|
||||
// TODO: убрать хак
|
||||
if (args.OptionValues.TryGetValue("unnamed", out var value) && value is "0")
|
||||
{
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var dt = DateTime.MinValue;
|
||||
DeviceWriters.SetTime(device, dt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
// FIXME: вызывает падение на windows 7
|
||||
var dt = DateTime.Now;
|
||||
DeviceWriters.SetTime(device, dt);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("Время установлено");
|
||||
|
||||
|
||||
// читаем статус
|
||||
var status = DeviceReaders.GetStatusRegister(device);
|
||||
DeviceReaders.PrintStatusValues(status);
|
||||
|
||||
// Сбрасываем OSF (Oscillator Stop Flags) бит 7 регистра 0x0F
|
||||
// Иначе часы будут считаться ненадёжными
|
||||
Console.WriteLine("Сбрасываем OSF");
|
||||
DeviceWriters.ResetOSF(device, status);
|
||||
|
||||
// заново выводим статус для визуального контроля
|
||||
DeviceReaders.PrintStatusValues(device);
|
||||
|
||||
|
||||
// читаем данные контроля
|
||||
var control = DeviceReaders.GetControlRegister(device);
|
||||
DeviceReaders.PrintControlValues(control);
|
||||
|
||||
// если осциллятор не включен, то включаем
|
||||
if ((control[0] & 0x80) != 0)
|
||||
{
|
||||
Console.WriteLine("Включаем осциллятор");
|
||||
DeviceWriters.EnableEOSC(device, control);
|
||||
// заново выводим данные контроля для визуального контроля
|
||||
DeviceReaders.PrintControlValues(device);
|
||||
}
|
||||
|
||||
|
||||
// все сделали - выводим время
|
||||
while (true)
|
||||
{
|
||||
DeviceReaders.PrintTimeRTC(device);
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using RTCSync.Services;
|
||||
using RTCSync.Utils;
|
||||
|
||||
namespace RTCSync.Options;
|
||||
|
||||
public class SyncTimeOption : IOption
|
||||
{
|
||||
public string Description => "Синхронизирует системное время со временем модуля реального времени. Если часы хранят UTC время, то используйте опцию universal";
|
||||
public List<string> OptionNames => ["-S", "--sync-time"];
|
||||
public string OptionValues => "{local, l, universal, u}";
|
||||
|
||||
public void Execute(OptionArgs args)
|
||||
{
|
||||
var device = DeviceDispatcher.SetUpDevice();
|
||||
if (device == null)
|
||||
return;
|
||||
|
||||
Console.WriteLine("Время на модуле:");
|
||||
DeviceReaders.PrintTimeRTC(device);
|
||||
var timeModel = DeviceReaders.GetTimeRTC(device);
|
||||
|
||||
// время в формате понятном для kernel32
|
||||
WindowsTimeUtils.SystemTime systime;
|
||||
|
||||
if (args.OptionValues.TryGetValue("unnamed", out var value) && value is "universal" or "u")
|
||||
systime = new WindowsTimeUtils.SystemTime(timeModel.Time);
|
||||
else if (value is "local" or "l")
|
||||
systime = new WindowsTimeUtils.SystemTime(timeModel.Time.ToUniversalTime());
|
||||
else
|
||||
systime = new WindowsTimeUtils.SystemTime(timeModel.Time.ToUniversalTime());
|
||||
|
||||
Console.WriteLine(WindowsTimeUtils.SetSystemTime(ref systime)
|
||||
? "Время установлено в системе"
|
||||
: "Ошибка: нет прав администратора");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user