diff --git a/server/src/Controllers/SwitchController.cs b/server/src/Controllers/SwitchController.cs new file mode 100644 index 0000000..76b98ef --- /dev/null +++ b/server/src/Controllers/SwitchController.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Cors; +using Peripherals.SwitchClient; + +namespace server.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class SwitchController : ControllerBase +{ + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + private readonly Database.UserManager _userManager = new(); + + /// + /// 获取示波器实例 + /// + private SwitchCtrl? GetSwitchCtrl() + { + var userName = User.Identity?.Name; + if (string.IsNullOrEmpty(userName)) + return null; + + var userRet = _userManager.GetUserByName(userName); + if (!userRet.IsSuccessful || !userRet.Value.HasValue) + return null; + + var user = userRet.Value.Value; + if (user.BoardID == Guid.Empty) + return null; + + var boardRet = _userManager.GetBoardByID(user.BoardID); + if (!boardRet.IsSuccessful || !boardRet.Value.HasValue) + return null; + + var board = boardRet.Value.Value; + return new SwitchCtrl(board.IpAddr, board.Port, 0); + } + + /// + /// 启用或禁用 Switch 外设 + /// + /// 是否启用 + /// 操作结果 + [HttpPost("enable")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task SetEnable([FromQuery] bool enable) + { + var switchCtrl = GetSwitchCtrl(); + if (switchCtrl == null) + return BadRequest("Can't get user or board info"); + + var result = await switchCtrl.SetEnable(enable); + if (!result.IsSuccessful) + { + logger.Error(result.Error, "SetEnable failed"); + return StatusCode(500, result.Error); + } + return Ok(result.Value); + } + + /// + /// 控制指定编号的 Switch 开关 + /// + /// 开关编号 + /// 开/关 + /// 操作结果 + [HttpPost("switch")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task SetSwitchOnOff([FromQuery] int num, [FromQuery] bool onOff) + { + if (num <= 0 || num > 6) + return BadRequest(new ArgumentException($"Switch num should be 1~5, instead of {num}")); + + var switchCtrl = GetSwitchCtrl(); + if (switchCtrl == null) + return BadRequest("Can't get user or board info"); + + var result = await switchCtrl.SetSwitchOnOff(num, onOff); + if (!result.IsSuccessful) + { + logger.Error(result.Error, $"SetSwitchOnOff({num}, {onOff}) failed"); + return StatusCode(500, result.Error); + } + return Ok(result.Value); + } + + /// + /// 控制 Switch 开关 + /// + /// 开关状态 + /// 操作结果 + [HttpPost("MultiSwitch")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task SetMultiSwitchsOnOff(bool[] keyStatus) + { + if (keyStatus.Length == 0 || keyStatus.Length > 6) return BadRequest( + new ArgumentException($"Switch num should be 1~5, instead of {keyStatus.Length}")); + + var switchCtrl = GetSwitchCtrl(); + if (switchCtrl == null) + return BadRequest("Can't get user or board info"); + + for (int i = 0; i < keyStatus.Length; i++) + { + var result = await switchCtrl.SetSwitchOnOff(i, keyStatus[i]); + if (!result.IsSuccessful) + { + logger.Error(result.Error, $"SetSwitchOnOff({i}, {keyStatus[i]}) failed"); + return StatusCode(500, result.Error); + } + if (!result.Value) return Ok(false); + } + return Ok(true); + } +} diff --git a/server/src/Peripherals/SwitchClient.cs b/server/src/Peripherals/SwitchClient.cs new file mode 100644 index 0000000..45a974f --- /dev/null +++ b/server/src/Peripherals/SwitchClient.cs @@ -0,0 +1,65 @@ +using System.Collections; +using System.Net; +using DotNext; + +namespace Peripherals.SwitchClient; + +class SwitchCtrlAddr +{ + public const UInt32 BASE = 0xB0_00_00_20; + + public const UInt32 ENABLE = BASE; +} + +/// +/// 矩阵键盘外设类,用于控制和管理矩阵键盘的功能。 +/// +public class SwitchCtrl +{ + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + readonly int timeout = 500; + readonly int taskID; + readonly int port; + readonly string address; + private IPEndPoint ep; + + public SwitchCtrl(string address, int port, int taskID, int timeout = 500) + { + if (timeout < 0) + throw new ArgumentException("Timeout couldn't be negative", nameof(timeout)); + this.address = address; + this.port = port; + this.ep = new IPEndPoint(IPAddress.Parse(address), port); + this.taskID = taskID; + this.timeout = timeout; + } + + public async ValueTask> SetEnable(bool enable) + { + if (MsgBus.IsRunning) + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + else return new(new Exception("Message Bus not work!")); + + var ret = await UDPClientPool.WriteAddr( + this.ep, this.taskID, SwitchCtrlAddr.ENABLE, enable ? 0x1U : 0x0U, this.timeout); + if (!ret.IsSuccessful) return new(ret.Error); + return ret.Value; + } + + public async ValueTask> SetSwitchOnOff(int num, bool onOff) + { + if (MsgBus.IsRunning) + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + else return new(new Exception("Message Bus not work!")); + + var ret = await UDPClientPool.WriteAddr( + this.ep, this.taskID, SwitchCtrlAddr.BASE + (UInt32)num, onOff ? 0x1U : 0x0U, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Set Switch {onOff} failed: {ret.Error}"); + return new(ret.Error); + } + return ret.Value; + } +} diff --git a/src/APIClient.ts b/src/APIClient.ts index 8f3a32e..86468c1 100644 --- a/src/APIClient.ts +++ b/src/APIClient.ts @@ -6936,6 +6936,255 @@ export class ResourceClient { } } +export class SwitchClient { + protected instance: AxiosInstance; + protected baseUrl: string; + protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined; + + constructor(baseUrl?: string, instance?: AxiosInstance) { + + this.instance = instance || axios.create(); + + this.baseUrl = baseUrl ?? "http://127.0.0.1:5000"; + + } + + /** + * 启用或禁用 Switch 外设 + * @param enable (optional) 是否启用 + * @return 操作结果 + */ + setEnable(enable: boolean | undefined, cancelToken?: CancelToken): Promise { + let url_ = this.baseUrl + "/api/Switch/enable?"; + 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_: AxiosRequestConfig = { + method: "POST", + url: url_, + headers: { + "Accept": "application/json" + }, + cancelToken + }; + + return this.instance.request(options_).catch((_error: any) => { + if (isAxiosError(_error) && _error.response) { + return _error.response; + } else { + throw _error; + } + }).then((_response: AxiosResponse) => { + return this.processSetEnable(_response); + }); + } + + protected processSetEnable(response: AxiosResponse): Promise { + const status = response.status; + let _headers: any = {}; + if (response.headers && typeof response.headers === "object") { + for (const k in response.headers) { + if (response.headers.hasOwnProperty(k)) { + _headers[k] = response.headers[k]; + } + } + } + if (status === 200) { + const _responseText = response.data; + let result200: any = null; + let resultData200 = _responseText; + result200 = resultData200 !== undefined ? resultData200 : null; + + return Promise.resolve(result200); + + } else if (status === 500) { + const _responseText = response.data; + let result500: any = null; + let resultData500 = _responseText; + result500 = Exception.fromJS(resultData500); + return throwException("A server side error occurred.", status, _responseText, _headers, result500); + + } else if (status === 401) { + const _responseText = response.data; + let result401: any = null; + let resultData401 = _responseText; + result401 = ProblemDetails.fromJS(resultData401); + return throwException("A server side error occurred.", status, _responseText, _headers, result401); + + } else if (status !== 200 && status !== 204) { + const _responseText = response.data; + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + } + return Promise.resolve(null as any); + } + + /** + * 控制指定编号的 Switch 开关 + * @param num (optional) 开关编号 + * @param onOff (optional) 开/关 + * @return 操作结果 + */ + setSwitchOnOff(num: number | undefined, onOff: boolean | undefined, cancelToken?: CancelToken): Promise { + let url_ = this.baseUrl + "/api/Switch/switch?"; + if (num === null) + throw new Error("The parameter 'num' cannot be null."); + else if (num !== undefined) + url_ += "num=" + encodeURIComponent("" + num) + "&"; + if (onOff === null) + throw new Error("The parameter 'onOff' cannot be null."); + else if (onOff !== undefined) + url_ += "onOff=" + encodeURIComponent("" + onOff) + "&"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: AxiosRequestConfig = { + method: "POST", + url: url_, + headers: { + "Accept": "application/json" + }, + cancelToken + }; + + return this.instance.request(options_).catch((_error: any) => { + if (isAxiosError(_error) && _error.response) { + return _error.response; + } else { + throw _error; + } + }).then((_response: AxiosResponse) => { + return this.processSetSwitchOnOff(_response); + }); + } + + protected processSetSwitchOnOff(response: AxiosResponse): Promise { + const status = response.status; + let _headers: any = {}; + if (response.headers && typeof response.headers === "object") { + for (const k in response.headers) { + if (response.headers.hasOwnProperty(k)) { + _headers[k] = response.headers[k]; + } + } + } + if (status === 200) { + const _responseText = response.data; + let result200: any = null; + let resultData200 = _responseText; + result200 = resultData200 !== undefined ? resultData200 : null; + + return Promise.resolve(result200); + + } else if (status === 400) { + const _responseText = response.data; + let result400: any = null; + let resultData400 = _responseText; + result400 = ArgumentException.fromJS(resultData400); + return throwException("A server side error occurred.", status, _responseText, _headers, result400); + + } else if (status === 500) { + const _responseText = response.data; + let result500: any = null; + let resultData500 = _responseText; + result500 = Exception.fromJS(resultData500); + return throwException("A server side error occurred.", status, _responseText, _headers, result500); + + } else if (status === 401) { + const _responseText = response.data; + let result401: any = null; + let resultData401 = _responseText; + result401 = ProblemDetails.fromJS(resultData401); + return throwException("A server side error occurred.", status, _responseText, _headers, result401); + + } else if (status !== 200 && status !== 204) { + const _responseText = response.data; + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + } + return Promise.resolve(null as any); + } + + /** + * 控制 Switch 开关 + * @param keyStatus 开关状态 + * @return 操作结果 + */ + setMultiSwitchsOnOff(keyStatus: boolean[], cancelToken?: CancelToken): Promise { + let url_ = this.baseUrl + "/api/Switch/MultiSwitch"; + url_ = url_.replace(/[?&]$/, ""); + + const content_ = JSON.stringify(keyStatus); + + let options_: AxiosRequestConfig = { + data: content_, + method: "POST", + url: url_, + headers: { + "Content-Type": "application/json", + "Accept": "application/json" + }, + cancelToken + }; + + return this.instance.request(options_).catch((_error: any) => { + if (isAxiosError(_error) && _error.response) { + return _error.response; + } else { + throw _error; + } + }).then((_response: AxiosResponse) => { + return this.processSetMultiSwitchsOnOff(_response); + }); + } + + protected processSetMultiSwitchsOnOff(response: AxiosResponse): Promise { + const status = response.status; + let _headers: any = {}; + if (response.headers && typeof response.headers === "object") { + for (const k in response.headers) { + if (response.headers.hasOwnProperty(k)) { + _headers[k] = response.headers[k]; + } + } + } + if (status === 200) { + const _responseText = response.data; + let result200: any = null; + let resultData200 = _responseText; + result200 = resultData200 !== undefined ? resultData200 : null; + + return Promise.resolve(result200); + + } else if (status === 400) { + const _responseText = response.data; + let result400: any = null; + let resultData400 = _responseText; + result400 = ArgumentException.fromJS(resultData400); + return throwException("A server side error occurred.", status, _responseText, _headers, result400); + + } else if (status === 500) { + const _responseText = response.data; + let result500: any = null; + let resultData500 = _responseText; + result500 = Exception.fromJS(resultData500); + return throwException("A server side error occurred.", status, _responseText, _headers, result500); + + } else if (status === 401) { + const _responseText = response.data; + let result401: any = null; + let resultData401 = _responseText; + result401 = ProblemDetails.fromJS(resultData401); + return throwException("A server side error occurred.", status, _responseText, _headers, result401); + + } else if (status !== 200 && status !== 204) { + const _responseText = response.data; + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + } + return Promise.resolve(null as any); + } +} + export class TutorialClient { protected instance: AxiosInstance; protected baseUrl: string; diff --git a/src/components/equipments/Switch.vue b/src/components/equipments/Switch.vue index 05d792a..d8108f4 100644 --- a/src/components/equipments/Switch.vue +++ b/src/components/equipments/Switch.vue @@ -3,7 +3,7 @@ xmlns="http://www.w3.org/2000/svg" :width="width" :height="height" - :viewBox="`4 6 ${props.switchCount + 2} 4`" + :viewBox="`4 6 ${switchCount + 2} 4`" class="dip-switch" > @@ -38,7 +38,7 @@ -