using System; using System.Collections; using System.Collections.Generic; using System.Configuration; using System.IO; using System.Net; using System.Text; using System.Web.Script.Serialization; namespace HanvonF710XAttendanceService { /// /// F710X / AI Biometric web API client (POST http://{ip}/api). /// Auth: each request includes the device web UI password. /// internal sealed class HanvonHttpApiClient { private readonly string _baseUrl; private readonly string _password; private readonly int _timeoutMs; private readonly JavaScriptSerializer _json = new JavaScriptSerializer { MaxJsonLength = int.MaxValue }; public HanvonHttpApiClient(string machineIp, int httpPort = 80, string password = null, int timeoutMs = 60000) { if (string.IsNullOrWhiteSpace(machineIp)) throw new ArgumentException("machineIp required"); int port = httpPort > 0 ? httpPort : 80; _baseUrl = "http://" + machineIp.Trim() + (port == 80 ? "" : ":" + port) + "/api"; _password = password ?? MachineScope.GetDevicePassword() ?? ""; _timeoutMs = timeoutMs > 0 ? timeoutMs : 60000; } public static HanvonHttpApiClient ForMachine(AttendanceMachine machine) { int httpPort = GetIntSetting("DEVICE_HTTP_PORT", 80); return new HanvonHttpApiClient(machine.MachineIp, httpPort, MachineScope.GetDevicePassword()); } public bool TryLogin(out string error) { error = null; try { var resp = Post(new Dictionary { { "cmd", "login" }, { "username", MachineScope.GetDeviceUsername() }, { "password", _password }, { "rememberMe", false } }); if (IsSuccess(resp)) return true; error = GetMsg(resp) ?? "login failed"; return false; } catch (Exception ex) { error = ex.Message; return false; } } public List GetLogs(DateTime? from, DateTime? to, out string error) { error = null; var all = new List(); try { int index = 0; for (int guard = 0; guard < 10000; guard++) { var req = new Dictionary { { "cmd", "getlog" }, { "password", _password }, { "index", index } }; if (from.HasValue) req["from"] = from.Value.ToString("yyyy-MM-dd HH:mm:ss"); if (to.HasValue) req["to"] = to.Value.ToString("yyyy-MM-dd HH:mm:ss"); var resp = Post(req); if (!IsSuccess(resp)) { error = GetMsg(resp) ?? "getlog failed"; return all; } var batch = ParseLogRecords(resp); all.AddRange(batch); int count = GetInt(resp, "count"); int toIdx = GetInt(resp, "to"); if (count <= 0 || toIdx <= 0 || count <= toIdx || batch.Count == 0) { break; } index = toIdx + 1; } return all; } catch (Exception ex) { error = ex.Message; return all; } } public List GetUserList(out string error) { error = null; var all = new List(); try { int stn = 1; for (int guard = 0; guard < 10000; guard++) { var resp = Post(new Dictionary { { "cmd", "getuserlist" }, { "password", _password }, { "stn", stn } }); if (!IsSuccess(resp)) { error = GetMsg(resp) ?? "getuserlist failed"; return all; } var batch = ParseUserRecords(resp); all.AddRange(batch); int count = GetInt(resp, "count"); int toIdx = GetInt(resp, "to"); if (count <= 0 || batch.Count == 0 || (toIdx > 0 && toIdx >= count)) { break; } stn = 0; } return all; } catch (Exception ex) { error = ex.Message; return all; } } public bool DeleteUsers(IEnumerable enrollIds, out string error) { error = null; try { var list = new List(); foreach (var id in enrollIds) { if (string.IsNullOrWhiteSpace(id)) continue; if (int.TryParse(id.Trim(), out int n)) list.Add(n); else list.Add(id.Trim()); } if (list.Count == 0) return true; var resp = Post(new Dictionary { { "cmd", "deleteusers" }, { "password", _password }, { "list", list } }); if (IsSuccess(resp)) return true; error = GetMsg(resp) ?? "deleteusers failed"; return false; } catch (Exception ex) { error = ex.Message; return false; } } public bool CleanLog(out string error) { error = null; try { var resp = Post(new Dictionary { { "cmd", "cleanlog" }, { "password", _password } }); if (IsSuccess(resp)) return true; error = GetMsg(resp) ?? "cleanlog failed"; return false; } catch (Exception ex) { error = ex.Message; return false; } } public bool TryGetFaceRecord(int enrollId, out string recordBase64, out string name, out string error) { recordBase64 = null; name = null; error = null; try { var resp = Post(new Dictionary { { "cmd", "getuserinfo" }, { "password", _password }, { "enrollid", enrollId }, { "backupnum", GetIntSetting("DEVICE_FACE_BACKUPNUM", 50) } }); if (!IsSuccess(resp)) { error = GetMsg(resp) ?? "getuserinfo failed"; return false; } name = GetString(resp, "name"); recordBase64 = GetString(resp, "record"); if (string.IsNullOrWhiteSpace(recordBase64)) { error = "no face/record payload"; return false; } return true; } catch (Exception ex) { error = ex.Message; return false; } } private Dictionary Post(Dictionary payload) { string body = _json.Serialize(payload); var req = (HttpWebRequest)WebRequest.Create(_baseUrl); req.Method = "POST"; req.ContentType = "application/json; charset=utf-8"; req.Timeout = _timeoutMs; req.ReadWriteTimeout = _timeoutMs; req.KeepAlive = false; byte[] bytes = Encoding.UTF8.GetBytes(body); req.ContentLength = bytes.Length; using (var stream = req.GetRequestStream()) { stream.Write(bytes, 0, bytes.Length); } using (var resp = (HttpWebResponse)req.GetResponse()) using (var reader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8)) { string text = reader.ReadToEnd(); if (string.IsNullOrWhiteSpace(text)) { return new Dictionary(); } return _json.Deserialize>(text) ?? new Dictionary(); } } private static bool IsSuccess(Dictionary resp) { if (resp == null) return false; if (!resp.TryGetValue("result", out object r) || r == null) return false; if (r is bool b) return b; return string.Equals(r.ToString(), "true", StringComparison.OrdinalIgnoreCase); } private static string GetMsg(Dictionary resp) { return GetString(resp, "msg") ?? GetString(resp, "reason"); } private static string GetString(Dictionary resp, string key) { if (resp == null || !resp.TryGetValue(key, out object v) || v == null) return null; return v.ToString(); } private static int GetInt(Dictionary resp, string key) { if (resp == null || !resp.TryGetValue(key, out object v) || v == null) return 0; if (v is int i) return i; if (v is long l) return (int)l; if (v is decimal d) return (int)d; int.TryParse(v.ToString(), out int n); return n; } private static List ParseLogRecords(Dictionary resp) { var list = new List(); if (resp == null || !resp.TryGetValue("record", out object rec) || rec == null) return list; if (rec is ArrayList arr) { foreach (var item in arr) AddLog(list, item as Dictionary); } else if (rec is object[] oa) { foreach (var item in oa) AddLog(list, item as Dictionary); } return list; } private static void AddLog(List list, Dictionary row) { if (row == null) return; string enroll = GetString(row, "enrollid") ?? ""; string name = GetString(row, "name") ?? ""; string timeStr = GetString(row, "time") ?? ""; if (!DateTime.TryParse(timeStr, out DateTime time)) return; string acNo = !string.IsNullOrWhiteSpace(name) ? name.Trim() : enroll.Trim(); if (string.IsNullOrWhiteSpace(acNo)) return; list.Add(new HttpLogRecord { EnrollId = enroll, Name = name, AcNo = acNo, CheckTime = time, Mode = GetInt(row, "mode"), InOut = GetInt(row, "inout"), Event = GetInt(row, "event") }); } private static List ParseUserRecords(Dictionary resp) { var list = new List(); if (resp == null || !resp.TryGetValue("record", out object rec) || rec == null) return list; IEnumerable items = null; if (rec is ArrayList arr) items = arr; else if (rec is object[] oa) items = oa; if (items == null) return list; foreach (var item in items) { var row = item as Dictionary; if (row == null) continue; string id = GetString(row, "id") ?? ""; string name = GetString(row, "name") ?? ""; string serial = !string.IsNullOrWhiteSpace(name) ? name.Trim() : id.Trim(); if (string.IsNullOrWhiteSpace(serial)) continue; list.Add(new HttpUserRecord { DeviceUserId = id.Trim(), Name = name.Trim(), SerialNumber = serial }); } return list; } private static int GetIntSetting(string key, int defaultValue) { try { var raw = ConfigurationManager.AppSettings[key]; if (string.IsNullOrWhiteSpace(raw)) return defaultValue; return int.TryParse(raw.Trim(), out var v) && v > 0 ? v : defaultValue; } catch { return defaultValue; } } } internal sealed class HttpLogRecord { public string EnrollId { get; set; } public string Name { get; set; } public string AcNo { get; set; } public DateTime CheckTime { get; set; } public int Mode { get; set; } public int InOut { get; set; } public int Event { get; set; } } internal sealed class HttpUserRecord { public string DeviceUserId { get; set; } public string Name { get; set; } public string SerialNumber { get; set; } } internal static class DeviceProtocol { public static bool UseHttp() { var p = ConfigurationManager.AppSettings["DEVICE_PROTOCOL"] ?? "HTTP"; return string.Equals(p.Trim(), "HTTP", StringComparison.OrdinalIgnoreCase) || string.Equals(p.Trim(), "WEB", StringComparison.OrdinalIgnoreCase) || string.Equals(p.Trim(), "API", StringComparison.OrdinalIgnoreCase); } } }