feat: add power control
This commit is contained in:
parent
46621fdb40
commit
dc64a65702
|
@ -0,0 +1,46 @@
|
|||
using System.Collections;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace server.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 矩阵键控制器,用于管理矩阵键的启用、禁用和状态设置
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class PowerController : ControllerBase
|
||||
{
|
||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="address">[TODO:parameter]</param>
|
||||
/// <param name="port">[TODO:parameter]</param>
|
||||
/// <param name="enable">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
[HttpPost("SetPowerOnOff")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
|
||||
public async ValueTask<IResult> SetPowerOnOff(string address, int port, bool enable)
|
||||
{
|
||||
var powerCtrl = new Peripherals.PowerClient.Power(address, port);
|
||||
var ret = await powerCtrl.SetPowerOnOff(enable);
|
||||
|
||||
if (ret.IsSuccessful)
|
||||
{
|
||||
var powerStatus = enable ? "ON" : "OFF";
|
||||
logger.Info($"Set device {address}:{port.ToString()} power {powerStatus} finished: {ret.Value}.");
|
||||
return TypedResults.Ok(ret.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error(ret.Error);
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
using System.Net;
|
||||
using DotNext;
|
||||
|
||||
namespace Peripherals.PowerClient;
|
||||
|
||||
class PowerAddr
|
||||
{
|
||||
public const UInt32 Base = 0x10_00_00_00;
|
||||
public const UInt32 PowerCtrl = Base + 7;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
public class Power
|
||||
{
|
||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
readonly int timeout;
|
||||
|
||||
readonly int port;
|
||||
readonly string address;
|
||||
private IPEndPoint ep;
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="address">[TODO:parameter]</param>
|
||||
/// <param name="port">[TODO:parameter]</param>
|
||||
/// <param name="timeout">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public Power(string address, int port, int timeout = 1000)
|
||||
{
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="enable">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public async ValueTask<Result<bool>> SetPowerOnOff(bool enable)
|
||||
{
|
||||
if (MsgBus.IsRunning)
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address);
|
||||
else return new(new Exception("Message Bus not work!"));
|
||||
|
||||
var ret = await UDPClientPool.WriteAddr(this.ep, PowerAddr.PowerCtrl, Convert.ToUInt32(enable), this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
|
121
src/APIClient.ts
121
src/APIClient.ts
|
@ -1129,6 +1129,78 @@ export class MatrixKeyClient {
|
|||
}
|
||||
}
|
||||
|
||||
export class PowerClient {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
|
||||
this.http = http ? http : window as any;
|
||||
this.baseUrl = baseUrl ?? "http://localhost:5000";
|
||||
}
|
||||
|
||||
/**
|
||||
* [TODO:description]
|
||||
* @param address (optional) [TODO:parameter]
|
||||
* @param port (optional) [TODO:parameter]
|
||||
* @param enable (optional) [TODO:parameter]
|
||||
* @return [TODO:return]
|
||||
*/
|
||||
setPowerOnOff(address: string | undefined, port: number | undefined, enable: boolean | undefined): Promise<boolean> {
|
||||
let url_ = this.baseUrl + "/api/Power/SetPowerOnOff?";
|
||||
if (address === null)
|
||||
throw new Error("The parameter 'address' cannot be null.");
|
||||
else if (address !== undefined)
|
||||
url_ += "address=" + encodeURIComponent("" + address) + "&";
|
||||
if (port === null)
|
||||
throw new Error("The parameter 'port' cannot be null.");
|
||||
else if (port !== undefined)
|
||||
url_ += "port=" + encodeURIComponent("" + port) + "&";
|
||||
if (enable === null)
|
||||
throw new Error("The parameter 'enable' cannot be null.");
|
||||
else if (enable !== undefined)
|
||||
url_ += "enable=" + encodeURIComponent("" + enable) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processSetPowerOnOff(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processSetPowerOnOff(response: Response): Promise<boolean> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result200 = resultData200 !== undefined ? resultData200 : <any>null;
|
||||
|
||||
return result200;
|
||||
});
|
||||
} else if (status === 500) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result500: any = null;
|
||||
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result500 = Exception.fromJS(resultData500);
|
||||
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<boolean>(null as any);
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoteUpdateClient {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
|
@ -1781,6 +1853,55 @@ export class UDPClient {
|
|||
}
|
||||
}
|
||||
|
||||
export class TutorialClient {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
|
||||
this.http = http ? http : window as any;
|
||||
this.baseUrl = baseUrl ?? "http://localhost:5000";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有可用的教程目录
|
||||
* @return 教程目录列表
|
||||
*/
|
||||
getTutorials(): Promise<void> {
|
||||
let url_ = this.baseUrl + "/api/Tutorial";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processGetTutorials(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processGetTutorials(response: Response): Promise<void> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
return;
|
||||
});
|
||||
} else if (status === 500) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("A server side error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<void>(null as any);
|
||||
}
|
||||
}
|
||||
|
||||
export class Exception implements IException {
|
||||
message?: string;
|
||||
innerException?: Exception | undefined;
|
||||
|
|
|
@ -46,9 +46,16 @@
|
|||
</div>
|
||||
<div class="divider"></div>
|
||||
<h1 class="font-bold text-center text-2xl">外设</h1>
|
||||
<div class="flex flex-row">
|
||||
<input type="checkbox" class="checkbox" :checked="eqps.enableMatrixKey" @change="handleMatrixkeyCheckboxChange" />
|
||||
<p class="mx-2">启用矩阵键盘</p>
|
||||
<div class="flex flex-row justify-around">
|
||||
<div class="flex flex-row">
|
||||
<input type="checkbox" class="checkbox" :checked="eqps.enableMatrixKey"
|
||||
@change="handleMatrixkeyCheckboxChange" />
|
||||
<p class="mx-2">启用矩阵键盘</p>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
<input type="checkbox" class="checkbox" :checked="eqps.enablePower" @change="handlePowerCheckboxChange" />
|
||||
<p class="mx-2">启用电源</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -119,6 +126,16 @@ async function handleMatrixkeyCheckboxChange(event: Event) {
|
|||
}
|
||||
}
|
||||
|
||||
async function handlePowerCheckboxChange(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
const ret = await eqps.powerSetOnOff(target.checked);
|
||||
if (target.checked) {
|
||||
eqps.enablePower = ret;
|
||||
} else {
|
||||
eqps.enablePower = !ret;
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleJtagBoundaryScan() {
|
||||
if (eqps.jtagClientMutex.isLocked()) {
|
||||
dialog.warn("Jtag正在被占用");
|
||||
|
|
|
@ -4,7 +4,7 @@ import { isString, toNumber } from 'lodash';
|
|||
import { Common } from '@/Common';
|
||||
import z from "zod"
|
||||
import { isNumber } from 'mathjs';
|
||||
import { JtagClient, MatrixKeyClient } from "@/APIClient";
|
||||
import { JtagClient, MatrixKeyClient, PowerClient } from "@/APIClient";
|
||||
import { Mutex, withTimeout } from 'async-mutex';
|
||||
import { useConstraintsStore } from "@/stores/constraints";
|
||||
import { useDialogStore } from './dialog';
|
||||
|
@ -29,9 +29,14 @@ export const useEquipments = defineStore('equipments', () => {
|
|||
const matrixKeypadClientMutex = withTimeout(new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"));
|
||||
const matrixKeypadClient = new MatrixKeyClient();
|
||||
|
||||
// Power
|
||||
const powerClientMutex = withTimeout(new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"));
|
||||
const powerClient = new PowerClient();
|
||||
|
||||
// Enable Setting
|
||||
const enableJtagBoundaryScan = ref(false);
|
||||
const enableMatrixKey = ref(false);
|
||||
const enablePower = ref(false)
|
||||
|
||||
// Watch
|
||||
watchPostEffect(async () => {
|
||||
|
@ -212,6 +217,24 @@ export const useEquipments = defineStore('equipments', () => {
|
|||
}
|
||||
}
|
||||
|
||||
async function powerSetOnOff(enable: boolean) {
|
||||
const release = await powerClientMutex.acquire();
|
||||
try {
|
||||
const resp = await powerClient.setPowerOnOff(
|
||||
boardAddr.value,
|
||||
boardPort.value,
|
||||
enable
|
||||
);
|
||||
return resp;
|
||||
} catch (e) {
|
||||
dialog.error("无法开关电源");
|
||||
console.error(e);
|
||||
return false;
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
boardAddr,
|
||||
boardPort,
|
||||
|
@ -237,6 +260,12 @@ export const useEquipments = defineStore('equipments', () => {
|
|||
matrixKeypadClient,
|
||||
matrixKeypadEnable,
|
||||
matrixKeypadSetKeyStates,
|
||||
|
||||
// Power
|
||||
enablePower,
|
||||
powerClient,
|
||||
powerClientMutex,
|
||||
powerSetOnOff,
|
||||
}
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue