nope/pages/modules/beckhoff.tsx

350 lines
9.7 KiB
TypeScript
Raw Normal View History

/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-12-03 11:57:29
* @modify date 2020-12-03 11:57:29
* @desc [description]
*/
import { faHashtag, faHome } from "@fortawesome/free-solid-svg-icons";
import React from "react";
import {
Alert,
ButtonGroup,
Card,
Col,
Container,
Row,
Spinner,
Table,
ToggleButton
} from "react-bootstrap";
import { INopeDispatcher } from "../../lib/types/nope/nopeDispatcher.interface";
import { INopeModule } from "../../lib/types/nope/nopeModule.interface";
import { INopeObserver } from "../../lib/types/nope/nopeObservable.interface";
2020-12-31 12:18:24 +00:00
import { IReadableIos } from "../../modules/generic-plc/type/interfaces";
import { IBeckhoffPLC } from "../../modules/mod-Beckhoff-PLC-Interface/type/interfaces";
import Toolbar from "../../resources/ui/layout/toolbar";
class BeckhoffComponent extends React.Component<
{ instance: IBeckhoffPLC & INopeModule },
{
intialized: boolean;
connected: boolean;
remote;
2020-12-31 12:18:24 +00:00
inputs: IReadableIos[];
outputs: IReadableIos[];
}
> {
constructor(props) {
super(props);
this.state = {
connected: false,
intialized: false,
remote: "unkown",
inputs: [],
outputs: []
};
}
observers: INopeObserver[] = [];
componentDidMount() {
if (this.props.instance) {
const _this = this;
2020-12-31 12:18:24 +00:00
// Subscribe to the Oberservers
// which will influence the visuals.
this.observers.push(
this.props.instance.connected.subscribe((connected) => {
_this.setState({ connected });
})
);
this.observers.push(
2020-12-31 12:18:24 +00:00
// Wait for the System to Initialize.
// If done, define the Remote-String.
this.props.instance.initialized.subscribe((intialized) => {
_this.setState({
intialized,
remote:
2021-01-07 11:20:43 +00:00
_this.props.instance.adsOptions.getContent()?.hostUri +
":" +
_this.props.instance.adsOptions.getContent()?.port
});
})
);
// Subscribe to the IO-Elements of the
// Module.
this.observers.push(
this.props.instance.readableIos.subscribe((ios) => {
_this.setState({
// Assign the Inputs
inputs: ios.filter((io) => io.type === "input"),
// Assign the Outputs
outputs: ios.filter((io) => io.type === "output")
});
})
);
2020-12-31 12:18:24 +00:00
// Initially define the Remote.
this.setState({
remote:
2021-01-07 11:20:43 +00:00
this.props.instance.adsOptions.getContent()?.hostUri +
":" +
this.props.instance.adsOptions.getContent()?.port
});
}
}
render() {
return (
<Card border={this.state.connected ? "success" : "danger"}>
<Card.Header>
<b>PLC: </b>
<code>{this.props.instance.identifier}</code>{" "}
</Card.Header>
<Card.Body>
{!this.state.intialized ? (
<Alert variant="warning">
<Alert.Heading>
<Spinner animation="border" /> Grabbing Configuration
</Alert.Heading>
<p>Trying to extract the Configuration online.</p>
</Alert>
) : (
<Table striped bordered hover>
<thead>
<tr>
<th>Module</th>
<th>Port</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{/* Currently Render the Inputs Only. */}
{this.state.inputs.map((io, idx) => (
<tr key={"input" + idx}>
<td>{io.orginalName}</td>
<td>{io.dataType}</td>
2020-12-31 12:18:24 +00:00
<td>{io.currentValue}</td>
</tr>
))}
{this.state.outputs
.filter((io) => io.dataType === "boolean")
.map((io, idx) => {
<tr key={"output" + idx}>
<td>{io.orginalName}</td>
<td>{io.dataType}</td>
<td>
<ButtonGroup>
<ToggleButton
key={idx}
type="radio"
variant="danger"
value={false}
checked={io.currentValue === false}
// onChange={(e) => setRadioValue(e.currentTarget.value)}
>
Off
</ToggleButton>
<ToggleButton
key={idx}
type="radio"
variant="sucess"
value={true}
checked={io.currentValue === true}
// onChange={(e) => setRadioValue(e.currentTarget.value)}
>
On
</ToggleButton>
</ButtonGroup>
</td>
</tr>;
})}
</tbody>
</Table>
)}
</Card.Body>
{this.state.connected ? (
""
) : (
<Card.Footer>
Waiting for the plc to accept the connection. Host of the PLC is{" "}
<code>{this.state.remote}</code>
</Card.Footer>
)}
</Card>
);
}
}
class BeckhoffOverviewComponent extends React.Component<
{ dispatcher: INopeDispatcher },
{ instances: (IBeckhoffPLC & INopeModule)[]; connected: boolean }
> {
private _observer: INopeObserver[] = [];
private async __refresh() {
const connected = this.props.dispatcher.communicator.connected.getContent();
const modules = this.props.dispatcher.availableInstances
.getContent()
.filter((mod) => mod.type == "BeckhoffPlc");
const plcs: (IBeckhoffPLC & INopeModule)[] = [];
for (const plc of modules) {
const mod = await this.props.dispatcher.generateInstance<
IBeckhoffPLC & INopeModule
>({
identifier: plc.identifier,
type: plc.type
});
plcs.push(mod);
}
// Dispose the old instances
for (const plc of this.state.instances || []) {
await plc.dispose();
}
this.setState({
instances: plcs,
connected
});
}
/**
* Function will be called if the Item has been rendered sucessfully.
*/
componentDidMount() {
if (this.props.dispatcher) {
// Subscribe to the Instances.
this.__refresh();
const _this = this;
this._observer.push(
this.props.dispatcher.availableInstances.subscribe(() => {
_this.__refresh().catch(console.error);
})
);
this._observer.push(
this.props.dispatcher.communicator.connected.subscribe(() => {
_this.__refresh().catch(console.error);
})
);
}
}
/**
* Function, that will be called before the network fails.
*/
componentWillUnmount() {
// Call the unmount
for (const _observer of this._observer) {
_observer.unsubscribe();
}
}
constructor(props) {
super(props);
this.state = {
instances: [],
connected: false
};
}
public render() {
const idx = 0;
return (
<Container>
<Toolbar<undefined>
toolbar={{
items: [
{
icon: faHome,
label: "root",
ref: "/",
type: "link"
},
{
type: "link",
ref: "/docs",
label: "docs",
icon: faHashtag
}
]
}}
generateData={() => undefined}
brand={{
ref: "",
type: "link",
icon:
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/13/Beckhoff_Logo.svg/1024px-Beckhoff_Logo.svg.png",
label: ""
}}
></Toolbar>
<Row>
<Col>
{this.state.connected ? (
<Alert variant={"success"}>Backend online</Alert>
) : (
<>
<Alert variant={"danger"}>
Not able to connect to the Backend <b>:'(</b>
</Alert>
<p>
Please start a Nope-Backend with the following command:{" "}
<code>
node .\dist\lib\cli\runNopeBackend.js -c io-server
</code>
</p>
</>
)}
</Col>
</Row>
<Row>
<Col>
{/* Available PLCs */}
<Table striped bordered hover>
<thead>
<tr>
<th>PLC-Module-Name</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{/* Create the PLC instances. */}
{this.state.instances.map((instance, idx) => {
return (
<tr key={idx++}>
<td>{instance.identifier}</td>
<td>
<code>{instance.type}</code>
</td>
</tr>
);
})}
</tbody>
</Table>
</Col>
</Row>
{this.state.instances.map((instance, idx) => {
return (
<Row key={idx} style={{ paddingTop: "1rem" }}>
<Col>
<BeckhoffComponent instance={instance}></BeckhoffComponent>
</Col>
</Row>
);
})}
</Container>
);
}
}
export default BeckhoffOverviewComponent;