Initial commit
This commit is contained in:
3
src/App.css
Normal file
3
src/App.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.demo-logo h2 {
|
||||
color: antiquewhite;
|
||||
}
|
||||
74
src/App.jsx
Normal file
74
src/App.jsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import ConnectForm from "./pages/Login";
|
||||
import { Client } from "paho-mqtt";
|
||||
import UnauthPage from "./layout/UnauthPage";
|
||||
import { Spin } from "antd";
|
||||
import Page from "./layout/Page";
|
||||
import Spectrum from "./pages/Spectrum";
|
||||
|
||||
const App = () => {
|
||||
const [client, setClient] = useState(null);
|
||||
const [connected, setConnected] = useState(false);
|
||||
const [connecting, setConnecting] = useState(false);
|
||||
const [alert, setAlert] = useState(null);
|
||||
|
||||
const onConnect = (url, options) => {
|
||||
console.log(`Trying to connect to ${url}...`);
|
||||
|
||||
const _client = new Client(url, options.clientId);
|
||||
|
||||
_client.onConnectionLost = onDisconnect;
|
||||
|
||||
_client.connect({
|
||||
onSuccess: onConnected,
|
||||
onFailure: onError,
|
||||
userName: options.username,
|
||||
password: options.password,
|
||||
cleanSession: options.clean,
|
||||
timeout: options.connectTimeout,
|
||||
});
|
||||
|
||||
console.log('Options:', options);
|
||||
|
||||
setClient(_client);
|
||||
setConnecting(true);
|
||||
};
|
||||
|
||||
const onConnected = () => {
|
||||
console.log("Connected!");
|
||||
setConnecting(false);
|
||||
setConnected(true);
|
||||
};
|
||||
|
||||
const onError = (err) => {
|
||||
console.error("Failed to connect", err);
|
||||
setConnecting(false);
|
||||
setAlert({ type: 'error', msg: `Could not connect, error: ${err.errorMessage}` });
|
||||
};
|
||||
|
||||
const onDisconnect = () => {
|
||||
client.disconnect();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (connecting)
|
||||
setAlert(null);
|
||||
}, [connecting]);
|
||||
|
||||
if (!connected)
|
||||
return (
|
||||
<UnauthPage content={
|
||||
<ConnectForm
|
||||
connect={onConnect}
|
||||
btnContent={connecting ? <Spin /> : 'Connect'}
|
||||
/>
|
||||
} alert={alert} />
|
||||
)
|
||||
|
||||
return (
|
||||
<Page
|
||||
content={<Spectrum />} />
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
65
src/ConnectedPage.jsx
Normal file
65
src/ConnectedPage.jsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { useState } from 'react';
|
||||
import { Client } from 'paho-mqtt';
|
||||
|
||||
|
||||
const ConnectedPage = () => {
|
||||
const StateMessages = [
|
||||
"Clique no botão para conectar",
|
||||
"Conectando...",
|
||||
"Conectado",
|
||||
"Erro na conexão",
|
||||
];
|
||||
|
||||
const [connected, setConnected] = useState(false);
|
||||
const [client, setClient] = useState(null);
|
||||
const [state, setState] = useState(0)
|
||||
|
||||
const handleClick = () => {
|
||||
const options = {
|
||||
clientId: 'aaaaa',
|
||||
username: 'test',
|
||||
password: '31415926',
|
||||
clean: true,
|
||||
reconnectPeriod: 1000,
|
||||
connectTimeout: 30 * 1000,
|
||||
host: 'fwmari.net',
|
||||
port: 8883,
|
||||
};
|
||||
|
||||
const _client = new Client(`ws://${options.host}:${options.port}/mqtt`, options.clientId);
|
||||
|
||||
_client.connect({
|
||||
onSuccess: () => {
|
||||
console.log("Conectado");
|
||||
setConnected(true);
|
||||
setState(2);
|
||||
},
|
||||
onFailure: (err) => {
|
||||
console.log(`Erro ao conectar ${err}`);
|
||||
setState(3);
|
||||
},
|
||||
});
|
||||
|
||||
setClient(_client);
|
||||
setState(1);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={handleClick}>
|
||||
Conectar ao servidor MQTT
|
||||
</button>
|
||||
|
||||
<p>Status: {StateMessages[state]}</p>
|
||||
|
||||
{connected && (
|
||||
<div>
|
||||
{/* O resto da sua página vai aqui */}
|
||||
<p>Conectado ao servidor MQTT! Agora você pode ver o resto da página.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ConnectedPage;
|
||||
16
src/layout/Footer.jsx
Normal file
16
src/layout/Footer.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Layout } from "antd";
|
||||
const { Footer } = Layout;
|
||||
|
||||
const SFooter = () => {
|
||||
return (
|
||||
<Footer
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Marisa © 2023
|
||||
</Footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default SFooter;
|
||||
70
src/layout/Page.jsx
Normal file
70
src/layout/Page.jsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Alert, Layout, Menu, theme } from 'antd';
|
||||
import SFooter from './Footer';
|
||||
import PropTypes from 'prop-types';
|
||||
const { Header, Content } = Layout;
|
||||
|
||||
import '../App.css'
|
||||
|
||||
const menus = [
|
||||
{
|
||||
key: 'spectrum',
|
||||
label: 'Espectro',
|
||||
},
|
||||
{
|
||||
key: 'config',
|
||||
label: 'Configuração',
|
||||
},
|
||||
{
|
||||
key: 'about',
|
||||
label: 'sobre',
|
||||
}
|
||||
];
|
||||
|
||||
const Page = ({ content, alert }) => {
|
||||
const {
|
||||
token: { colorBgContainer },
|
||||
} = theme.useToken();
|
||||
return (
|
||||
<Layout className="layout">
|
||||
<Header
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="demo-logo"
|
||||
style={{ margin: '0 30px' }}
|
||||
>
|
||||
<h2>Spectrometer</h2>
|
||||
</div>
|
||||
<Menu
|
||||
theme="dark"
|
||||
mode="horizontal"
|
||||
defaultSelectedKeys={['spectrum']}
|
||||
items={menus}
|
||||
/>
|
||||
</Header>
|
||||
<Content>
|
||||
{alert && <Alert type={alert.type} message={alert.msg} banner />}
|
||||
<div
|
||||
className="site-layout-content"
|
||||
style={{
|
||||
padding: '16px 50px',
|
||||
background: colorBgContainer,
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
</Content>
|
||||
<SFooter />
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
Page.propTypes = {
|
||||
content: PropTypes.element.isRequired,
|
||||
alert: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Page;
|
||||
49
src/layout/UnauthPage.jsx
Normal file
49
src/layout/UnauthPage.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Alert, Layout, theme } from "antd";
|
||||
import PropTypes from 'prop-types';
|
||||
const { Header, Content } = Layout;
|
||||
|
||||
import '../App.css'
|
||||
import SFooter from "./Footer";
|
||||
|
||||
const UnauthPage = ({ content, alert }) => {
|
||||
const {
|
||||
token: { colorBgContainer },
|
||||
} = theme.useToken();
|
||||
return (
|
||||
<Layout>
|
||||
<Header
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="demo-logo"
|
||||
style={{ margin: '0 30px' }}
|
||||
>
|
||||
<h2>Spectrometer</h2>
|
||||
</div>
|
||||
</Header>
|
||||
<Content>
|
||||
{alert && <Alert type={alert.type} message={alert.msg} banner />}
|
||||
<div
|
||||
className="site-layout-content"
|
||||
style={{
|
||||
padding: '16px 50px',
|
||||
background: colorBgContainer,
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
</Content>
|
||||
<SFooter />
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
UnauthPage.propTypes = {
|
||||
content: PropTypes.element.isRequired,
|
||||
alert: PropTypes.object,
|
||||
};
|
||||
|
||||
export default UnauthPage;
|
||||
9
src/main.jsx
Normal file
9
src/main.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
0
src/pages/Calib.jsx
Normal file
0
src/pages/Calib.jsx
Normal file
48
src/pages/Dashboard.jsx
Normal file
48
src/pages/Dashboard.jsx
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Main dashboard page
|
||||
*/
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
const Dashboard = ({ messageHandlers, setMessageHandlers, sendMsg }) => {
|
||||
const [motorStatus, setMotorStatus] = useState({});
|
||||
const [encoderStatus, setEncoderStatus] = useState({});
|
||||
const [ccdStatus, setCcdStatus] = useState({});
|
||||
|
||||
const onMessageArrived = (msg) => {
|
||||
};
|
||||
|
||||
setMessageHandlers({
|
||||
...messageHandlers,
|
||||
dashboard: onMessageArrived,
|
||||
});
|
||||
|
||||
const getMotorStatus = () => {
|
||||
data = {
|
||||
"type": "motor",
|
||||
"command": "get_status",
|
||||
};
|
||||
|
||||
sendMsg("/spectrometer/motor/getStatus", data);
|
||||
};
|
||||
|
||||
const getEncoderStatus = () => {
|
||||
data = {
|
||||
"type": "encoder",
|
||||
"command": "get_status",
|
||||
};
|
||||
|
||||
sendMsg("/spectrometer/encoder/getStatus", data);
|
||||
};
|
||||
|
||||
const getCcdStatus = () => {
|
||||
data = {
|
||||
"type": "ccd",
|
||||
"command": "get_status",
|
||||
};
|
||||
|
||||
sendMsg("/spectrometer/ccd/getStatus", data);
|
||||
};
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
103
src/pages/Login.jsx
Normal file
103
src/pages/Login.jsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { Card, Button, Form, Input, Row, Col, Space } from 'antd'
|
||||
|
||||
const ConnectForm = ({ connect, btnContent }) => {
|
||||
const [form] = Form.useForm()
|
||||
const initialConnectionOptions = {
|
||||
// ws or wss
|
||||
protocol: 'ws',
|
||||
host: 'fwmari.net',
|
||||
clientId: `sp_${Math.random().toString(16).substring(2, 8)}`,
|
||||
// ws -> 8083; wss -> 8084
|
||||
port: 8883,
|
||||
/**
|
||||
* By default, EMQX allows clients to connect without authentication.
|
||||
* https://docs.emqx.com/en/enterprise/v4.4/advanced/auth.html#anonymous-login
|
||||
*/
|
||||
username: 'test',
|
||||
password: '31415926',
|
||||
}
|
||||
|
||||
const onFinish = (values) => {
|
||||
const { host, clientId, port, username, password } = values
|
||||
const url = `ws://${host}:${port}/mqtt`
|
||||
const options = {
|
||||
clientId,
|
||||
username,
|
||||
password,
|
||||
clean: true,
|
||||
reconnectPeriod: 1000, // ms
|
||||
connectTimeout: 30 * 1000, // ms
|
||||
}
|
||||
connect(url, options)
|
||||
}
|
||||
|
||||
const handleConnect = () => {
|
||||
form.submit()
|
||||
}
|
||||
|
||||
const ConnectionForm = (
|
||||
<Form
|
||||
layout="vertical"
|
||||
name="basic"
|
||||
form={form}
|
||||
initialValues={initialConnectionOptions}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<Row gutter={20}>
|
||||
<Col span={8}>
|
||||
<Form.Item label="Host" name="host">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="Port" name="port">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="Client ID" name="clientId">
|
||||
<Input disabled={true} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="Username" name="username">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="Password" name="password">
|
||||
<Input type='password' />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
)
|
||||
|
||||
return (
|
||||
<Space
|
||||
align='center'
|
||||
style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}
|
||||
>
|
||||
<Card
|
||||
title={<h2>Please, connect before using the system</h2>}
|
||||
actions={[
|
||||
<Button type="primary" onClick={handleConnect} key={'Connect'} ghost>
|
||||
{btnContent}
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
{ConnectionForm}
|
||||
</Card>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
|
||||
ConnectForm.propTypes = {
|
||||
connect: PropTypes.func.isRequired,
|
||||
btnContent: PropTypes.oneOfType([
|
||||
PropTypes.element, PropTypes.string,
|
||||
]).isRequired
|
||||
};
|
||||
|
||||
export default ConnectForm
|
||||
66
src/pages/Spectrum.jsx
Normal file
66
src/pages/Spectrum.jsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Button, Card, Form, Input, Layout, Radio, theme } from "antd";
|
||||
import { useState } from "react";
|
||||
|
||||
const { Content, Sider } = Layout;
|
||||
|
||||
const Spectrum = () => {
|
||||
const {
|
||||
token: { colorBgContainer },
|
||||
} = theme.useToken();
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const [mode, setMode] = useState('Angle');
|
||||
|
||||
const initialValues = {
|
||||
mode: "Angle",
|
||||
};
|
||||
|
||||
const onFormChange = ({ mode }) => {
|
||||
setMode(mode);
|
||||
};
|
||||
|
||||
const angleForm = (
|
||||
<Form
|
||||
form={form}
|
||||
onValuesChange={onFormChange}
|
||||
initialValues={initialValues}
|
||||
layout="horizontal"
|
||||
size="small"
|
||||
>
|
||||
<Form.Item label="Mode" name="mode">
|
||||
<Radio.Group value={mode}>
|
||||
<Radio.Button value="Angle">Angle</Radio.Button>
|
||||
<Radio.Button value="Wavelength">Wavelength</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={`Target ${mode} (${mode == "Angle" ? '°' : 'Å'})`}>
|
||||
<Input placeholder="30.127" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
||||
return (
|
||||
<Layout style={{ height: 500 }}>
|
||||
<Content>
|
||||
<h2>kk eae men</h2>
|
||||
</Content>
|
||||
|
||||
<Sider width={300} style={{ background: colorBgContainer }}>
|
||||
<Card
|
||||
title="Motor control"
|
||||
size="small"
|
||||
actions={[
|
||||
<Button type="primary" key='sendAngle' size="small" ghost>
|
||||
Send
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
{angleForm}
|
||||
</Card>
|
||||
</Sider>
|
||||
</Layout>
|
||||
)
|
||||
};
|
||||
|
||||
export default Spectrum;
|
||||
Reference in New Issue
Block a user