203 lines
7.3 KiB
C#
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();
|
|
}
|
|
}
|
|
} |