|
@@ -0,0 +1,99 @@
|
|
|
+package org.jetlinks.community.device.message;
|
|
|
+
|
|
|
+import lombok.AllArgsConstructor;
|
|
|
+import org.hswebframework.web.id.IDGenerator;
|
|
|
+import org.jetlinks.core.device.DeviceRegistry;
|
|
|
+import org.jetlinks.core.enums.ErrorCode;
|
|
|
+import org.jetlinks.core.exception.DeviceOperationException;
|
|
|
+import org.jetlinks.core.message.DeviceMessageReply;
|
|
|
+import org.jetlinks.core.message.MessageType;
|
|
|
+import org.jetlinks.core.message.RepayableDeviceMessage;
|
|
|
+import org.jetlinks.community.device.entity.DeviceInstanceEntity;
|
|
|
+import org.jetlinks.community.device.service.LocalDeviceInstanceService;
|
|
|
+import org.jetlinks.community.gateway.external.Message;
|
|
|
+import org.jetlinks.community.gateway.external.SubscribeRequest;
|
|
|
+import org.jetlinks.community.gateway.external.SubscriptionProvider;
|
|
|
+import org.jetlinks.supports.utils.MqttTopicUtils;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import reactor.core.publisher.Flux;
|
|
|
+import reactor.core.publisher.Mono;
|
|
|
+
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+@Component
|
|
|
+@AllArgsConstructor
|
|
|
+public class DeviceMessageSendSubscriptionProvider implements SubscriptionProvider {
|
|
|
+
|
|
|
+ private final DeviceRegistry registry;
|
|
|
+
|
|
|
+ private final LocalDeviceInstanceService instanceService;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String id() {
|
|
|
+ return "device-message-sender";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String name() {
|
|
|
+ return "设备消息发送";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String[] getTopicPattern() {
|
|
|
+ return new String[]{
|
|
|
+ "/device-message-sender/*/*"
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Flux<Message> subscribe(SubscribeRequest request) {
|
|
|
+
|
|
|
+ String topic = request.getTopic();
|
|
|
+
|
|
|
+ Map<String, String> variables = MqttTopicUtils.getPathVariables("/device-message-sender/{productId}/{deviceId}", topic);
|
|
|
+ String deviceId = variables.get("deviceId");
|
|
|
+ String productId = variables.get("productId");
|
|
|
+
|
|
|
+ //发给所有设备
|
|
|
+ if ("*".equals(deviceId)) {
|
|
|
+ return instanceService.createQuery()
|
|
|
+ .select(DeviceInstanceEntity::getId)
|
|
|
+ .where(DeviceInstanceEntity::getProductId, productId)
|
|
|
+ //.and(DeviceInstanceEntity::getState, DeviceState.online)
|
|
|
+ .fetch()
|
|
|
+ .map(DeviceInstanceEntity::getId)
|
|
|
+ .flatMap(id -> doSend(request.getId(), topic, id, new HashMap<>(request.getParameter())));
|
|
|
+ }
|
|
|
+ return Flux.fromArray(deviceId.split("[,]"))
|
|
|
+ .flatMap(id -> doSend(request.getId(), topic, id, new HashMap<>(request.getParameter())));
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public Flux<Message> doSend(String requestId, String topic, String deviceId, Map<String, Object> message) {
|
|
|
+ message.put("messageId", IDGenerator.SNOW_FLAKE_STRING.generate());
|
|
|
+ message.put("deviceId", deviceId);
|
|
|
+
|
|
|
+ RepayableDeviceMessage<?> msg = MessageType.convertMessage(message)
|
|
|
+ .filter(RepayableDeviceMessage.class::isInstance)
|
|
|
+ .map(RepayableDeviceMessage.class::cast)
|
|
|
+ .orElseThrow(() -> new UnsupportedOperationException("不支持的消息格式"));
|
|
|
+ return registry
|
|
|
+ .getDevice(deviceId)
|
|
|
+ .switchIfEmpty(Mono.error(() -> new DeviceOperationException(ErrorCode.CLIENT_OFFLINE)))
|
|
|
+ .flatMapMany(deviceOperator -> deviceOperator
|
|
|
+ .messageSender()
|
|
|
+ .send(Mono.just(msg)))
|
|
|
+ .map(reply -> Message.success(requestId, topic, reply))
|
|
|
+ .onErrorResume(error -> {
|
|
|
+ DeviceMessageReply reply = msg.newReply();
|
|
|
+ if (error instanceof DeviceOperationException) {
|
|
|
+ reply.error(((DeviceOperationException) error).getCode());
|
|
|
+ } else {
|
|
|
+ reply.error(error);
|
|
|
+ }
|
|
|
+ return Mono.just(Message.success(requestId, topic, reply));
|
|
|
+ })
|
|
|
+ ;
|
|
|
+ }
|
|
|
+}
|