TKK_E32230371/UPSNet/UPSApiFetcher.cs

203 lines
7.3 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Threading;
namespace UPSNet
{
public class UPSApiFetcher : IDisposable
{
// Configuration
private string _apiUrl;
private string _apiUrlOnce;
private int _pollingInterval;
private int _pollingIntervalOnce;
// State tracking
private Thread _workerThread;
private bool _isRunning;
private DateTime _lastSuccessfulFetch;
private ManualResetEvent _sleepEvent = new ManualResetEvent(false);
private readonly object _syncLock = new object();
// API Response Data
public int Success { get; private set; }
public int ChargeLevel { get; private set; }
public int BattMaxLevel { get; private set; }
public bool OnState { get; private set; }
public int ShutdownSuggestMode { get; private set; }
public bool ShutdownSuggested { get; private set; }
public bool OnTimedShutdown { get; private set; }
public int TimedShutdownRemaining { get; private set; }
public bool BatteryCritical { get; private set; }
public int UPSLineFrom { get; private set; }
public bool IsChargingOrBlink { get; private set; }
public bool IsReachable { get; private set; }
public DateTime LastUpdateTime { get; private set; }
public bool IsOnRequestUrlOnce { get; private set; }
private string json;
// Event
public event Action<UPSApiFetcher> StatusUpdated;
public UPSApiFetcher(string apiUrl, int pollingIntervalMs = 1000) {
_apiUrl = apiUrl;
_pollingInterval = pollingIntervalMs;
}
public void SetApiUrl(string apiUrl, int pollingIntervalMs = 1000) {
lock (_syncLock) {
_apiUrl = apiUrl;
_pollingInterval = pollingIntervalMs;
}
}
public void SetApiUrlOnce(string apiUrl, int pollingIntervalMs = 500) {
lock (_syncLock){
_apiUrlOnce = apiUrl;
_pollingIntervalOnce = pollingIntervalMs;
IsOnRequestUrlOnce = true;
_sleepEvent.Set(); // Wake up thread for immediate fetch
}
}
public void Start() {
if (_isRunning) return;
_isRunning = true;
_workerThread = new Thread(WorkerLoop){
IsBackground = true,
Priority = ThreadPriority.Normal//ThreadPriority.BelowNormal
};
_workerThread.Start();
}
public void Stop() {
_isRunning = false;
_sleepEvent.Set(); // Wake thread to exit
_workerThread.Join(1000);
}
public void FetchNow() {
_sleepEvent.Set(); // Wake thread for immediate fetch
}
private void WorkerLoop() {
while (_isRunning) {
FetchApiData();
// Wait for next interval or immediate fetch
int interval;
lock (_syncLock) {
interval = IsOnRequestUrlOnce ? _pollingIntervalOnce : _pollingInterval;
}
_sleepEvent.WaitOne(interval);
_sleepEvent.Reset();
}
}
private void FetchApiData() {
try {
string currentUrl;
lock (_syncLock) {
currentUrl = IsOnRequestUrlOnce ? _apiUrlOnce : _apiUrl;
}
var request = WebRequest.Create(currentUrl);
request.Timeout = 5000;
// Add Basic Authentication for V2 compatibility
string credentials = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("admin:admin123"));
request.Headers[HttpRequestHeader.Authorization] = "Basic " + credentials;
using (var response = (HttpWebResponse)request.GetResponse())
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream)) {
string tmp = reader.ReadToEnd();
if (json!=tmp){
json = tmp;
ParseJsonResponse(json);
}
IsReachable = true;
_lastSuccessfulFetch = DateTime.Now;
LastUpdateTime = DateTime.Now;
lock (_syncLock) {
if (IsOnRequestUrlOnce) {
IsOnRequestUrlOnce = false;
}
}
}
}catch{
UpdateReachability();
}finally{
StatusUpdated.Invoke(this);
}
}
private void UpdateReachability(){
if ((DateTime.Now - _lastSuccessfulFetch).TotalSeconds > 5) {
IsReachable = false;
LastUpdateTime = DateTime.Now;
}
}
private void ParseJsonResponse(string json) {
try {
json = json.Trim('{', '}', ' ', '\n', '\r', '\t');
string[] pairs = json.Split(',');
foreach (string pair in pairs) {
string[] keyValue = pair.Split(':');
if (keyValue.Length != 2) continue;
string key = keyValue[0].Trim().Trim('"');
string value = keyValue[1].Trim().Trim('"');
switch (key.ToLower()){
case "success":
Success = int.Parse(value);
break;
case "chargelv":
ChargeLevel = int.Parse(value);
break;
case "shutdownsuggestmode":
ShutdownSuggestMode = int.Parse(value);
break;
case "shutdownsuggest":
ShutdownSuggested = int.Parse(value) > 0;
break;
case "timedshutdown":
OnTimedShutdown = int.Parse(value) > 0;
break;
case "shutdownremain":
TimedShutdownRemaining = int.Parse(value);
break;
case "battcritical":
BatteryCritical = int.Parse(value) > 0;
break;
case "line":
UPSLineFrom = int.Parse(value);
break;
case "battblink":
IsChargingOrBlink = int.Parse(value) > 0;
break;
case "state":
OnState = int.Parse(value) > 0;
break;
case "battmax":
BattMaxLevel = int.Parse(value);
break;
}
}
}catch{
// Keep previous values on parse error
}
}
public void Dispose(){
Stop();
_sleepEvent.Dispose();
}
}
}