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

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-componentsCode 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 FrontPanelCode 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.