added thingsboard support

This commit is contained in:
Nico Melone
2022-10-12 10:14:10 -05:00
parent 69d91dfbce
commit 5051ba81dc
5 changed files with 34542 additions and 22963 deletions

38047
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,20 +3,20 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"aws-sdk": "^2.707.0",
"axios": "^0.19.2",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"aws-sdk": "^2.1226.0",
"axios": "^0.27.2",
"cors": "^2.8.5",
"express": "^4.17.1",
"fetch-retry": "^4.0.1",
"express": "^4.18.1",
"fetch-retry": "^5.0.3",
"multer": "^1.4.2",
"nodemon": "^2.0.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"react-toastify": "^6.0.6"
"nodemon": "^2.0.20",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"react-toastify": "^9.0.8"
},
"scripts": {
"start": "react-scripts start",

View File

@@ -7,6 +7,7 @@ label {
font-size: medium;
}
.App-header {
background-color: #282c34;
min-height: 5vh;
@@ -20,6 +21,7 @@ label {
UpdateDeviceForm {
padding: 10px;
display: inline-block
}
SyncForm {
padding: 10px;
@@ -36,4 +38,6 @@ FileUpload {
.tool {
padding-bottom: 5em;
display: inline-flex;
}

View File

@@ -1,86 +1,111 @@
import React from 'react';
import { ToastContainer, toast } from 'react-toastify';
import axios from 'axios';
import 'react-toastify/dist/ReactToastify.css';
import './App.css';
import React from "react";
import { ToastContainer, toast } from "react-toastify";
import axios from "axios";
import "react-toastify/dist/ReactToastify.css";
import "./App.css";
class UpdateDeviceForm extends React.Component {
constructor(props) {
super(props);
this.state = {
mac: '',
devicetype: '',
m1: true
mac: "",
devicetype: "",
m1: true,
tb: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleTBChange = this.handleTBChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.name === 'm1' ? target.checked : target.value;
const value = target.name === "m1" ? target.checked : target.value;
const name = target.name;
this.setState({[name]: value});
this.setState({ [name]: value });
}
handleTBChange(event) {
const target = event.target;
const value = target.name === "tb" ? target.checked : target.value;
const name = target.name;
this.setState({ [name]: value });
}
handleSubmit(event) {
const devUrl = {
"abbflow": "abbflow",
"advvfdipp": "advvfdipp/advvfdippv2",
"dhsensor": "dhsensor",
"dual_flowmeter": "dual_flowmeter",
"flowmeterskid": "flowmeterskid",
"flowmonitor": "flow-monitor/flow-monitorv2",
"PiFlow": "piflow",
"ipp": "ipp",
"plcpond": "plcpond",
"multisensor": "multisensor",
"dualactuator": "dualactuator",
"dualactuatorpri": "dualactuator/alt",
"plcfreshwater": "plcfreshwater",
"pondlevel": "pondlevel",
"promagmbs": "promagmbs",
"poc": "poc",
"recycle_train": "recycle_train",
"rigpump": "rigpump",
"submonitor": "submonitor",
"swdcontroller": "swdcontroller",
"tankalarms": "tankalarms",
"tanktransfer": "tanktransfer",
"tenflowmeterskid": "tenflowmeterskid",
"transferlite": "transferlite"
}
let config = ""
if (this.state.m1) {
if (this.state.devicetype === "plcfreshwater"){
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/plcfresh/'";
}else if(this.state.devicetype === "PiFlow"){
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/piflow/'";
}else{
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/','m1': 'https://hp-drivers.s3-us-west-2.amazonaws.com/M1/'"
abbflow: "abbflow",
advvfdipp: "advvfdipp/advvfdippv2",
dhsensor: "dhsensor",
dual_flowmeter: "dual_flowmeter",
flowmeterskid: "flowmeterskid",
flowmonitor: "flow-monitor/flow-monitorv2",
PiFlow: "piflow",
ipp: "ipp",
plcpond: "plcpond",
multisensor: "multisensor",
dualactuator: "dualactuator",
dualactuatorpri: "dualactuator/alt",
plcfreshwater: "plcfreshwater",
pondlevel: "pondlevel",
promagmbs: "promagmbs",
poc: "poc",
recycle_train: "recycle_train",
rigpump: "rigpump",
submonitor: "submonitor",
swdcontroller: "swdcontroller",
tankalarms: "tankalarms",
tanktransfer: "tanktransfer",
tenflowmeterskid: "tenflowmeterskid",
transferlite: "transferlite",
};
let config = "";
if (this.state.tb) {
if (this.state.m1) {
if (this.state.devicetype === "plcfreshwater") {
config = "'mainHP': 'https://hp-thingsboard.s3.amazonaws.com/mainHP/'";
} else if (this.state.devicetype === "PiFlow") {
config = "'mainHP': 'https://hp-thingsboard.s3.amazonaws.com/mainHP/'";
} else {
config = "'mainHP': 'https://hp-thingsboard.s3.amazonaws.com/mainHP/','m1': 'https://hp-thingsboard.s3.amazonaws.com/M1/'";
}
} else {
if (this.state.devicetype === "plcfreshwater") {
config = "'mainHP': 'https://hp-thingsboard.s3.amazonaws.com/mainHPRPI/'";
} else {
config = "'mainHP': 'https://hp-thingsboard.s3.amazonaws.com/mainHPRPI/'";
}
}
}else {
if (this.state.devicetype === "plcfreshwater"){
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/plcfresh/'";
}else{
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/piflow/'"
} else {
if (this.state.m1) {
if (this.state.devicetype === "plcfreshwater") {
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/plcfresh/'";
} else if (this.state.devicetype === "PiFlow") {
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/piflow/'";
} else {
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/','m1': 'https://hp-drivers.s3-us-west-2.amazonaws.com/M1/'";
}
} else {
if (this.state.devicetype === "plcfreshwater") {
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/plcfresh/'";
} else {
config = "'mainHP': 'https://hp-drivers.s3-us-west-2.amazonaws.com/mainMeshify/piflow/'";
}
}
}
const devArray = this.state.devicetype.split(',');
const devArray = this.state.devicetype.split(",");
//console.log(devArray);
for(let index = 0; index < devArray.length; index ++){
if (devArray[index] === "dualactuatorpri"){
config = config + ", 'dualactuator': 'https://s3.amazonaws.com/pocloud-drivers/" + devUrl[devArray[index]] +"/'";
}else{
config = config + ",'" + devArray[index] +"': 'https://s3.amazonaws.com/pocloud-drivers/" + devUrl[devArray[index]] +"/'";
for (let index = 0; index < devArray.length; index++) {
if (devArray[index] === "dualactuatorpri") {
config = config + ", 'dualactuator': 'https://s3.amazonaws.com/pocloud-drivers/" + devUrl[devArray[index]] + "/'";
} else {
config = config + ",'" + devArray[index] + "': 'https://s3.amazonaws.com/pocloud-drivers/" + devUrl[devArray[index]] + "/'";
}
};
fetch('https://f5rrbd3r45.execute-api.us-east-1.amazonaws.com/device_config?mac='+this.state.mac + '&config={' + config + '}')
.then(res => {
}
fetch("https://f5rrbd3r45.execute-api.us-east-1.amazonaws.com/device_config?mac=" + this.state.mac + "&config={" + config + "}").then((res) => {
res.json();
if (res["status"] === 200){
if (res["status"] === 200) {
toast.success("Added to Database", {
position: "bottom-center",
autoClose: 5000,
@@ -89,8 +114,8 @@ class UpdateDeviceForm extends React.Component {
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}else {
});
} else {
toast.error("Something went wrong try again", {
position: "bottom-center",
autoClose: 5000,
@@ -99,25 +124,22 @@ class UpdateDeviceForm extends React.Component {
pauseOnHover: true,
draggable: true,
progress: undefined,
});
});
}
console.log(res);
})
});
//console.log(config);
event.preventDefault();
}
render() {
return (
<div className="tool">
<form onSubmit={this.handleSubmit}>
<label>
MAC: </label>
<form onSubmit={this.handleSubmit}>
<label>MAC: </label>
<input name="mac" type="text" value={this.state.value} onChange={this.handleChange} />
<label>
Device Type: </label>
<label>Device Type: </label>
<select name="devicetype" value={this.state.devicetype} onChange={this.handleChange}>
<option value=""></option>
<option value="abbflow">ABB Flow Meter</option>
@@ -148,12 +170,14 @@ class UpdateDeviceForm extends React.Component {
<option value="transferlite">Transfer Station Lite</option>
</select>
<label>
M1: </label>
<input name="m1" type="checkbox" checked={this.state.m1} onChange={this.handleChange}/>
<label>M1: </label>
<input name="m1" type="checkbox" checked={this.state.m1} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
<label>ThingsBoard: </label>
<input name="tb" type="checkbox" checked={this.state.tb} onChange={this.handleTBChange} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
@@ -162,8 +186,8 @@ class SyncForm extends React.Component {
constructor(props) {
super(props);
this.state = {
mac: '',
devicetype: ''
mac: "",
devicetype: "",
};
this.handleChange = this.handleChange.bind(this);
@@ -174,41 +198,40 @@ class SyncForm extends React.Component {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({[name]: value});
this.setState({ [name]: value });
}
handleSubmit(event) {
const tech = {
"abbflow": "01:99",
"advvfdipp": "01:99",
"dhsensor": "01:99",
"dual_flowmeter": "01:99",
"flowmeterskid": "01:99",
"flowmonitor": "01:40",
"PiFlow": "01:99",
"ipp": "01:99",
"plcpond": "01:99",
"multisensor": "01:99",
"dualactuator": "01:99",
"M1": "00:30",
"plcfreshwater": "01:99",
"pondlevel": "01:30",
"promagmbs": "01:99",
"poc": "01:99",
"recycle_train": "01:99",
"rigpump": "01:99",
"submonitor": "01:99",
"swdcontroller": "01:99",
"tankalarms": "01:99",
"tanktransfer": "01:99",
"tenflowmeterskid": "01:99",
"transferlite": "01:99",
}
fetch('https://f5rrbd3r45.execute-api.us-east-1.amazonaws.com/sync?mac='+this.state.mac + '&devicetype='+ this.state.devicetype + '&technumber=' + tech[this.state.devicetype])
.then(res => {
abbflow: "01:99",
advvfdipp: "01:99",
dhsensor: "01:99",
dual_flowmeter: "01:99",
flowmeterskid: "01:99",
flowmonitor: "01:40",
PiFlow: "01:99",
ipp: "01:99",
plcpond: "01:99",
multisensor: "01:99",
dualactuator: "01:99",
M1: "00:30",
plcfreshwater: "01:99",
pondlevel: "01:30",
promagmbs: "01:99",
poc: "01:99",
recycle_train: "01:99",
rigpump: "01:99",
submonitor: "01:99",
swdcontroller: "01:99",
tankalarms: "01:99",
tanktransfer: "01:99",
tenflowmeterskid: "01:99",
transferlite: "01:99",
};
fetch("https://f5rrbd3r45.execute-api.us-east-1.amazonaws.com/sync?mac=" + this.state.mac + "&devicetype=" + this.state.devicetype + "&technumber=" + tech[this.state.devicetype]).then((res) => {
res.json();
console.log(res);
if (res["status"] === 200){
if (res["status"] === 200) {
toast.success("Sent to MQTT Broker", {
position: "bottom-center",
autoClose: 5000,
@@ -217,8 +240,8 @@ class SyncForm extends React.Component {
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}else {
});
} else {
toast.error("Something went wrong try again", {
position: "bottom-center",
autoClose: 5000,
@@ -227,21 +250,19 @@ class SyncForm extends React.Component {
pauseOnHover: true,
draggable: true,
progress: undefined,
});
});
}
})
});
event.preventDefault();
}
render() {
return (
<div className="tool">
<form onSubmit={this.handleSubmit}>
<label>
MAC: </label>
<label>MAC: </label>
<input name="mac" type="text" value={this.state.value} onChange={this.handleChange} />
<label>
Device Type: </label>
<label>Device Type: </label>
<select name="devicetype" value={this.state.devicetype} onChange={this.handleChange}>
<option value=""></option>
<option value="abbflow">ABB Flow Meter</option>
@@ -270,26 +291,24 @@ class SyncForm extends React.Component {
<option value="transferlite">Transfer Station Lite</option>
</select>
<input type="submit" value="Submit" />
<input type="submit" value="Submit" />
</form>
</div>
)
);
}
}
class FileUpload extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedFile: null,
resample: '',
timescale: 'S',
file: null
resample: "",
timescale: "S",
file: null,
};
this.onSampleChange = this.onSampleChange.bind(this);
}
/* state = {
@@ -300,121 +319,120 @@ class FileUpload extends React.Component {
}; */
// On file select (from the pop up)
onFileChange = event => {
onFileChange = (event) => {
// Update the state
this.setState({ selectedFile: event.target.files });
};
onSampleChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({[name]: value});
this.setState({ [name]: value });
}
// On file upload (click the upload button)
onFileUpload = () => {
let requests = [];
let names = [];
let resample = this.state.resample;
let timescale = this.state.timescale;
// Update the formData object
for(var x = 0; x<this.state.selectedFile.length; x++){
for (var x = 0; x < this.state.selectedFile.length; x++) {
// Request made to the backend api
// Send formData object
names.push(this.state.selectedFile[x].name)
requests.push(axios.put("https://gvya48j9o5.execute-api.us-east-1.amazonaws.com/beta/channel-combiner/"+this.state.selectedFile[x].name, this.state.selectedFile[x]))
names.push(this.state.selectedFile[x].name);
requests.push(axios.put("https://gvya48j9o5.execute-api.us-east-1.amazonaws.com/beta/channel-combiner/" + this.state.selectedFile[x].name, this.state.selectedFile[x]));
}
axios.all(requests).then(axios.spread((...respones) => {
let sentData = true;
for( var y = 0; y<respones.length; y++){
//console.log(respones[y]);
if(respones[y]["data"] !== ""){
sentData = false;
axios.all(requests).then(
axios.spread((...respones) => {
let sentData = true;
for (var y = 0; y < respones.length; y++) {
//console.log(respones[y]);
if (respones[y]["data"] !== "") {
sentData = false;
}
}
}
if (sentData){
toast.success("File(s) uploaded successfully", {
position: "bottom-center",
autoClose: 2500,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
if (sentData) {
toast.success("File(s) uploaded successfully", {
position: "bottom-center",
autoClose: 2500,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
//trigger lambda to process files
setTimeout(() => {fetch('https://gvya48j9o5.execute-api.us-east-1.amazonaws.com/beta/processing?filenames='+names + '&resample='+resample+timescale)
.catch(console.log("API Gateway Timed Out"))},1000);
setTimeout(() => {
fetch("https://gvya48j9o5.execute-api.us-east-1.amazonaws.com/beta/processing?filenames=" + names + "&resample=" + resample + timescale).catch(console.log("API Gateway Timed Out"));
}, 1000);
//poll to get output csv every 30 secs until success because API Gateway will likely timeout
const fetch_retry = (n) => {
fetch('https://gvya48j9o5.execute-api.us-east-1.amazonaws.com/beta/channel-combiner/output.csv')
.then(res => {
res.text()
.then((blob, error) => {
if (blob[0] === "<") throw error;
const url = window.URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'output.csv');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
axios.delete("https://gvya48j9o5.execute-api.us-east-1.amazonaws.com/beta/channel-combiner/output.csv")
}).catch((error) => {
if (n === 1) throw error;
toast.warning(`File not ready trying again in 30s, ${n-1} tries left`, {
position: "bottom-center",
autoClose: 2500,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
fetch("https://gvya48j9o5.execute-api.us-east-1.amazonaws.com/beta/channel-combiner/output.csv")
.then((res) => {
res
.text()
.then((blob, error) => {
if (blob[0] === "<") throw error;
const url = window.URL.createObjectURL(new Blob([blob]));
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "output.csv");
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
axios.delete("https://gvya48j9o5.execute-api.us-east-1.amazonaws.com/beta/channel-combiner/output.csv");
})
.catch((error) => {
if (n === 1) throw error;
toast.warning(`File not ready trying again in 30s, ${n - 1} tries left`, {
position: "bottom-center",
autoClose: 2500,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
return setTimeout(() => {fetch_retry(n-1);}, 30000);
});
}).catch((error) => {
if (n === 1) throw error;
console.log("There are " + n-1 + " tries left")
return setTimeout(() => {fetch_retry(n-1);}, 30000);
})
}
fetch_retry(6);
}else{
toast.error("File(s) failed to upload try again", {
position: "bottom-center",
autoClose: 2500,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
return setTimeout(() => {
fetch_retry(n - 1);
}, 30000);
});
})
.catch((error) => {
if (n === 1) throw error;
console.log("There are " + n - 1 + " tries left");
return setTimeout(() => {
fetch_retry(n - 1);
}, 30000);
});
};
fetch_retry(6);
} else {
toast.error("File(s) failed to upload try again", {
position: "bottom-center",
autoClose: 2500,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
}))
}
})
);
};
// File content to be displayed after
// file upload is complete
fileData = () => {
if (this.state.selectedFile) {
return (
<div>
<h2>File Details:</h2>
<p>File Name: {this.state.selectedFile.name}</p>
<p>File Type: {this.state.selectedFile.type}</p>
</div>
);
} else {
@@ -428,55 +446,38 @@ class FileUpload extends React.Component {
};
render() {
return (
<div>
<div className="tool">
<label>Input Files: </label>
<input type="file" multiple onChange={this.onFileChange} />
<label>Resample Rate: </label>
<input name="resample" type="text" value={this.state.resample} onChange={this.onSampleChange} />
<select name="timescale" value={this.state.timescale} onChange={this.onSampleChange}>
<option value="S">Seconds</option>
<option value="T">Minutes</option>
<option value="H">Hours</option>
<option value="D">Days</option>
</select>
{<button onClick={this.onFileUpload}>
Upload
</button> }
</div>
<div className="tool">
<label>Input Files: </label>
<input type="file" multiple onChange={this.onFileChange} />
<label>Resample Rate: </label>
<input name="resample" type="text" value={this.state.resample} onChange={this.onSampleChange} />
<select name="timescale" value={this.state.timescale} onChange={this.onSampleChange}>
<option value="S">Seconds</option>
<option value="T">Minutes</option>
<option value="H">Hours</option>
<option value="D">Days</option>
</select>
{<button onClick={this.onFileUpload}>Upload</button>}
</div>
</div>
);
}
}
function App() {
document.title = "HP Tools"
document.title = "HP Tools";
return (
<div className="App">
<header className="App-header">
Tools
</header>
<header className="App-header">Tools</header>
<header className="toolheader">Update/Create Device</header>
<UpdateDeviceForm></UpdateDeviceForm>
<header className="toolheader">Sync Device</header>
<SyncForm></SyncForm>
<header className="toolheader">Channel Combiner</header>
<FileUpload></FileUpload>
<ToastContainer
position="bottom-center"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
<ToastContainer position="bottom-center" autoClose={5000} hideProgressBar={false} newestOnTop={false} closeOnClick rtl={false} pauseOnFocusLoss draggable pauseOnHover />
</div>
);
}

18875
yarn.lock

File diff suppressed because it is too large Load Diff