Pipe Transfers
In this tutorial you will build a FrontPanel Alloy application that reads data from a PipeOut endpoint and writes data to a PipeIn endpoint.
Install Prereqisites
- Node.js – Download | Node.js (nodejs.org)
- Node Package Manager – Downloading and installing Node.js and npm | npm Docs (npmjs.com)
- Source Code Editor
- FrontPanel Application version 6.0.0 or greater
- PipeTest Sample bitfile for you device – Opal Kelly Pins
Create the React Application
Set the current directory to the ‘Workspace’ directory created in the ‘Application Development’ tutorial.
Create a React application in the workspace directory using the Typescript template.
npx create-react-app pipe-transfer-reactapp --template typescript
This command should create a new project directory named ‘pipe-transfer-reactapp’.
Set the current directory to ‘pipe-transfer-reactapp’ and build the template React application.
cd pipe-transfer-reactapp
npm run build
Verify that the application builds successfully.
Install the FrontPanel Alloy React components package
npm install @opalkelly/frontpanel-react-components
Code language: CSS (css)
This step will update the package.json file in your project to add a dependency to the latest version of the FrontPanel React components package. It will also download and install the contents of the package to allow importing the modules provided by the library.
Add the FrontPanel React Component
Add a new file named FrontPanel.tsx to the src directory and add the following:
import React, { Component } from "react";
import {
IFrontPanel,
WorkQueue
} from "@opalkelly/frontpanel-alloy-core";
import {
Button
} from "@opalkelly/frontpanel-react-components";
import "./FrontPanel.css";
export interface FrontPanelProps {
name: string;
}
export interface FrontPanelState {
device: IFrontPanel;
readStatusMessage: string;
writeStatusMessage: string;
}
class FrontPanel extends Component<FrontPanelProps, FrontPanelState> {
private readonly _WorkQueue: WorkQueue = new WorkQueue();
constructor(props: FrontPanelProps) {
super(props);
this.state = {
device: window.FrontPanel,
readStatusMessage: "Ready",
writeStatusMessage: "Ready"
};
}
render() {
return (
<div>
<div className="ControlPanel">
<span>{this.state.readStatusMessage}</span>
<Button
label="Read Data"
tooltip="Read data from PipeOut endpoint"
onButtonClick={this.OnReadDataButtonClick.bind(this)}
/>
</div>
<div className="ControlPanel">
<span>{this.state.writeStatusMessage}</span>
<Button
label="Write Data"
tooltip="Write data to PipeIn endpoint"
onButtonClick={this.OnWriteDataButtonClick.bind(this)}
/>
</div>
</div>
);
}
// Initialize Device
private async InitializeTargetDevice() {
await this.state.device.setWireInValue(0x03, 0x0, 0xffffffff); // Apply fixed pattern
await this.state.device.setWireInValue(0x02, 0xffffffff, 0xffffffff); // Pipe In throttle
await this.state.device.setWireInValue(0x01, 0xffffffff, 0xffffffff); // Pipe Out throttle
}
private async ResetTargetDevice() {
await this.state.device.setWireInValue(0x00, (0 << 2) | (0 << 1) | 1, 0xffffffff); // PATTERN | SET_THROTTLE=0 | RESET=1
await this.state.device.updateWireIns();
await this.state.device.setWireInValue(0x00, (0 << 2) | (0 << 1) | 0, 0xffffffff); // PATTERN | SET_THROTTLE=0 | RESET=1
await this.state.device.updateWireIns();
}
// Event Handlers
private OnReadDataButtonClick() {
this.setState({ readStatusMessage: "Reading..." });
this._WorkQueue.Post(async () => {
await this.InitializeTargetDevice();
await this.ResetTargetDevice();
const data: ArrayBuffer = await this.state.device.readFromPipeOut(0xa0, 1024);
// Validate that the data was read sucessfully
let isValid: boolean = data.byteLength === 1024;
if(isValid) {
const dataArray = new Uint32Array(data);
let nextValue = 0x00000001;
for (let index = 0; (index < dataArray.length) && isValid; index++) {
isValid = dataArray[index] === nextValue;
nextValue = (nextValue + 1) % 0xffffffff;
}
}
if(isValid) {
this.setState({ readStatusMessage: "Read 1024 bytes successfully." });
}
else {
this.setState({ readStatusMessage: "Failed to read 1024 bytes." });
}
});
}
private OnWriteDataButtonClick() {
this.setState({ readStatusMessage: "Writing..." });
this._WorkQueue.Post(async () => {
await this.InitializeTargetDevice();
await this.ResetTargetDevice();
await this.state.device.writeToPipeIn(0x80, 1024, (data: ArrayBuffer) => {
const dataArray = new Uint32Array(data);
// Write data to the data array
let nextValue = 0x00000001;
for (let index = 0; index < dataArray.length; index++) {
dataArray[index] = nextValue;
nextValue = (nextValue + 1) % 0xffffffff;
}
});
// Validate that the data was written sucessfully
await this.state.device.updateWireOuts();
const validationResult: number = await this.state.device.getWireOutValue(0x21);
if(validationResult === 0) {
this.setState({ writeStatusMessage: "Wrote 1024 bytes successfully." });
}
else {
this.setState({ writeStatusMessage: "Failed to write 1024 bytes." });
}
});
}
}
export default FrontPanel
Code language: JavaScript (javascript)
This defines a React Component named FrontPanel
whose state contains an instance of an IFrontPanel
interface and two strings. Each string is used to store the text of a message indicating the status of the corresponding read or write operation. Read operations are performed and the data retrieved is validated by the OnReadDataButtonClick
method in response to the user clicking the ‘Read Data’ button. Write operations are performed and validated by the OnWriteDataButtonClick
method when the user clicks the ‘Write Data’ button.
Add the Style Sheet
Add a new file named FrontPanel.css to the src directory and add the following:
.ControlPanel {
display: flex;
flex-direction: column;
color: black;
background-color: white;
border-radius: 8px;
padding: 10px;
gap: 8px;
margin: 10px;
}
Code language: CSS (css)
This defines the style to apply to the <div>
elements that each contain a span
and a Button
so that the components will layout in a column with some space around them.
Update the App Component
Open the App.tsx file and edit it to look like the following:
import React from "react";
import "./App.css";
import FrontPanel from "./FrontPanel";
import { Application } from "@opalkelly/frontpanel-react-components";
function App() {
return (
<div className="App">
<Application>
<FrontPanel name="Sample" />
</Application>
</div>
);
}
export default App;
Code language: JavaScript (javascript)
This will cause the application to display the FrontPanel
component that was created in the previous steps.
Open the App.css file and edit it to look like the following:
.App {
text-align: center;
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
}
Code language: CSS (css)
Build the Application
Run the following command in the terminal:
npm run build
Run the Application
Using the FrontPanel application with an XEM device connected via USB, load the ‘pipetest.bit’ file and then load the ‘FrontPanel Profile’.
The application should run, and clicking both the ‘Read Data’ and the ‘Write Data’ buttons should indicate that the corresponding operation was successful.