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 {
|
export class RemoteUpdateClient {
|
||||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||||
private baseUrl: string;
|
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 {
|
export class Exception implements IException {
|
||||||
message?: string;
|
message?: string;
|
||||||
innerException?: Exception | undefined;
|
innerException?: Exception | undefined;
|
||||||
|
|
|
@ -46,10 +46,17 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<h1 class="font-bold text-center text-2xl">外设</h1>
|
<h1 class="font-bold text-center text-2xl">外设</h1>
|
||||||
|
<div class="flex flex-row justify-around">
|
||||||
<div class="flex flex-row">
|
<div class="flex flex-row">
|
||||||
<input type="checkbox" class="checkbox" :checked="eqps.enableMatrixKey" @change="handleMatrixkeyCheckboxChange" />
|
<input type="checkbox" class="checkbox" :checked="eqps.enableMatrixKey"
|
||||||
|
@change="handleMatrixkeyCheckboxChange" />
|
||||||
<p class="mx-2">启用矩阵键盘</p>
|
<p class="mx-2">启用矩阵键盘</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<input type="checkbox" class="checkbox" :checked="eqps.enablePower" @change="handlePowerCheckboxChange" />
|
||||||
|
<p class="mx-2">启用电源</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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() {
|
async function toggleJtagBoundaryScan() {
|
||||||
if (eqps.jtagClientMutex.isLocked()) {
|
if (eqps.jtagClientMutex.isLocked()) {
|
||||||
dialog.warn("Jtag正在被占用");
|
dialog.warn("Jtag正在被占用");
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { isString, toNumber } from 'lodash';
|
||||||
import { Common } from '@/Common';
|
import { Common } from '@/Common';
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { isNumber } from 'mathjs';
|
import { isNumber } from 'mathjs';
|
||||||
import { JtagClient, MatrixKeyClient } from "@/APIClient";
|
import { JtagClient, MatrixKeyClient, PowerClient } from "@/APIClient";
|
||||||
import { Mutex, withTimeout } from 'async-mutex';
|
import { Mutex, withTimeout } from 'async-mutex';
|
||||||
import { useConstraintsStore } from "@/stores/constraints";
|
import { useConstraintsStore } from "@/stores/constraints";
|
||||||
import { useDialogStore } from './dialog';
|
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 matrixKeypadClientMutex = withTimeout(new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"));
|
||||||
const matrixKeypadClient = new MatrixKeyClient();
|
const matrixKeypadClient = new MatrixKeyClient();
|
||||||
|
|
||||||
|
// Power
|
||||||
|
const powerClientMutex = withTimeout(new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"));
|
||||||
|
const powerClient = new PowerClient();
|
||||||
|
|
||||||
// Enable Setting
|
// Enable Setting
|
||||||
const enableJtagBoundaryScan = ref(false);
|
const enableJtagBoundaryScan = ref(false);
|
||||||
const enableMatrixKey = ref(false);
|
const enableMatrixKey = ref(false);
|
||||||
|
const enablePower = ref(false)
|
||||||
|
|
||||||
// Watch
|
// Watch
|
||||||
watchPostEffect(async () => {
|
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 {
|
return {
|
||||||
boardAddr,
|
boardAddr,
|
||||||
boardPort,
|
boardPort,
|
||||||
|
@ -237,6 +260,12 @@ export const useEquipments = defineStore('equipments', () => {
|
||||||
matrixKeypadClient,
|
matrixKeypadClient,
|
||||||
matrixKeypadEnable,
|
matrixKeypadEnable,
|
||||||
matrixKeypadSetKeyStates,
|
matrixKeypadSetKeyStates,
|
||||||
|
|
||||||
|
// Power
|
||||||
|
enablePower,
|
||||||
|
powerClient,
|
||||||
|
powerClientMutex,
|
||||||
|
powerSetOnOff,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue