Fix OpenShockHttp: Wrong json caused Bad Request
Get OpenShock Shockers from API. Save Shockers for PiShock and OpenShock in different structs Implement Action Queue, to avoid synchronous actions getting lost. Moved SerialPortInfo to own file Created ShockerJsonConverter Better separation of Devices/APIs and Shockers
This commit is contained in:
77
CShocker/Devices/Abstract/Device.cs
Normal file
77
CShocker/Devices/Abstract/Device.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using CShocker.Devices.Additional;
|
||||
using CShocker.Ranges;
|
||||
using CShocker.Shockers.Abstract;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CShocker.Devices.Abstract;
|
||||
|
||||
public abstract class Device : IDisposable
|
||||
{
|
||||
// ReSharper disable 4 times MemberCanBePrivate.Global external use
|
||||
public readonly IntensityRange IntensityRange;
|
||||
public readonly DurationRange DurationRange;
|
||||
protected ILogger? Logger;
|
||||
public readonly DeviceApi ApiType;
|
||||
private readonly Queue<ValueTuple<ControlAction, IShocker, int, int>> _queue = new();
|
||||
private bool _workQueue = true;
|
||||
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
||||
private readonly Thread _workQueueThread;
|
||||
private const short CommandDelay = 50;
|
||||
|
||||
public void Control(ControlAction action, int? intensity = null, int? duration = null, params IShocker[] shockers)
|
||||
{
|
||||
int i = intensity ?? IntensityRange.GetRandomRangeValue();
|
||||
int d = duration ?? DurationRange.GetRandomRangeValue();
|
||||
if (action is ControlAction.Nothing)
|
||||
{
|
||||
this.Logger?.Log(LogLevel.Information, "Doing nothing");
|
||||
return;
|
||||
}
|
||||
foreach (IShocker shocker in shockers)
|
||||
{
|
||||
this.Logger?.Log(LogLevel.Debug, $"Enqueueing {action} {(intensity is not null ? $"Overwrite {i}" : $"{i}")} {(duration is not null ? $"Overwrite {d}" : $"{d}")}");
|
||||
_queue.Enqueue(new(action, shocker, i ,d));
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void ControlInternal(ControlAction action, IShocker shocker, int intensity, int duration);
|
||||
|
||||
protected Device(IntensityRange intensityRange, DurationRange durationRange, DeviceApi apiType, ILogger? logger = null)
|
||||
{
|
||||
Thread workQueueThread;
|
||||
this.IntensityRange = intensityRange;
|
||||
this.DurationRange = durationRange;
|
||||
this.ApiType = apiType;
|
||||
this.Logger = logger;
|
||||
this._workQueueThread = new Thread(QueueThread);
|
||||
this._workQueueThread.Start();
|
||||
}
|
||||
|
||||
private void QueueThread()
|
||||
{
|
||||
while (_workQueue)
|
||||
if (_queue.Count > 0 && _queue.Dequeue() is { } action)
|
||||
{
|
||||
this.Logger?.Log(LogLevel.Information, $"{action.Item1} {action.Item2} {action.Item3} {action.Item4}");
|
||||
ControlInternal(action.Item1, action.Item2, action.Item3, action.Item4);
|
||||
Thread.Sleep(action.Item4 + CommandDelay);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLogger(ILogger? logger)
|
||||
{
|
||||
this.Logger = logger;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"ShockerType: {Enum.GetName(typeof(DeviceApi), this.ApiType)}\n" +
|
||||
$"IntensityRange: {IntensityRange}\n" +
|
||||
$"DurationRange: {DurationRange}\n\r";
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_workQueue = false;
|
||||
}
|
||||
}
|
72
CShocker/Devices/Abstract/OpenShockDevice.cs
Normal file
72
CShocker/Devices/Abstract/OpenShockDevice.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System.Net.Http.Headers;
|
||||
using CShocker.Devices.Additional;
|
||||
using CShocker.Ranges;
|
||||
using CShocker.Shockers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace CShocker.Devices.Abstract;
|
||||
|
||||
public abstract class OpenShockDevice : Device
|
||||
{
|
||||
protected readonly HttpClient HttpClient = new();
|
||||
public string Endpoint { get; init; }
|
||||
public string ApiKey { get; init; }
|
||||
|
||||
public OpenShockDevice(IntensityRange intensityRange, DurationRange durationRange, DeviceApi apiType, string apiKey, string endpoint = "https://api.shocklink.net", ILogger? logger = null) : base(intensityRange, durationRange, apiType, logger)
|
||||
{
|
||||
this.Endpoint = endpoint;
|
||||
this.ApiKey = apiKey;
|
||||
}
|
||||
|
||||
public List<OpenShockShocker> GetShockers()
|
||||
{
|
||||
List<OpenShockShocker> shockers = new();
|
||||
|
||||
HttpClient httpClient = new();
|
||||
HttpRequestMessage requestOwnShockers = new (HttpMethod.Get, $"{Endpoint}/1/shockers/own")
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
UserAgent = { new ProductInfoHeaderValue("CShocker", "1") },
|
||||
Accept = { new MediaTypeWithQualityHeaderValue("application/json") }
|
||||
}
|
||||
};
|
||||
requestOwnShockers.Headers.Add("OpenShockToken", ApiKey);
|
||||
this.Logger?.Log(LogLevel.Debug, $"Requesting {requestOwnShockers.RequestUri}");
|
||||
HttpResponseMessage ownResponse = httpClient.Send(requestOwnShockers);
|
||||
this.Logger?.Log(!ownResponse.IsSuccessStatusCode ? LogLevel.Error : LogLevel.Debug,
|
||||
$"{requestOwnShockers.RequestUri} response: {ownResponse.StatusCode}");
|
||||
if (!ownResponse.IsSuccessStatusCode)
|
||||
return shockers;
|
||||
|
||||
StreamReader ownShockerStreamReader = new(ownResponse.Content.ReadAsStream());
|
||||
string ownShockerJson = ownShockerStreamReader.ReadToEnd();
|
||||
this.Logger?.Log(LogLevel.Debug,ownShockerJson);
|
||||
JObject ownShockerListJObj = JObject.Parse(ownShockerJson);
|
||||
shockers.AddRange(ownShockerListJObj.SelectTokens("$.data..shockers[*]").Select(t => t.ToObject<OpenShockShocker>()));
|
||||
|
||||
HttpRequestMessage requestSharedShockers = new (HttpMethod.Get, $"{Endpoint}/1/shockers/shared")
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
UserAgent = { new ProductInfoHeaderValue("CShocker", "1") },
|
||||
Accept = { new MediaTypeWithQualityHeaderValue("application/json") }
|
||||
}
|
||||
};
|
||||
requestSharedShockers.Headers.Add("OpenShockToken", ApiKey);
|
||||
this.Logger?.Log(LogLevel.Debug, $"Requesting {requestSharedShockers.RequestUri}");
|
||||
HttpResponseMessage sharedResponse = httpClient.Send(requestSharedShockers);
|
||||
this.Logger?.Log(!sharedResponse.IsSuccessStatusCode ? LogLevel.Error : LogLevel.Debug,
|
||||
$"{requestSharedShockers.RequestUri} response: {sharedResponse.StatusCode}");
|
||||
if (!sharedResponse.IsSuccessStatusCode)
|
||||
return shockers;
|
||||
|
||||
StreamReader sharedShockerStreamReader = new(sharedResponse.Content.ReadAsStream());
|
||||
string sharedShockerJson = sharedShockerStreamReader.ReadToEnd();
|
||||
this.Logger?.Log(LogLevel.Debug, sharedShockerJson);
|
||||
JObject sharedShockerListJObj = JObject.Parse(sharedShockerJson);
|
||||
shockers.AddRange(sharedShockerListJObj.SelectTokens("$.data..shockers[*]").Select(t => t.ToObject<OpenShockShocker>()));
|
||||
return shockers;
|
||||
}
|
||||
}
|
12
CShocker/Devices/Abstract/PiShockDevice.cs
Normal file
12
CShocker/Devices/Abstract/PiShockDevice.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using CShocker.Devices.Additional;
|
||||
using CShocker.Ranges;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CShocker.Devices.Abstract;
|
||||
|
||||
public abstract class PiShockDevice : Device
|
||||
{
|
||||
protected PiShockDevice(IntensityRange intensityRange, DurationRange durationRange, DeviceApi apiType, ILogger? logger = null) : base(intensityRange, durationRange, apiType, logger)
|
||||
{
|
||||
}
|
||||
}
|
20
CShocker/Devices/Abstract/SerialPortInfo.cs
Normal file
20
CShocker/Devices/Abstract/SerialPortInfo.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace CShocker.Devices.Abstract;
|
||||
|
||||
public struct SerialPortInfo
|
||||
{
|
||||
public readonly string? PortName, Description, Manufacturer, DeviceID;
|
||||
|
||||
public SerialPortInfo(string? portName, string? description, string? manufacturer, string? deviceID)
|
||||
{
|
||||
this.PortName = portName;
|
||||
this.Description = description;
|
||||
this.Manufacturer = manufacturer;
|
||||
this.DeviceID = deviceID;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
$"{GetType().Name}\nPortName: {PortName}\nDescription: {Description}\nManufacturer: {Manufacturer}\nDeviceID: {DeviceID}\n\r";
|
||||
}
|
||||
}
|
9
CShocker/Devices/Additional/ControlActionEnum.cs
Normal file
9
CShocker/Devices/Additional/ControlActionEnum.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace CShocker.Devices.Additional;
|
||||
|
||||
public enum ControlAction
|
||||
{
|
||||
Beep,
|
||||
Vibrate,
|
||||
Shock,
|
||||
Nothing
|
||||
}
|
9
CShocker/Devices/Additional/DeviceApiEnum.cs
Normal file
9
CShocker/Devices/Additional/DeviceApiEnum.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace CShocker.Devices.Additional;
|
||||
|
||||
public enum DeviceApi : byte
|
||||
{
|
||||
OpenShockHttp = 0,
|
||||
OpenShockSerial = 1,
|
||||
PiShockHttp = 2,
|
||||
PiShockSerial = 3
|
||||
}
|
61
CShocker/Devices/Additional/DeviceJsonConverter.cs
Normal file
61
CShocker/Devices/Additional/DeviceJsonConverter.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using CShocker.Devices.Abstract;
|
||||
using CShocker.Ranges;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace CShocker.Devices.Additional;
|
||||
|
||||
public class DeviceJsonConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return (objectType == typeof(Device));
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||
{
|
||||
JObject jo = JObject.Load(reader);
|
||||
DeviceApi? apiType = (DeviceApi?)jo.SelectToken("ApiType")?.Value<byte>();
|
||||
|
||||
switch (apiType)
|
||||
{
|
||||
case DeviceApi.OpenShockHttp:
|
||||
return new OpenShockHttp(
|
||||
jo.SelectToken("IntensityRange")!.ToObject<IntensityRange>()!,
|
||||
jo.SelectToken("DurationRange")!.ToObject<DurationRange>()!,
|
||||
jo.SelectToken("ApiKey")!.Value<string>()!,
|
||||
jo.SelectToken("Endpoint")!.Value<string>()!
|
||||
);
|
||||
case DeviceApi.OpenShockSerial:
|
||||
return new OpenShockSerial(
|
||||
jo.SelectToken("IntensityRange")!.ToObject<IntensityRange>()!,
|
||||
jo.SelectToken("DurationRange")!.ToObject<DurationRange>()!,
|
||||
jo.SelectToken("SerialPortI")!.ToObject<SerialPortInfo>()!,
|
||||
jo.SelectToken("ApiKey")!.Value<string>()!,
|
||||
jo.SelectToken("Endpoint")!.Value<string>()!
|
||||
);
|
||||
case DeviceApi.PiShockHttp:
|
||||
return new PiShockHttp(
|
||||
jo.SelectToken("IntensityRange")!.ToObject<IntensityRange>()!,
|
||||
jo.SelectToken("DurationRange")!.ToObject<DurationRange>()!,
|
||||
jo.SelectToken("ApiKey")!.Value<string>()!,
|
||||
jo.SelectToken("Username")!.Value<string>()!,
|
||||
jo.SelectToken("Endpoint")!.Value<string>()!
|
||||
);
|
||||
case DeviceApi.PiShockSerial:
|
||||
throw new NotImplementedException();
|
||||
default:
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
/// <summary>
|
||||
/// Don't call this
|
||||
/// </summary>
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
throw new Exception("Dont call this");
|
||||
}
|
||||
}
|
47
CShocker/Devices/Additional/SerialHelper.cs
Normal file
47
CShocker/Devices/Additional/SerialHelper.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Management;
|
||||
using System.Runtime.Versioning;
|
||||
using CShocker.Devices.Abstract;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace CShocker.Devices.Additional;
|
||||
|
||||
public static class SerialHelper
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static List<SerialPortInfo> GetSerialPorts()
|
||||
{
|
||||
List<SerialPortInfo> ret = new();
|
||||
using (ManagementClass entity = new("Win32_PnPEntity"))
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
const string CUR_CTRL = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\";
|
||||
|
||||
foreach (ManagementObject instance in entity.GetInstances())
|
||||
{
|
||||
object oGuid;
|
||||
oGuid = instance.GetPropertyValue("ClassGuid");
|
||||
if (oGuid == null || oGuid.ToString()?.ToUpper().Equals("{4D36E978-E325-11CE-BFC1-08002BE10318}") is false)
|
||||
continue; // Skip all devices except device class "PORTS"
|
||||
|
||||
string? caption = instance.GetPropertyValue("Caption")?.ToString();
|
||||
string? manufacturer = instance.GetPropertyValue("Manufacturer")?.ToString();
|
||||
string? deviceID = instance.GetPropertyValue("PnpDeviceID")?.ToString();
|
||||
string regEnum = CUR_CTRL + "Enum\\" + deviceID + "\\Device Parameters";
|
||||
string? portName = Registry.GetValue(regEnum, "PortName", "")?.ToString();
|
||||
|
||||
int? s32Pos = caption?.IndexOf(" (COM");
|
||||
if (s32Pos > 0) // remove COM port from description
|
||||
caption = caption?.Substring(0, (int)s32Pos);
|
||||
|
||||
ret.Add(new SerialPortInfo(
|
||||
portName,
|
||||
caption,
|
||||
manufacturer,
|
||||
deviceID));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
64
CShocker/Devices/OpenShockHttp.cs
Normal file
64
CShocker/Devices/OpenShockHttp.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using CShocker.Devices.Abstract;
|
||||
using CShocker.Devices.Additional;
|
||||
using CShocker.Ranges;
|
||||
using CShocker.Shockers;
|
||||
using CShocker.Shockers.Abstract;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CShocker.Devices;
|
||||
|
||||
public class OpenShockHttp : OpenShockDevice
|
||||
{
|
||||
|
||||
|
||||
protected override void ControlInternal(ControlAction action, IShocker shocker, int intensity, int duration)
|
||||
{
|
||||
if (shocker is not OpenShockShocker openShockShocker)
|
||||
{
|
||||
this.Logger?.Log(LogLevel.Warning, $"Shocker {shocker} is not {typeof(OpenShockShocker).FullName}");
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequestMessage request = new (HttpMethod.Post, $"{Endpoint}/2/shockers/control")
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
UserAgent = { new ProductInfoHeaderValue("CShocker", "1") },
|
||||
Accept = { new MediaTypeWithQualityHeaderValue("application/json") }
|
||||
},
|
||||
Content = new StringContent("{" +
|
||||
" \"shocks\": [" +
|
||||
" {" +
|
||||
$" \"id\": \"{openShockShocker.id}\"," +
|
||||
$" \"type\": {ControlActionToByte(action)}," +
|
||||
$" \"intensity\": {intensity}," +
|
||||
$" \"duration\": {duration}" +
|
||||
" }" +
|
||||
" ]," +
|
||||
" \"customName\": \"CShocker\"" +
|
||||
"}", Encoding.UTF8, new MediaTypeHeaderValue("application/json"))
|
||||
};
|
||||
request.Headers.Add("OpenShockToken", ApiKey);
|
||||
this.Logger?.Log(LogLevel.Debug, $"Request-Content: {request.Content.ReadAsStringAsync().Result}");
|
||||
HttpResponseMessage response = HttpClient.Send(request);
|
||||
this.Logger?.Log(!response.IsSuccessStatusCode ? LogLevel.Error : LogLevel.Debug,
|
||||
$"{request.RequestUri} response: {response.StatusCode}");
|
||||
}
|
||||
|
||||
private byte ControlActionToByte(ControlAction action)
|
||||
{
|
||||
return action switch
|
||||
{
|
||||
ControlAction.Beep => 3,
|
||||
ControlAction.Vibrate => 2,
|
||||
ControlAction.Shock => 1,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
public OpenShockHttp(IntensityRange intensityRange, DurationRange durationRange, string apiKey, string endpoint = "https://api.shocklink.net", ILogger? logger = null) : base(intensityRange, durationRange, DeviceApi.OpenShockHttp, apiKey, endpoint, logger)
|
||||
{
|
||||
}
|
||||
}
|
51
CShocker/Devices/OpenShockSerial.cs
Normal file
51
CShocker/Devices/OpenShockSerial.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System.IO.Ports;
|
||||
using CShocker.Devices.Abstract;
|
||||
using CShocker.Devices.Additional;
|
||||
using CShocker.Ranges;
|
||||
using CShocker.Shockers;
|
||||
using CShocker.Shockers.Abstract;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CShocker.Devices;
|
||||
|
||||
public class OpenShockSerial : OpenShockDevice
|
||||
{
|
||||
private const int BaudRate = 115200;
|
||||
public SerialPortInfo SerialPortI;
|
||||
private readonly SerialPort _serialPort;
|
||||
|
||||
public OpenShockSerial(IntensityRange intensityRange, DurationRange durationRange, SerialPortInfo serialPortI, string apiKey, string endpoint = "https://api.shocklink.net", ILogger? logger = null) : base(intensityRange, durationRange, DeviceApi.OpenShockSerial, apiKey, endpoint, logger)
|
||||
{
|
||||
this.SerialPortI = serialPortI;
|
||||
this._serialPort = new SerialPort(serialPortI.PortName, BaudRate);
|
||||
this._serialPort.Open();
|
||||
}
|
||||
|
||||
protected override void ControlInternal(ControlAction action, IShocker shocker, int intensity, int duration)
|
||||
{
|
||||
if (shocker is not OpenShockShocker openShockShocker)
|
||||
{
|
||||
this.Logger?.Log(LogLevel.Warning, $"Shocker {shocker} is not {typeof(OpenShockShocker).FullName}");
|
||||
return;
|
||||
}
|
||||
string json = "rftransmit {" +
|
||||
$"\"model\":\"{Enum.GetName(openShockShocker.model)!.ToLower()}\"," +
|
||||
$"\"id\":{openShockShocker.rfId}," +
|
||||
$"\"type\":\"{ControlActionToString(action)}\"," +
|
||||
$"\"intensity\":{intensity}," +
|
||||
$"\"durationMs\":{duration}" +
|
||||
"}";
|
||||
_serialPort.WriteLine(json);
|
||||
}
|
||||
|
||||
private static string ControlActionToString(ControlAction action)
|
||||
{
|
||||
return action switch
|
||||
{
|
||||
ControlAction.Beep => "sound",
|
||||
ControlAction.Vibrate => "vibrate",
|
||||
ControlAction.Shock => "shock",
|
||||
_ => "stop"
|
||||
};
|
||||
}
|
||||
}
|
65
CShocker/Devices/PiShockHttp.cs
Normal file
65
CShocker/Devices/PiShockHttp.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using CShocker.Devices.Abstract;
|
||||
using CShocker.Devices.Additional;
|
||||
using CShocker.Ranges;
|
||||
using CShocker.Shockers;
|
||||
using CShocker.Shockers.Abstract;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CShocker.Devices;
|
||||
|
||||
public class PiShockHttp : PiShockDevice
|
||||
{
|
||||
// ReSharper disable twice MemberCanBePrivate.Global external usage
|
||||
public string Username, Endpoint, ApiKey;
|
||||
protected readonly HttpClient HttpClient = new();
|
||||
|
||||
public PiShockHttp(IntensityRange intensityRange, DurationRange durationRange, string apiKey, string username, string endpoint = "https://do.pishock.com/api/apioperate", ILogger? logger = null) : base(intensityRange, durationRange, DeviceApi.PiShockHttp, logger)
|
||||
{
|
||||
this.Username = username;
|
||||
this.Endpoint = endpoint;
|
||||
this.ApiKey = apiKey;
|
||||
}
|
||||
|
||||
protected override void ControlInternal(ControlAction action, IShocker shocker, int intensity, int duration)
|
||||
{
|
||||
if (shocker is not PiShockShocker piShockShocker)
|
||||
{
|
||||
this.Logger?.Log(LogLevel.Warning, $"Shocker {shocker} is not {typeof(OpenShockShocker).FullName}");
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequestMessage request = new (HttpMethod.Post, $"{Endpoint}")
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
UserAgent = { new ProductInfoHeaderValue("CShocker", "1") },
|
||||
Accept = { new MediaTypeWithQualityHeaderValue("text/plain") }
|
||||
},
|
||||
Content = new StringContent("{" +
|
||||
$"\"Username\":\"{Username}\"," +
|
||||
"\"Name\":\"CShocker\"," +
|
||||
$"\"Code\":\"{piShockShocker.Code}\"," +
|
||||
$"\"Intensity\":\"{intensity}\"," +
|
||||
$"\"Duration\":\"{duration/1000}\"," + //duration is in seconds no ms
|
||||
$"\"Apikey\":\"{ApiKey}\"," +
|
||||
$"\"Op\":\"{ControlActionToByte(action)}\"" +
|
||||
"}", Encoding.UTF8, new MediaTypeHeaderValue("application/json"))
|
||||
};
|
||||
HttpResponseMessage response = HttpClient.Send(request);
|
||||
this.Logger?.Log(!response.IsSuccessStatusCode ? LogLevel.Error : LogLevel.Debug,
|
||||
$"{request.RequestUri} response: {response.StatusCode}");
|
||||
}
|
||||
|
||||
private byte ControlActionToByte(ControlAction action)
|
||||
{
|
||||
return action switch
|
||||
{
|
||||
ControlAction.Beep => 2,
|
||||
ControlAction.Vibrate => 1,
|
||||
ControlAction.Shock => 0,
|
||||
_ => 2
|
||||
};
|
||||
}
|
||||
}
|
47
CShocker/Devices/PiShockSerial.cs
Normal file
47
CShocker/Devices/PiShockSerial.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.IO.Ports;
|
||||
using CShocker.Devices.Abstract;
|
||||
using CShocker.Devices.Additional;
|
||||
using CShocker.Ranges;
|
||||
using CShocker.Shockers.Abstract;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CShocker.Devices;
|
||||
|
||||
public class PiShockSerial : PiShockDevice
|
||||
{
|
||||
private const int BaudRate = 115200;
|
||||
public SerialPortInfo SerialPortI;
|
||||
private readonly SerialPort _serialPort;
|
||||
|
||||
public PiShockSerial(IntensityRange intensityRange, DurationRange durationRange, DeviceApi apiType, SerialPortInfo serialPortI, ILogger? logger = null) : base(intensityRange, durationRange, apiType, logger)
|
||||
{
|
||||
this.SerialPortI = serialPortI;
|
||||
this._serialPort = new SerialPort(this.SerialPortI.PortName, BaudRate);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ControlInternal(ControlAction action, IShocker shocker, int intensity, int duration)
|
||||
{
|
||||
string json = "{" +
|
||||
"\"cmd\": \"operate\"," +
|
||||
"\"value\":{" +
|
||||
$"\"op\": \"{ControlActionToOp(action)}\"," +
|
||||
$"\"duration\": {duration}," +
|
||||
$"\"intensity\": {intensity}," +
|
||||
$"\"id\": " +
|
||||
"}" +
|
||||
"}";
|
||||
_serialPort.WriteLine(json);
|
||||
}
|
||||
|
||||
private static string ControlActionToOp(ControlAction action)
|
||||
{
|
||||
return action switch
|
||||
{
|
||||
ControlAction.Beep => "",
|
||||
ControlAction.Vibrate => "vibrate",
|
||||
ControlAction.Shock => "",
|
||||
_ => ""
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user