如何添加新的器件 - ChayCai/Wonton_master GitHub Wiki
器件实现的源代码位于 Wonton/Wonton.CrossUI.Web/ClientApp/src/components/Devices 目录下(以下简称 Devices 目录)。
一般将器件类型分为输入型(如LED)和输出型(如按钮),下面从两个例子介绍如何添加新的器件。
在 Devices 目录下创建 LED 目录,在 LED 目录下又创建 LED.js 和 LEDCore.js 文件。在 LED 目录下引入其他必要的文件。
后缀为Core的文件代表这是一个无状态组件,无状态的直观意思是组件中仅涉及 props,不涉及 state。
import React, { Component } from 'react';
import ledOn from './led_on.svg';
import ledOff from './led_off.svg';
export class LEDCore extends Component {
static defaultProps = {
name: "LED",
ClassName: "LED",
onOff: false
}
render() {
return (
<div>
<img src={this.props.onOff ? ledOn : ledOff} alt="led"></img>
</div>
);
}
}
LEDCore 组件的意义十分明了:根据 props.onOff 值改变自身的亮、灭图片,来虚拟展示LED的亮、灭。
LEDCore 组件的 defaultProps 必须设置,name 和 ClassName 必须设为器件名称。
没有后缀的文件代表是有状态的组件,状态组件涉及与 FPGA 的通信。
import React, { Component } from 'react';
import { manager } from '../../Service/FPGAManager';
import { LEDCore } from './LEDCore';
export class LED extends Component {
static defaultProps = {
name: 'LED',
input: [0], //LED只有一个输入
ports: ['输入1'],
portsDirs: ['输入']
}
state = {
inputs: [0]
}
componentDidMount() {
let ins = this.props.instance;
let that = this;
manager.Subscribe(ins, this.props.ports, (inputs, deltaTime) => {
that.setState({
inputs: inputs
});
});
manager.RegisterProjectPorts(this.props.instance, this.state.inputs.length);
}
componentWillUnmount() {
manager.UnSubscribe(this.props.instance);
manager.UnRegisterProjectPorts(this.props.instance);
}
render() {
let on = this.state.inputs[0] === 1 ? true : false;
return (
<LEDCore onOff={on}/>
);
}
}
首先设置 defaultProps,name 设置为器件名。input 是一个数组,数组的个数是这个器件的输入端口数,这里只有一个LED,因此input 数组个数为1;数组的值表示这个器件的初始值。ports 是一个数组,对 input 数组补充了每个端口的名称信息;portsDirs 是一个数组,对 input 数组补充了每个端口的输入、输出信息。
然后设置 state,本器件发生变化的就是输入端口的值,因此这里使用一个 input 数组,与 defaultProps 中的 input 数组相呼应。
注册两个 React 生命周期函数:componentDidMount(器件加载时触发)和 componentWillUnmount(器件销毁时触发)。 在 componentDidMount 中调用 FPGAManager 的两个注册函数 Subscribe 和 RegisterProjectPorts。在 componentWillUnmount 中调用 FPGAManager 的两个反注册函数 UnSubscribe 和 UnRegisterProjectPorts。
Subscribe 函数主要向 FPGAManager 注册回调函数,即当 FPGAManager 从 FPGA 硬件上获取到数据后能够更新器件。deltaTime 指的是这次 FPGA 运行时钟上升沿距离上一次时钟上升沿过去的时间,单位毫秒。
最后注册 render 函数,这一部分主要调用 LEDCore 这个无状态组件,将从 FPGAManager 取得的数据应用到器件上。
至此一个器件完成构建。
在 Devices 目录下创建 HButton 目录,在 HButton 目录下又创建 HButton.js 和 HButtonCore.js 文件。在 HButton 目录下引入其他必要的文件。
import React, { Component } from 'react';
import { Button } from 'reactstrap'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faMusic } from '@fortawesome/free-solid-svg-icons'
import './HButton.css';
export class HButtonCore extends Component {
static defaultProps = {
name: "按钮",
ClassName: "HButton"
}
onCheckClick = (e) => {
if(this.props.onClick)
{
this.props.onClick(e);
}
}
render() {
return (
<Button outline active={this.props.active} size='lg' className="myToggleButton" onClick={e => this.onCheckClick(e)}>
<FontAwesomeIcon icon={faMusic}/>
</Button>
);
}
}
HButtonCore 组件在内部实例化了一个 Button,该 Button 使用回调函数将按钮事件传导至上层组件。defaultProps 的要求同 LEDCore。
import React, { Component } from 'react';
import { manager } from '../../Service/FPGAManager';
import { HButtonCore } from './HButtonCore'
import './HButton.css'
export class HButton extends Component {
outputPorts = ['输出1']
static defaultProps = {
name: '按钮',
ports: ['输出1'],
portsDirs: ['输出']
}
state = {
outputs: [0]
}
componentDidMount() {
manager.Register(this.props.instance, this.state.outputs.length);
manager.RegisterProjectPorts(this.props.instance, this.state.outputs.length);
}
componentWillUnmount() {
manager.UnRegister(this.props.instance);
manager.UnRegisterProjectPorts(this.props.instance);
}
ButtonClick = (event) => {
this.setState((prevState) => {
let nextOutput = 1 - prevState.outputs[0];
console.log("Button click "+this.props.instance + ": "+nextOutput)
return {
outputs : [nextOutput],
}
});
manager.UpdateInput(this.props.instance, [1 - this.state.outputs[0]]);
}
render() {
return (
<HButtonCore onClick={this.ButtonClick} active={this.state.outputs[0] === 1} />
);
}
}
输入型器件和输出型器件的代码差异只有对 FPGAManager 的注册方式的差异。在 componentDidMount 中调用 Register 和 RegisterProjectPorts 函数,在 componentWillUnmount 调用反注册函数。
输出型器件还涉及对输出值的改变,调用 FPGAManager 的 UpdateInput 函数即可。注意 UpdateInput 的第二个参数为数组,因为输出型器件可能有多个输出。
当器件的代码写好后,还不能展示到界面上,还需要在 Devices 目录下的 Devices.js 进行全局注册。
import { LED } from "./LED/LED";
import { LEDCore } from "./LED/LEDCore";
import { HButton } from "./HButton/HButton";
import { HButtonCore } from "./HButton/HButtonCore";
export const deviceMap = new Map([
['LED', [LED, LEDCore]],
['HButton', [HButton, HButtonCore]]
])
export class Devices {
constructor (className, opts) {
return new deviceMap[className][0](opts);
}
}
export class DeviceCore {
constructor (className, opts) {
return new deviceMap[className][1](opts);
}
}
导入组件类,然后按照上述示例代码扩充 deviceMap 变量即可(Map的值是一个数组,第一个值为有状态类,第二个值为无状态值)。请勿修改其他函数和类代码。