【MQTT】MQTT实现前端与软件交互:

2 篇文章 0 订阅
该文展示了一个基于Vue.js的地磅监控界面,包括西地磅和东地磅的状态、车辆信息、过磅控制以及MQTT连接用于接收和发送地磅数据。系统使用Vuex进行状态管理,通过HTTP接口进行命令下发,并集成MQTT协议进行实时通信。
摘要由CSDN通过智能技术生成


一、效果:

在这里插入图片描述

二、实现:
<template>
	<a-card :bordered="false" class="terminalsettings">
		<!-- 上部分 -->
		<div class="topBox">
			<div class="topBox-box">
				<!-- 西地磅 -->
				<div class="box">
					<h3>西地磅</h3>
					<section>
						<div>
							地磅状态
							<img src="./img/red.png" alt="" class="content-img" v-show="WastStationIdInfo.stationStatus == 2" />
							<img src="./img/green.png" alt="" class="content-img" v-show="WastStationIdInfo.stationStatus == 1" />
						</div>
						<div class="content-span">
							<span>
								进磅
								<img src="./img/red.png" alt="" class="content-img" v-show="WastStationIdInfo.enterStatus == 2" />
								<img src="./img/green.png" alt="" class="content-img" v-show="WastStationIdInfo.enterStatus == 1" />
							</span>
							<span>
								出磅
								<img src="./img/red.png" alt="" class="content-img" v-show="WastStationIdInfo.leaveStatus == 2" />
								<img src="./img/green.png" alt="" class="content-img" v-show="WastStationIdInfo.leaveStatus == 1" />
							</span>
						</div>
						<div class="content-input">车牌<a-input v-model="WastStationIdInfo.carNumber" disabled /></div>
						<div class="content-input">称重<a-input v-model="WastStationIdInfo.weight" disabled />(kg)</div>
						<div class="content-input">车道编号<a-input v-model="WastStationIdInfo.laneNum" disabled /></div>
						<div>
							<div class="content-tipTitle"
								v-if="WastStationIdInfo.contentList && WastStationIdInfo.contentList.length > 0">
								<p v-for="(item, i) in WastStationIdInfo.contentList" :key="i">{{ item }}</p>
							</div>
							<div class="content-tipTitle" v-else>
								<p>{{ $store.state.settings.PLATFORM_NAME }}</p>
								<p>无人值守地磅</p>
								<p>欢迎光临</p>
							</div>
							<p class="content-p">汽车衡</p>
						</div>
						<div class="content-radioGroup">
							<a-radio-group v-model="WastStationIdInfo.orderData">
								<a-radio :style="radioStyle" :value="'2'">禁止过磅</a-radio>
								<a-radio :style="radioStyle" :value="'1'">允许过磅</a-radio>
							</a-radio-group>
						</div>
						<div class="content-button">
							<a-button type="primary" @click="connecter()">刷新</a-button>
							<a-button type="danger"
								@click="handleOrderData(WastStationIdInfo.stationId, WastStationIdInfo.orderData, WastStationIdInfo.laneNum)">下发命令</a-button>
						</div>
					</section>
				</div>

				<!-- 东地磅 -->
				<div class="box">
					<h3>东地磅</h3>
					<section>
						<div>
							地磅状态
							<img src="./img/red.png" alt="" class="content-img" v-show="EastStationIdInfo.stationStatus == 2" />
							<img src="./img/green.png" alt="" class="content-img" v-show="EastStationIdInfo.stationStatus == 1" />
						</div>
						<div class="content-span">
							<span>
								进磅
								<img src="./img/red.png" alt="" class="content-img" v-show="EastStationIdInfo.enterStatus == 2" />
								<img src="./img/green.png" alt="" class="content-img" v-show="EastStationIdInfo.enterStatus == 1" />
							</span>
							<span>
								出磅
								<img src="./img/red.png" alt="" class="content-img" v-show="EastStationIdInfo.leaveStatus == 2" />
								<img src="./img/green.png" alt="" class="content-img" v-show="EastStationIdInfo.leaveStatus == 1" />
							</span>
						</div>
						<div class="content-input">车牌<a-input v-model="EastStationIdInfo.carNumber" disabled /></div>
						<div class="content-input">称重<a-input v-model="EastStationIdInfo.weight" disabled />(kg)</div>
						<div class="content-input">车道编号<a-input v-model="EastStationIdInfo.laneNum" disabled /></div>
						<div>
							<div class="content-tipTitle"
								v-if="EastStationIdInfo.contentList && EastStationIdInfo.contentList.length > 0">
								<p v-for="(item, i) in EastStationIdInfo.contentList" :key="i">{{ item }}</p>
							</div>
							<div class="content-tipTitle" v-else>
								<p>{{ $store.state.settings.PLATFORM_NAME }}</p>
								<p>无人值守地磅</p>
								<p>欢迎光临</p>
							</div>
							<p class="content-p">汽车衡</p>
						</div>
						<div class="content-radioGroup">
							<a-radio-group v-model="EastStationIdInfo.orderData">
								<a-radio :style="radioStyle" :value="'2'">禁止过磅</a-radio>
								<a-radio :style="radioStyle" :value="'1'">允许过磅</a-radio>
							</a-radio-group>
						</div>
						<div class="content-button">
							<a-button type="primary" @click="connecter()">刷新</a-button>
							<a-button type="danger"
								@click="handleOrderData(EastStationIdInfo.stationId, EastStationIdInfo.orderData, EastStationIdInfo.laneNum)">下发命令</a-button>
						</div>
					</section>
				</div>
			</div>
		</div>
	</a-card>
</template>

<script>
import { mapState } from 'vuex';
import '@/assets/less/TableExpand.less'
import { mixinDevice } from '@/utils/mixin'
import { httpAction, getAction, httpActions } from '@/api/manage'
import { filterMultiDictText, filterDictTextByCache } from '@/components/dict/JDictSelectUtil'
import mqtt from './mqtt.js';

export default {
	name: 'terminalsettings',
	mixins: [mixinDevice],
	data() {
		return {
			//西地磅
			WastStationIdInfo: {
				orderData: '1',
				laneNum: '1'
			},
			//东地磅
			EastStationIdInfo: {
				orderData: '1',
				laneNum: '2'
			},

			deptId: null,
			stationId: null,
			
			radioStyle: {
				display: 'block',
				height: '30px',
				lineHeight: '30px',
			},
			url: {
				list: "/kd/actual/getActual",//实时数据
				edit: "/kd/stationOrder/submitOrder",//下发命令
				getOrder: "/kd/stationOrder/getOrder",//查询磅点指令

				firstweightInfo: "/kd/weightInfo/findInfo",//首次过磅列表
				secondweightInfo: "/kd/weightInfo/findInfo",//首次过磅列表
			},

			// 主题:mqtt/{车道编号}/{站点编号}/ledContent
			// 地址:
			// 端口号:8083
			// 用户名:
			// 密码:
			serveMqtt: 'ws://地址:端口号/mqtt',
			sendMassage: {},
			receiveMessage: {}, //收到的推送消息
			client: null,
			//MQTT连接的配置
			options: {
				// wsOptions: {},
				// protocolVersion: 4, //MQTT连接协议版本
				clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
				keepalive: 60,
				clean: false,
				username: 用户名,
				password: 密码,
				reconnectPeriod: 3000, //1000毫秒,两次重新连接之间的间隔
				connectTimeout: 30 * 1000, //1000毫秒,两次重新连接之间的间隔
				resubscribe: true //如果连接断开并重新连接,则会再次自动订阅已订阅的主题(默认true)
			},
		};
	},
	computed: {
		...mapState(['bdidInfo']),
	},
	watch: {
		// 监听机构和磅点bdidInfo存储信息的变化
		bdidInfo: {
			handler(value) {
				if (value != null) {
					this.deptId = value.deptId
					this.stationId = value.stationId
					this.sendMassage.stationId = value.stationId
				}
			},
			immediate: true
		}
	},
	mounted() {
		this.connecter()
	},
	methods: {
		handleOrderData(stationId, orderData, laneNum) {
			const that = this
			if (!laneNum || laneNum == null || laneNum == undefined) {
				that.$message.warning("车道编号不能为空");
				return
			} else if (!orderData || orderData == null || orderData == undefined) {
				that.$message.warning("指令不能为空");
				return
			}
			var param = {
				stationId: that.sendMassage.stationId || stationId,
				orderData: orderData,//指令。1=允许过磅,2=禁止过磅
				laneNum: laneNum,//车道编号
			}
			httpActions(this.url.edit, param, 'POST').then((res) => {
				if (res.success) {
					that.$message.success(res.message);
				} else {
					that.$message.warning(res.message);
				}
			})
		},


		/* 
			mqtt 推送
		*/
		connecter: function () {			// 连接
			const that = this;
			if (that.client == null || that.client.connented == false) {
				that.$message.loading({ content: '连接中···', duration: 3 });

				// 连接
				that.client = mqtt.connect(that.serveMqtt, that.options);
				that.client.on('connect', () => {
					that.$message.success({ content: '连接成功!' });
					that.subscribe();
				});

				that.client.on('message', (topic, message) => {
					console.log('收到来自' + topic + '的消息' + message.toString());
					that.receiveMessage = JSON.parse(message);
					console.log("receiveMessage:", that.receiveMessage)
					if (!(typeof that.receiveMessage == 'object') || Object.keys(that.receiveMessage).length == 0) {
						that.WastStationIdInfo = { orderData: '1', laneNum: '1' }
						that.EastStationIdInfo = { orderData: '1', laneNum: '2' }
						return
					}
					that.receiveMessage.contentList = that.receiveMessage.contentList && that.receiveMessage.contentList.length > 0 ? that.receiveMessage.contentList.split(",") : []
					if (!that.receiveMessage.laneNum) {
						that.$message.error("MQTT获取到的车道编号为空")
						return
					}
					if (that.receiveMessage.laneNum == 1) {// 西地磅
						that.WastStationIdInfo = { ...that.receiveMessage }
						console.log("西地磅", that.WastStationIdInfo)
					} else {// 东地磅
						that.EastStationIdInfo = { ...that.receiveMessage }
						console.log("东地磅", that.EastStationIdInfo)
					}
				});
			}
			// 重连
			that.client.on('reconnect', error => {
				that.$message.loading({ content: '加载中...', duration: 2 });
				setTimeout(() => {
					that.$message.warning({ content: "正在重连···" });
				}, 2000);
			});

			// 连接失败
			that.client.on('error', error => {
				that.$message.error({ content: '连接失败!' + error });
			});
		},
		//订阅
		subscribe: function () {
			const that = this;
			if (!that.client || !that.client.connected) {// 判断是否已成功连接
				that.$message.error({ content: '客户端未连接!' });
				that.connecter()
				return;
			}

			// 订阅主题(网页接收主题)
			// mqtt/磅点ID+车道/ReceivedCarNumber  测试主题: let Topic = 'mqtt/1/10086/ledContent'; // mqtt/磅点ID+车道/ReceivedCarNumber
			let Topic1 = 'mqtt/1/' + that.sendMassage.stationId + '/ledContent';
			let Topic2 = 'mqtt/2/' + that.sendMassage.stationId + '/ledContent';
			let Topic3 = 'mqtt/3/' + that.sendMassage.stationId + '/ledContent';
			let Topic4 = 'mqtt/4/' + that.sendMassage.stationId + '/ledContent';

			// 【1】[Topic1, Topic2]:订阅多个主题。
			// 【2】QoS: (1)0:消息最多传递一次,如果当时客户端不可用,则会丢失该消息;(2)1:消息传递至少 1 次;(3)2:消息仅传送一次。
			that.client.subscribe([Topic1, Topic2, Topic3, Topic4], { qos: 0 }, error => {
				if (!error) {
					that.$message.loading({ content: '加载中...', duration: 2 });
					setTimeout(() => {
						that.$message.success({ content: '订阅成功' });
					}, 2000);
				} else {
					that.$message.error({ content: '订阅失败!' + error });
				}
			});

			// 订阅多个主题 称重信息   支付信息 
			// this.client.subscribe(['mqtt/' + 317522 + '/weight', 'mqtt/' + 317522 + '/pay '], { qos: this.Qos }, err => {
			// 	console.log(err || '订阅成功');
			// 	// this.show(err || '订阅成功');
			// });

			// 订阅不同 qos 的不同主题
			// this.client.subscribe([Topic, Topic1], (error) => {
			// 	if (!error) {
			// 		that.$message.loading({ content: '加载中...' });
			// 		setTimeout(() => {
			// 			that.$message.success({ content: '订阅成功', duration: 2 });
			// 		}, 1000);
			// 	} else {
			// 		console.log(error)
			// 	}
			// });
		},
		// 推送消息
		publish: function () {
			const that = this;
			if (!that.client || !that.client.connected) {// 判断是否已成功连接
				that.$message.error({ content: '客户端未连接!' });
				that.connecter()
				return;
			}
			if (Object.keys(that.sendMassage).length > 0) {
				const that = this
				if (!that.sendMassage.laneNum || that.sendMassage.laneNum == null || that.sendMassage.laneNum == undefined) {
					that.$message.warning("车道编号不能为空");
					return
				} else if (!that.sendMassage.orderData || that.sendMassage.orderData == null || that.sendMassage.orderData == undefined) {
					that.$message.warning("指令不能为空");
					return
				}
				let Topic = 'mqtt/' + that.sendMassage.laneNum + '/' + that.sendMassage.orderData + '/' + that.sendMassage.stationId + '/ledContent'; // mqtt/磅点ID+车道/ReceivedCarNumber  网页推送(发布)主题
				let sendInfo = JSON.stringify(that.sendMassage);
				that.client.publish(Topic, sendInfo, error => {
					if (!error) {
						that.$message.success("指令发送成功");
					} else {
						that.$message.error({ content: '指令发送失败!' + error });
					}
				});
			} else {
				that.$message.error('推送消息为空!');
			}
		},
	}
};
</script>

<style lang="less" scoped>
.terminalsettings {
	display: flex;
	flex-direction: column;

	.topBox {
		border: 1px solid lightgray;
		box-sizing: border-box;

		.noResult {
			height: 180px;
			display: flex;
			align-items: center;
			justify-content: center;
		}

		.topBox-box {
			display: grid;
			grid: auto / auto auto auto auto;
		}

		.box {
			border-right: 1px solid lightgray;
			box-sizing: border-box;

			&:nth-child(4n + 4) {
				border-right: unset;
			}

			h3 {
				font-weight: bold;
				margin: 5px 10px;
				padding-left: 5px;
				border-left: 6px solid #1890ff;
			}

			section {
				font-size: 15px;
				color: #000;
				font-weight: 600;
				padding: 10px 30px;

				.content-span {
					margin: 10px 0;

					span {
						&:nth-child(1) {
							padding-right: 20px;
						}
					}
				}

				.content-img {
					width: 20px;
					height: 20px;
					margin-left: 10px;
				}

				.content-input {
					padding: 5px 0;

					/deep/.ant-input {
						margin-left: 30px;
						width: 65%;
						color: #000;
					}
				}

				.content-inputTime {
					padding: 5px 0;

					/deep/.ant-input {
						margin-left: 10px;
						width: 60%;
						color: #000;
					}
				}

				.content-tipTitle {
					width: 100%;
					height: 180px;
					margin-top: 5px;
					padding: 25px 0;
					border-radius: 4px;
					border: 1px solid lightgrey;

					display: flex;
					flex-direction: column;
					align-items: center;
					justify-content: center;

					overflow-y: auto;
					overflow-x: hidden;

					p {
						color: red;
						font-size: 14px;
						font-weight: bold;
						// letter-spacing: 4px;
						padding: 0;
						margin: 0;
					}
				}

				.content-p {
					text-align: center;
					font-weight: normal;
					font-size: 12px;
					margin-top: 4px;
				}

				.content-radioGroup {
					text-align: center;
					font-size: 14px;
					padding: 5px 0 15px 0;
					cursor: pointer;
				}

				.content-button {
					display: flex;
					justify-content: space-evenly;
				}
			}
		}
	}

	.bottomBox {
		margin-top: 24px;
		box-sizing: border-box;
	}
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sun Peng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值