diff --git a/server/src/Controllers/PowerController.cs b/server/src/Controllers/PowerController.cs new file mode 100644 index 0000000..b30e927 --- /dev/null +++ b/server/src/Controllers/PowerController.cs @@ -0,0 +1,46 @@ +using System.Collections; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; + +namespace server.Controllers; + +/// +/// 矩阵键控制器,用于管理矩阵键的启用、禁用和状态设置 +/// +[ApiController] +[Route("api/[controller]")] +public class PowerController : ControllerBase +{ + private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + [HttpPost("SetPowerOnOff")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] + public async ValueTask 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); + } + } + +} + diff --git a/server/src/Peripherals/PowerClient.cs b/server/src/Peripherals/PowerClient.cs new file mode 100644 index 0000000..95b9ca1 --- /dev/null +++ b/server/src/Peripherals/PowerClient.cs @@ -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; +} + +/// +/// [TODO:description] +/// +public class Power +{ + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + readonly int timeout; + + readonly int port; + readonly string address; + private IPEndPoint ep; + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + 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; + } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:return] + public async ValueTask> 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; + } +} + diff --git a/src/APIClient.ts b/src/APIClient.ts index 83383ad..44c8f70 100644 --- a/src/APIClient.ts +++ b/src/APIClient.ts @@ -1129,6 +1129,78 @@ export class MatrixKeyClient { } } +export class PowerClient { + private http: { fetch(url: RequestInfo, init?: RequestInit): Promise }; + private baseUrl: string; + protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined; + + constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) { + 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 { + 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 { + 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 : 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(null as any); + } +} + export class RemoteUpdateClient { private http: { fetch(url: RequestInfo, init?: RequestInit): Promise }; private baseUrl: string; @@ -1781,6 +1853,55 @@ export class UDPClient { } } +export class TutorialClient { + private http: { fetch(url: RequestInfo, init?: RequestInit): Promise }; + private baseUrl: string; + protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined; + + constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) { + this.http = http ? http : window as any; + this.baseUrl = baseUrl ?? "http://localhost:5000"; + } + + /** + * 获取所有可用的教程目录 + * @return 教程目录列表 + */ + getTutorials(): Promise { + 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 { + 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(null as any); + } +} + export class Exception implements IException { message?: string; innerException?: Exception | undefined; diff --git a/src/components/equipments/MotherBoardCaps.vue b/src/components/equipments/MotherBoardCaps.vue index 351422d..01170d7 100644 --- a/src/components/equipments/MotherBoardCaps.vue +++ b/src/components/equipments/MotherBoardCaps.vue @@ -46,9 +46,16 @@

外设

-
- -

启用矩阵键盘

+
+
+ +

启用矩阵键盘

+
+
+ +

启用电源

+
@@ -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正在被占用"); diff --git a/src/stores/equipments.ts b/src/stores/equipments.ts index e259b54..5d00ee6 100644 --- a/src/stores/equipments.ts +++ b/src/stores/equipments.ts @@ -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, } })