initial commit

This commit is contained in:
2026-06-25 12:19:29 +03:00
commit 1160477945
19 changed files with 898 additions and 0 deletions
+15
View File
@@ -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; } = [];
}
+145
View File
@@ -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");
}
}
+25
View File
@@ -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);
}
}
+30
View File
@@ -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);
}
}
+98
View File
@@ -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);
}
}
}
+36
View File
@@ -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)
? "Время установлено в системе"
: "Ошибка: нет прав администратора");
}
}