增加父子设备支持

This commit is contained in:
zhou-hao 2020-03-09 15:45:25 +08:00
parent b693d0bc98
commit a029762367
7 changed files with 84 additions and 43 deletions

View File

@ -81,4 +81,7 @@ public class DeviceInstanceEntity extends GenericEntity<String> implements Recor
@Comment("所属机构id")
private String orgId;
@Column(name = "parent_id", length = 32)
@Comment("父级设备ID")
private String parentId;
}

View File

@ -3,10 +3,7 @@ package org.jetlinks.community.device.entity;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import org.hswebframework.ezorm.rdb.mapping.annotation.ColumnType;
import org.hswebframework.ezorm.rdb.mapping.annotation.Comment;
import org.hswebframework.ezorm.rdb.mapping.annotation.EnumCodec;
import org.hswebframework.ezorm.rdb.mapping.annotation.JsonCodec;
import org.hswebframework.ezorm.rdb.mapping.annotation.*;
import org.hswebframework.web.api.crud.entity.GenericEntity;
import org.hswebframework.web.api.crud.entity.RecordCreationEntity;
import org.hswebframework.web.crud.generator.Generators;
@ -89,16 +86,17 @@ public class DeviceProductEntity extends GenericEntity<String> implements Record
@Comment("产品状态")
@Column(name = "state")
@DefaultValue("0")
private Byte state;
@Column(name = "creator_id")
@Comment("创建者id")
private String creatorId;
@Comment("创建时间")
@Column(name = "create_time")
private Long createTime;
@Column(name = "org_id", length = 32)
@Comment("所属机构id")
private String orgId;

View File

@ -18,5 +18,8 @@ public class DeviceInstanceImportExportEntity {
@ExcelProperty("描述")
private String describe;
@ExcelProperty("父级设备ID")
private String parentId;
}

View File

@ -5,6 +5,7 @@ import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.core.dsl.Query;
import org.hswebframework.ezorm.core.param.QueryParam;
import org.hswebframework.ezorm.core.param.TermType;
@ -17,9 +18,11 @@ import org.hswebframework.web.exception.BusinessException;
import org.hswebframework.web.exception.NotFoundException;
import org.hswebframework.web.logger.ReactiveLogger;
import org.jetlinks.community.device.entity.DeviceOperationLogEntity;
import org.jetlinks.community.device.message.DeviceMessageUtils;
import org.jetlinks.core.device.DeviceConfigKey;
import org.jetlinks.core.device.DeviceOperator;
import org.jetlinks.core.device.DeviceRegistry;
import org.jetlinks.core.message.DeviceMessage;
import org.jetlinks.core.message.DeviceOfflineMessage;
import org.jetlinks.core.message.DeviceOnlineMessage;
import org.jetlinks.core.metadata.DataType;
@ -53,6 +56,7 @@ import reactor.core.publisher.Mono;
import reactor.core.publisher.SignalType;
import reactor.core.scheduler.Schedulers;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuple3;
import reactor.util.function.Tuples;
import javax.annotation.PostConstruct;
@ -65,6 +69,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.jetlinks.community.device.timeseries.DeviceTimeSeriesMetric.devicePropertyMetric;
@ -163,7 +168,9 @@ public class LocalDeviceInstanceService extends GenericReactiveCrudService<Devic
registry.registry(org.jetlinks.core.device.DeviceInfo.builder()
.id(instance.getId())
.productId(instance.getProductId())
.build())
.build()
.addConfig(DeviceConfigKey.parentGatewayId, instance.getParentId())
)
//设置其他配置信息
.flatMap(deviceOperator -> deviceOperator.getState()
.flatMap(r -> {
@ -320,19 +327,8 @@ public class LocalDeviceInstanceService extends GenericReactiveCrudService<Devic
//订阅设备上下线
FluxUtils.bufferRate(messageGateway
.subscribe("/device/*/online", "/device/*/offline")
.flatMap(message -> Mono.fromCallable(() -> {
if (message.getMessage() instanceof EncodableMessage) {
Object msg = ((EncodableMessage) message.getMessage()).getNativePayload();
if (msg instanceof DeviceOnlineMessage) {
return ((DeviceOnlineMessage) msg).getDeviceId();
}
if (msg instanceof DeviceOfflineMessage) {
return ((DeviceOfflineMessage) msg).getDeviceId();
}
}
return null;
})), 800, 200, Duration.ofSeconds(2))
.flatMap(message -> Mono.justOrEmpty(DeviceMessageUtils.convert(message))
.map(DeviceMessage::getDeviceId)), 800, 200, Duration.ofSeconds(2))
.flatMap(list -> syncStateBatch(Flux.just(list), false).count())
.onErrorContinue((err, obj) -> log.error(err.getMessage(), err))
.subscribe((i) -> log.info("同步设备状态成功:{}", i));
@ -347,30 +343,50 @@ public class LocalDeviceInstanceService extends GenericReactiveCrudService<Devic
}
public Flux<Integer> syncStateBatch(Flux<List<String>> batch, boolean force) {
return batch.flatMap(list -> Flux.fromIterable(list)
.flatMap(registry::getDevice)
.publishOn(Schedulers.parallel())
.flatMap(operation -> {
if (force) {
return operation.checkState().zipWith(Mono.just(operation.getDeviceId()));
}
return operation.getState().zipWith(Mono.just(operation.getDeviceId()));
})
.groupBy(Tuple2::getT1, Tuple2::getT2)
.flatMap(group -> {
@SuppressWarnings("all")
DeviceState state = group.key() == null ? DeviceState.offline : DeviceState.of(group.key());
return group.collectList()
.flatMap(idList -> getRepository()
.createUpdate()
.set(DeviceInstanceEntity::getState, state)
.where()
.in(DeviceInstanceEntity::getId, idList)
.execute());
}));
return batch
.flatMap(list -> Flux.fromIterable(list)
.flatMap(registry::getDevice)
.publishOn(Schedulers.parallel())
.flatMap(operation -> {
Mono<Byte> state = force ? operation.checkState() : operation.getState();
return Mono.zip(
state,//状态
Mono.just(operation.getDeviceId()), //设备id
operation.getConfig(DeviceConfigKey.isGatewayDevice)//是否为网关设备
);
})
.groupBy(Tuple2::getT1, Function.identity())
.flatMap(group -> {
@SuppressWarnings("all")
DeviceState state = group.key() == null ? DeviceState.offline : DeviceState.of(group.key());
return group
.collectList()
.flatMap(idList -> Mono.zip(
//修改设备状态
getRepository()
.createUpdate()
.set(DeviceInstanceEntity::getState, state)
.where()
.in(DeviceInstanceEntity::getId, idList.stream().map(Tuple3::getT2).collect(Collectors.toList()))
.execute(),
//修改子设备状态
Flux.fromIterable(idList)
.filter(Tuple3::getT3)
.map(Tuple3::getT2)
.collectList()
.filter(CollectionUtils::isNotEmpty)
.flatMap(parents ->
getRepository()
.createUpdate()
.set(DeviceInstanceEntity::getState, state)
.where()
.in(DeviceInstanceEntity::getParentId, parents)
.execute()
).defaultIfEmpty(0)
, Math::addExact));
}));
}
public Flux<ImportDeviceInstanceResult> doBatchImport(String fileUrl) {
return deviceProductService
.createQuery()

View File

@ -3,6 +3,7 @@ package org.jetlinks.community.device.service;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.web.bean.FastBeanCopier;
import org.hswebframework.web.crud.service.GenericReactiveCrudService;
import org.jetlinks.core.device.DeviceConfigKey;
import org.jetlinks.core.device.DeviceRegistry;
import org.jetlinks.core.device.ProductInfo;
import org.jetlinks.community.device.entity.DeviceProductEntity;
@ -14,6 +15,8 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import static org.jetlinks.community.device.enums.DeviceType.gateway;
@Service
@Slf4j
public class LocalDeviceProductService extends GenericReactiveCrudService<DeviceProductEntity, String> {
@ -26,7 +29,13 @@ public class LocalDeviceProductService extends GenericReactiveCrudService<Device
public Mono<Integer> deploy(String id) {
return findById(Mono.just(id))
.flatMap(product -> registry.registry(new ProductInfo(id, product.getMessageProtocol(), product.getMetadata()))
.flatMap(product -> registry.registry(
ProductInfo.builder()
.id(id)
.protocol(product.getMessageProtocol())
.metadata(product.getMetadata())
.build()
.addConfig(DeviceConfigKey.isGatewayDevice, product.getDeviceType() == gateway))
.flatMap(deviceProductOperator -> deviceProductOperator.setConfigs(product.getConfiguration()))
.flatMap(re -> createUpdate()
.set(DeviceProductEntity::getState, DeviceProductState.registered.getValue())

View File

@ -232,7 +232,7 @@ public class DefaultDeviceSessionManager implements DeviceSessionManager {
.switchIfEmpty(Mono.fromRunnable(() -> log.warn("children device [{}] not fond in registry", childrenDeviceId)))
.flatMap(deviceOperator -> deviceOperator
.online(session.getServerId().orElse(serverId), session.getId())
.then(deviceOperator.setConfig(DeviceConfigKey.parentMeshDeviceId, deviceId))
.then(deviceOperator.setConfig(DeviceConfigKey.parentGatewayId, deviceId))
.thenReturn(new ChildrenDeviceSession(childrenDeviceId, session, deviceOperator)))
.doOnSuccess(s -> children.computeIfAbsent(deviceId, __ -> new ConcurrentHashMap<>()).put(childrenDeviceId, s));
});

View File

@ -54,6 +54,18 @@ simulator.bindHandler("/read-property", function (message, session) {
}));
});
simulator.bindHandler("/children/read-property", function (message, session) {
_logger.info("读取子设备属性:[{}]", message);
session.sendMessage("/children/read-property-reply", JSON.stringify({
messageId: message.messageId,
deviceId: message.deviceId,
timestamp: new Date().getTime(),
properties: {"temperature": java.util.concurrent.ThreadLocalRandom.current().nextInt(20, 30)},
success: true
}));
});
simulator.bindHandler("/invoke-function", function (message, session) {
_logger.info("调用功能:[{}]", message);
session.sendMessage("/invoke-function", JSON.stringify({