init
This commit is contained in:
436
src/App.jsx
Normal file
436
src/App.jsx
Normal file
@@ -0,0 +1,436 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button, Input, Card, notification, Space, Typography, Alert, Row, Col } from 'antd';
|
||||
import { ReadOutlined, EditOutlined, DeleteOutlined, WifiOutlined } from '@ant-design/icons';
|
||||
import mqtt from 'mqtt';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
const { TextArea } = Input;
|
||||
|
||||
function App() {
|
||||
const [lockNo, setLockNo] = useState('1');
|
||||
const [startTime, setStartTime] = useState(dayjs().format('YYYY-MM-DDTHH:mm'));
|
||||
const [endTime, setEndTime] = useState(
|
||||
dayjs().add(1, 'day').set('hour', 12).set('minute', 0).format('YYYY-MM-DDTHH:mm')
|
||||
);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [connectionError, setConnectionError] = useState('');
|
||||
const [client, setClient] = useState(null);
|
||||
const [receivedMessages, setReceivedMessages] = useState('No messages received yet');
|
||||
|
||||
// MQTT connection options
|
||||
const mqttOptions = {
|
||||
host: '117.102.231.130',
|
||||
port: 7001,
|
||||
// protocol: "mqtt",
|
||||
username: 'morekmorekmorek',
|
||||
password: 'morek888',
|
||||
// connectTimeout: 5000,
|
||||
// reconnectPeriod: 5000,
|
||||
};
|
||||
// Update end time when start time changes
|
||||
useEffect(() => {
|
||||
const start = dayjs(startTime);
|
||||
const newEndTime = start.add(1, 'day').set('hour', 12).set('minute', 0).set('second', 0);
|
||||
setEndTime(newEndTime.format('YYYY-MM-DDTHH:mm'));
|
||||
}, [startTime]);
|
||||
|
||||
// Connect to MQTT broker - and keep connection alive
|
||||
useEffect(() => {
|
||||
let mqttClient;
|
||||
|
||||
const connectToMqtt = () => {
|
||||
try {
|
||||
const connectUrl = `mqtt://${mqttOptions.host}:${mqttOptions.port}`;
|
||||
console.log('Attempting to connect to:', connectUrl);
|
||||
|
||||
mqttClient = mqtt.connect(connectUrl, {
|
||||
username: mqttOptions.username,
|
||||
password: mqttOptions.password,
|
||||
clientId: 'mqttjs_' + Math.random().toString(16).substring(2, 8),
|
||||
connectTimeout: mqttOptions.connectTimeout,
|
||||
reconnectPeriod: mqttOptions.reconnectPeriod,
|
||||
clean: false,
|
||||
keepalive: 30,
|
||||
});
|
||||
|
||||
setClient(mqttClient);
|
||||
|
||||
mqttClient.on('connect', () => {
|
||||
console.log('Connected to MQTT broker');
|
||||
setIsConnected(true);
|
||||
setConnectionError('');
|
||||
notification.success({
|
||||
message: 'MQTT Connected',
|
||||
description: 'Successfully connected to MQTT broker',
|
||||
duration: 2,
|
||||
});
|
||||
|
||||
mqttClient.subscribe('hotel1/pc1', (err) => {
|
||||
if (err) {
|
||||
console.error('Subscription error:', err);
|
||||
} else {
|
||||
console.log('Subscribed to topic: hotel1/pc1');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
mqttClient.on('error', (err) => {
|
||||
console.error('MQTT connection error:', err);
|
||||
setConnectionError(err.message);
|
||||
setIsConnected(false);
|
||||
});
|
||||
|
||||
mqttClient.on('close', () => {
|
||||
console.log('MQTT connection closed');
|
||||
setIsConnected(false);
|
||||
});
|
||||
|
||||
mqttClient.on('reconnect', () => {
|
||||
console.log('Attempting to reconnect to MQTT broker');
|
||||
setIsConnected(false);
|
||||
setConnectionError('Reconnecting...');
|
||||
});
|
||||
|
||||
mqttClient.on('offline', () => {
|
||||
console.log('MQTT client is offline');
|
||||
setIsConnected(false);
|
||||
setConnectionError('Offline - attempting to reconnect');
|
||||
});
|
||||
|
||||
mqttClient.on('message', (topic, message) => {
|
||||
console.log('Received message:', topic, message.toString());
|
||||
const newMessage = `[${new Date().toLocaleTimeString()}] ${topic}: ${message.toString()}\n`;
|
||||
setReceivedMessages(prev => prev === 'No messages received yet' ? newMessage : prev + newMessage);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to initialize MQTT client:', err);
|
||||
setConnectionError(err.message);
|
||||
setTimeout(connectToMqtt, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
connectToMqtt();
|
||||
|
||||
}, []);
|
||||
|
||||
const handleStartTimeChange = (e) => {
|
||||
const newStartTime = e.target.value;
|
||||
setStartTime(newStartTime);
|
||||
};
|
||||
|
||||
const handleEndTimeChange = (e) => {
|
||||
const newEndTime = e.target.value;
|
||||
const start = dayjs(startTime);
|
||||
const end = dayjs(newEndTime);
|
||||
|
||||
if (end.isBefore(start)) {
|
||||
notification.error({
|
||||
message: 'Invalid End Time',
|
||||
description: 'End time cannot be before start time',
|
||||
duration: 3,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setEndTime(newEndTime);
|
||||
};
|
||||
|
||||
// Format datetime to YYYY-MM-DD HH:mm:ss
|
||||
const formatDateTime = (dateTimeString) => {
|
||||
return dayjs(dateTimeString).format('YYYY-MM-DD HH:mm:ss');
|
||||
};
|
||||
|
||||
const handleRead = () => {
|
||||
if (client && isConnected) {
|
||||
const payload = JSON.stringify({ cmd: "cek_kartu" });
|
||||
client.publish('hotel1/pc1', payload);
|
||||
notification.info({
|
||||
message: 'Read Command Sent',
|
||||
description: `Published: ${payload}`,
|
||||
duration: 2,
|
||||
});
|
||||
} else {
|
||||
notification.warning({
|
||||
message: 'Not Connected',
|
||||
description: 'Cannot publish message - not connected to MQTT',
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleWrite = () => {
|
||||
if (client && isConnected) {
|
||||
if (!lockNo || !startTime || !endTime) {
|
||||
notification.error({
|
||||
message: 'Validation Error',
|
||||
description: 'Please fill in all fields',
|
||||
duration: 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const start = dayjs(startTime);
|
||||
const end = dayjs(endTime);
|
||||
|
||||
if (end.isBefore(start)) {
|
||||
notification.error({
|
||||
message: 'Validation Error',
|
||||
description: 'End time must be after start time',
|
||||
duration: 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Format the datetime values before sending
|
||||
const formattedStartTime = formatDateTime(startTime);
|
||||
const formattedEndTime = formatDateTime(endTime);
|
||||
|
||||
const payload = JSON.stringify({
|
||||
cmd: "tulis_kartu",
|
||||
lock_no: parseInt(lockNo),
|
||||
start_time: formattedStartTime,
|
||||
end_time: formattedEndTime
|
||||
});
|
||||
|
||||
client.publish('hotel1/pc1', payload);
|
||||
notification.info({
|
||||
message: 'Write Command Sent',
|
||||
description: `Published: ${payload}`,
|
||||
duration: 2,
|
||||
});
|
||||
} else {
|
||||
notification.warning({
|
||||
message: 'Not Connected',
|
||||
description: 'Cannot publish message - not connected to MQTT',
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
if (client && isConnected) {
|
||||
const payload = JSON.stringify({ cmd: "hapus_kartu" });
|
||||
client.publish('hotel1/pc1', payload);
|
||||
notification.info({
|
||||
message: 'Delete Command Sent',
|
||||
description: `Published: ${payload}`,
|
||||
duration: 2,
|
||||
});
|
||||
} else {
|
||||
notification.warning({
|
||||
message: 'Not Connected',
|
||||
description: 'Cannot publish message - not connected to MQTT',
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const clearMessages = () => {
|
||||
setReceivedMessages('No messages received yet');
|
||||
};
|
||||
|
||||
const disconnectMqtt = () => {
|
||||
if (client) {
|
||||
client.end(false, () => {
|
||||
console.log('Manually disconnected from MQTT');
|
||||
setIsConnected(false);
|
||||
setConnectionError('Manually disconnected');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const reconnectMqtt = () => {
|
||||
if (client) {
|
||||
client.reconnect();
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate min and max values for datetime inputs
|
||||
const minEndTime = startTime;
|
||||
const maxStartTime = endTime;
|
||||
|
||||
return (
|
||||
<div style={{ padding: '24px', minHeight: '100vh', background: '#f0f2f5' }}>
|
||||
<Card
|
||||
style={{
|
||||
maxWidth: 800,
|
||||
margin: '0 auto',
|
||||
boxShadow: '0 4px 8px rgba(0,0,0,0.1)',
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
>
|
||||
<Title level={2} style={{ textAlign: 'center', color: '#1890ff' }}>
|
||||
Lock Management System
|
||||
</Title>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '16px', justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<WifiOutlined
|
||||
style={{
|
||||
color: isConnected ? '#52c41a' : '#ff4d4f',
|
||||
fontSize: '20px',
|
||||
marginRight: '8px'
|
||||
}}
|
||||
/>
|
||||
<Text strong>
|
||||
Status: {isConnected ? 'Connected' : 'Disconnected'}
|
||||
</Text>
|
||||
</div>
|
||||
<Space>
|
||||
<Button size="small" onClick={reconnectMqtt} disabled={isConnected}>
|
||||
Reconnect
|
||||
</Button>
|
||||
<Button size="small" danger onClick={disconnectMqtt} disabled={!isConnected}>
|
||||
Disconnect
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
{connectionError && (
|
||||
<Alert
|
||||
message="Connection Status"
|
||||
description={connectionError}
|
||||
type={isConnected ? 'success' : 'warning'}
|
||||
showIcon
|
||||
style={{ marginBottom: '16px' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Text strong>Lock Number:</Text>
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
value={lockNo}
|
||||
onChange={(e) => setLockNo(e.target.value)}
|
||||
size="large"
|
||||
style={{ marginTop: '8px' }}
|
||||
placeholder="Enter lock number"
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Text strong>Start Time:</Text>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
value={startTime}
|
||||
onChange={handleStartTimeChange}
|
||||
size="large"
|
||||
style={{ marginTop: '8px' }}
|
||||
max={maxStartTime}
|
||||
/>
|
||||
<Text type="secondary" style={{ fontSize: '12px', display: 'block', marginTop: '4px' }}>
|
||||
Will be formatted as: {formatDateTime(startTime)}
|
||||
</Text>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Text strong>End Time:</Text>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
value={endTime}
|
||||
onChange={handleEndTimeChange}
|
||||
size="large"
|
||||
style={{ marginTop: '8px' }}
|
||||
min={minEndTime}
|
||||
/>
|
||||
<Text type="secondary" style={{ fontSize: '12px', display: 'block', marginTop: '4px' }}>
|
||||
Will be formatted as: {formatDateTime(endTime)}
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Space direction="horizontal" style={{ width: '100%', justifyContent: 'center' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<ReadOutlined />}
|
||||
size="large"
|
||||
onClick={handleRead}
|
||||
style={{ background: '#52c41a', borderColor: '#52c41a' }}
|
||||
>
|
||||
Read
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<EditOutlined />}
|
||||
size="large"
|
||||
onClick={handleWrite}
|
||||
>
|
||||
Write
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<DeleteOutlined />}
|
||||
size="large"
|
||||
onClick={handleDelete}
|
||||
danger
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
<div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
|
||||
<Text strong>Received Messages:</Text>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={clearMessages}
|
||||
style={{ marginLeft: '8px' }}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
</div>
|
||||
<TextArea
|
||||
rows={10}
|
||||
value={receivedMessages}
|
||||
readOnly
|
||||
style={{
|
||||
backgroundColor: '#f9f9f9',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '14px'
|
||||
}}
|
||||
placeholder="Messages from MQTT will appear here..."
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
|
||||
<div style={{ marginTop: '24px' }}>
|
||||
<Title level={5}>Connection Information</Title>
|
||||
<Text type="secondary">
|
||||
Broker: {mqttOptions.host}:{mqttOptions.port}<br />
|
||||
Topic: hotel1/pc1<br />
|
||||
Username: {mqttOptions.username}<br />
|
||||
Client ID: {client?.options?.clientId || 'Not connected'}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '16px', padding: '12px', backgroundColor: '#f0f9ff', borderRadius: '6px' }}>
|
||||
<Title level={5}>Usage Instructions</Title>
|
||||
<Text type="secondary">
|
||||
1. Enter lock number (integer)<br />
|
||||
2. Select start time - end time will auto-set to next day at 12:00<br />
|
||||
3. Adjust end time if needed (cannot be before start time)<br />
|
||||
4. Click Write to send JSON: {"{"}lock_no: [value], start_time: "YYYY-MM-DD HH:mm:ss", end_time: "YYYY-MM-DD HH:mm:ss"{"}"}<br />
|
||||
5. Received messages will appear in the text area below
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '16px', padding: '12px', backgroundColor: '#fff7e6', borderRadius: '6px' }}>
|
||||
<Title level={5}>Output Format</Title>
|
||||
<Text type="secondary" style={{ fontFamily: 'monospace' }}>
|
||||
{`{
|
||||
"lock_no": ${lockNo},
|
||||
"start_time": "${formatDateTime(startTime)}",
|
||||
"end_time": "${formatDateTime(endTime)}"
|
||||
}`}
|
||||
</Text>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user