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 @@
-
+
+
+