This commit is contained in:
@@ -0,0 +1,437 @@
|
||||
using CheckEVS;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DahuaDiskChecker
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
string server = string.Empty;
|
||||
|
||||
//Console.WriteLine("=== ПРОВЕРКА СОСТОЯНИЯ ДИСКА DAHUA ===");
|
||||
#if DEBUG
|
||||
Console.WriteLine("Введи ip-адрес EVS:");
|
||||
server = Console.ReadLine();
|
||||
Console.WriteLine("Введи номер диска: (0 - количество неисправных, 1-24 - статус указанного");
|
||||
int diskNumber = Convert.ToInt32(Console.ReadLine());
|
||||
#else
|
||||
|
||||
// Парсинг аргументов
|
||||
if (args.Length < 2)
|
||||
{
|
||||
await PromoPrint();
|
||||
|
||||
//Console.WriteLine("Использование: DahuaDiskChecker.exe <IP> <номер_диска> [пользователь] [пароль]");
|
||||
//Console.WriteLine("Пример: DahuaDiskChecker.exe 172.16.48.147 10 admin admin");
|
||||
return;
|
||||
}
|
||||
server = args[0];
|
||||
if (!int.TryParse(args[1], out int diskNumber))
|
||||
{
|
||||
//Console.WriteLine("❌ Ошибка: номер диска должен быть числом");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
string user = args.Length > 2 ? args[2] : "admin";
|
||||
string password = args.Length > 3 ? args[3] : "4NUDZhJ7";
|
||||
|
||||
try
|
||||
{
|
||||
var checker = new DahuaDiskChecker();
|
||||
string status = await checker.CheckDiskStatusAsync(server, diskNumber, user, password);
|
||||
|
||||
// Вывод результата
|
||||
//Console.WriteLine();
|
||||
//Console.WriteLine(new string('=', 40));
|
||||
|
||||
try
|
||||
{
|
||||
Convert.ToInt32(status);
|
||||
Console.WriteLine(status);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (status == "NotFound")
|
||||
{
|
||||
Console.WriteLine("10");
|
||||
}
|
||||
else if (status == "Good")
|
||||
{
|
||||
Console.WriteLine("0");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("1");
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
Console.ReadKey();
|
||||
#endif
|
||||
|
||||
//Console.WriteLine(new string('=', 40));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"❌ Ошибка: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task PromoPrint()
|
||||
{
|
||||
// ╔ ═ ╦ ╗ ║ ╟ ╤ │ ─ ┷ ╢ ╚ ╝
|
||||
Console.WriteLine("╔════════════════════════════╤════════════╗");
|
||||
Console.WriteLine("║ autor: mastankov │ ver: 0.1 ║");
|
||||
Console.WriteLine("╟────────────────────────────┴────────────╢");
|
||||
Console.WriteLine("║ Пример: ║");
|
||||
Console.WriteLine("║ DahuaDiskChecker.exe <IP> <номер_диска> ║");
|
||||
Console.WriteLine("║ Вернет статус запрошенного диска ║");
|
||||
Console.WriteLine("║ ║");
|
||||
Console.WriteLine("║ DahuaDiskChecker.exe <IP> 0 ║");
|
||||
Console.WriteLine("║ Вернет количество неисправных дисков ║");
|
||||
Console.WriteLine("║ ║");
|
||||
Console.WriteLine("║ Нажми Enter, чтобы выйти ║");
|
||||
Console.WriteLine("╚═════════════════════════════════════════╝");
|
||||
//Console.ReadKey();
|
||||
}
|
||||
}
|
||||
|
||||
public class DahuaDiskChecker
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public DahuaDiskChecker()
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
}
|
||||
|
||||
// Вычисление MD5 хэша
|
||||
private string CalculateMD5(string input)
|
||||
{
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
|
||||
byte[] hashBytes = md5.ComputeHash(inputBytes);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in hashBytes)
|
||||
{
|
||||
sb.Append(b.ToString("X2"));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// Получение сессии
|
||||
private async Task<string> GetSessionAsync(string server, string user, string password)
|
||||
{
|
||||
// 1. Challenge запрос
|
||||
var challengeRequest = new
|
||||
{
|
||||
method = "global.login",
|
||||
@params = new
|
||||
{
|
||||
userName = user,
|
||||
password = "",
|
||||
clientType = "Web3.0"
|
||||
},
|
||||
id = 1,
|
||||
session = (string)null
|
||||
};
|
||||
|
||||
string json = JsonSerializer.Serialize(challengeRequest);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _httpClient.PostAsync($"http://{server}/RPC2_Login", content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var responseJson = await response.Content.ReadAsStringAsync();
|
||||
using JsonDocument doc = JsonDocument.Parse(responseJson);
|
||||
var root = doc.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("result", out var resultProp) || resultProp.GetBoolean())
|
||||
throw new Exception("Не удалось получить challenge");
|
||||
|
||||
string realm = root.GetProperty("params").GetProperty("realm").GetString();
|
||||
string random = root.GetProperty("params").GetProperty("random").GetString();
|
||||
string session = root.GetProperty("session").GetString();
|
||||
|
||||
// 2. Вычисление пароля
|
||||
string pwdHash = CalculateMD5($"{user}:{realm}:{password}");
|
||||
string passHash = CalculateMD5($"{user}:{random}:{pwdHash}");
|
||||
|
||||
// 3. Авторизация
|
||||
var authRequest = new
|
||||
{
|
||||
method = "global.login",
|
||||
@params = new
|
||||
{
|
||||
userName = user,
|
||||
clientType = "Web3.0",
|
||||
authorityType = "Default",
|
||||
passwordType = "Default",
|
||||
password = passHash
|
||||
},
|
||||
id = 2,
|
||||
session = session
|
||||
};
|
||||
|
||||
json = JsonSerializer.Serialize(authRequest);
|
||||
content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
response = await _httpClient.PostAsync($"http://{server}/RPC2_Login", content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
responseJson = await response.Content.ReadAsStringAsync();
|
||||
using JsonDocument authDoc = JsonDocument.Parse(responseJson);
|
||||
var authRoot = authDoc.RootElement;
|
||||
|
||||
if (!authRoot.TryGetProperty("result", out resultProp) || !resultProp.GetBoolean())
|
||||
throw new Exception("Ошибка авторизации");
|
||||
|
||||
return authRoot.GetProperty("session").GetString();
|
||||
}
|
||||
|
||||
// Получение состояния диска
|
||||
public async Task<string> CheckDiskStatusAsync(string server, int diskNumber, string user, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Получаем сессию
|
||||
string session = await GetSessionAsync(server, user, password);
|
||||
|
||||
// Запрос информации о дисках
|
||||
var diskRequest = new
|
||||
{
|
||||
method = "storage.getTankInfo",
|
||||
@params = (object)null,
|
||||
id = 270,
|
||||
session
|
||||
};
|
||||
|
||||
string json = JsonSerializer.Serialize(diskRequest);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _httpClient.PostAsync($"http://{server}/RPC2", content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var responseJson = await response.Content.ReadAsStringAsync();
|
||||
var listDisk = Newtonsoft.Json.JsonConvert.DeserializeObject<Disks>(responseJson).@params.tank[0].Device;
|
||||
if (diskNumber != 0)
|
||||
{
|
||||
// Парсим ответ для поиска диска
|
||||
string diskStatus = ParseDiskInfo(responseJson, diskNumber);
|
||||
return diskStatus ?? "NotFound";
|
||||
}
|
||||
else
|
||||
{
|
||||
int countDiskWithError = 0;
|
||||
for (int i = 0; i < listDisk.Count - 2; i++)
|
||||
{
|
||||
if (listDisk[i].PreDiskCheck != "Good" && listDisk[i].PreDiskCheck != "Warn")
|
||||
countDiskWithError++;
|
||||
}
|
||||
return countDiskWithError.ToString();
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new Exception($"Ошибка сети: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Ошибка: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Парсинг информации о дисках
|
||||
private string ParseDiskInfo(string jsonResponse, int diskNumber)
|
||||
{
|
||||
using JsonDocument doc = JsonDocument.Parse(jsonResponse);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Проверяем успешность запроса
|
||||
if (!root.TryGetProperty("result", out var resultProp) || !resultProp.GetBoolean())
|
||||
return null;
|
||||
|
||||
// Ищем диски
|
||||
if (!root.TryGetProperty("params", out var paramsProp))
|
||||
return null;
|
||||
|
||||
if (!paramsProp.TryGetProperty("tank", out var tanksProp))
|
||||
return null;
|
||||
|
||||
// Перебираем все массивы танков (хранилищ)
|
||||
foreach (var tank in tanksProp.EnumerateArray())
|
||||
{
|
||||
if (!tank.TryGetProperty("Device", out var devicesProp))
|
||||
continue;
|
||||
|
||||
// Перебираем устройства в танке
|
||||
foreach (var device in devicesProp.EnumerateArray())
|
||||
{
|
||||
// Если устройство - строка (как в вашем примере)
|
||||
if (device.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
string deviceStr = device.GetString();
|
||||
if (deviceStr.Contains($"PhysicNo={diskNumber}"))
|
||||
{
|
||||
// Ищем PreDiskCheck
|
||||
int start = deviceStr.IndexOf("PreDiskCheck=");
|
||||
if (start == -1) return "Unknown";
|
||||
|
||||
start += "PreDiskCheck=".Length;
|
||||
int end = deviceStr.IndexOf(';', start);
|
||||
if (end == -1) end = deviceStr.Length;
|
||||
|
||||
return deviceStr.Substring(start, end - start).Trim();
|
||||
}
|
||||
}
|
||||
// Если устройство - объект
|
||||
else if (device.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
// Пробуем получить PhysicNo как строку или число
|
||||
string physicNo = null;
|
||||
if (device.TryGetProperty("PhysicNo", out var physicNoProp))
|
||||
{
|
||||
if (physicNoProp.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
physicNo = physicNoProp.GetString();
|
||||
}
|
||||
else if (physicNoProp.ValueKind == JsonValueKind.Number)
|
||||
{
|
||||
physicNo = physicNoProp.GetInt32().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// Если нашли нужный диск
|
||||
if (physicNo == diskNumber.ToString())
|
||||
{
|
||||
// Получаем статус
|
||||
if (device.TryGetProperty("PreDiskCheck", out var statusProp))
|
||||
{
|
||||
return statusProp.GetString() ?? "Unknown";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Классы для десериализации JSON
|
||||
|
||||
public class DahuaResponse<T>
|
||||
{
|
||||
[JsonPropertyName("result")]
|
||||
public bool Result { get; set; }
|
||||
|
||||
[JsonPropertyName("session")]
|
||||
public string Session { get; set; }
|
||||
|
||||
[JsonPropertyName("params")]
|
||||
public T Params { get; set; }
|
||||
|
||||
[JsonPropertyName("error")]
|
||||
public ErrorInfo Error { get; set; }
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public class ChallengeData
|
||||
{
|
||||
[JsonPropertyName("authorization")]
|
||||
public string Authorization { get; set; }
|
||||
|
||||
[JsonPropertyName("random")]
|
||||
public string Random { get; set; }
|
||||
|
||||
[JsonPropertyName("realm")]
|
||||
public string Realm { get; set; }
|
||||
|
||||
[JsonPropertyName("opaque")]
|
||||
public string Opaque { get; set; }
|
||||
}
|
||||
|
||||
public class AuthData
|
||||
{
|
||||
[JsonPropertyName("keepAliveInterval")]
|
||||
public int KeepAliveInterval { get; set; }
|
||||
|
||||
[JsonPropertyName("isPwdOverdue")]
|
||||
public bool IsPwdOverdue { get; set; }
|
||||
}
|
||||
|
||||
public class TankInfoData
|
||||
{
|
||||
[JsonPropertyName("tank")]
|
||||
public List<Tank> Tanks { get; set; }
|
||||
}
|
||||
|
||||
public class Tank
|
||||
{
|
||||
[JsonPropertyName("Device")]
|
||||
public List<JsonElement> Devices { get; set; }
|
||||
|
||||
[JsonPropertyName("FreeSpace")]
|
||||
public double FreeSpace { get; set; }
|
||||
|
||||
[JsonPropertyName("Level")]
|
||||
public int Level { get; set; }
|
||||
|
||||
[JsonPropertyName("SlotNum")]
|
||||
public int SlotNum { get; set; }
|
||||
|
||||
[JsonPropertyName("TankNo")]
|
||||
public int TankNo { get; set; }
|
||||
|
||||
[JsonPropertyName("Temperature")]
|
||||
public double Temperature { get; set; }
|
||||
|
||||
[JsonPropertyName("TotalSpace")]
|
||||
public double TotalSpace { get; set; }
|
||||
}
|
||||
|
||||
public class Device
|
||||
{
|
||||
[JsonPropertyName("PhysicNo")]
|
||||
public string PhysicNo { get; set; }
|
||||
|
||||
[JsonPropertyName("PreDiskCheck")]
|
||||
public string PreDiskCheck { get; set; }
|
||||
|
||||
[JsonPropertyName("Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("SerialNo")]
|
||||
public string SerialNo { get; set; }
|
||||
|
||||
[JsonPropertyName("Temperature")]
|
||||
public string Temperature { get; set; }
|
||||
|
||||
[JsonPropertyName("State")]
|
||||
public string State { get; set; }
|
||||
}
|
||||
|
||||
public class ErrorInfo
|
||||
{
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; set; }
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user