| | |
| | | public class DeviceDiscoveryPersistenceService |
| | | { |
| | | private static final Duration MINIMUM_CANDIDATE_LIFETIME = Duration.ofSeconds(30); |
| | | /** |
| | | * 直连厂商存储的来源,其通道登记清单是长期存在的事实,没有"过期"的对应语义: |
| | | * 候选是否出现在界面上只由"已登记且未删除"决定,这个时间只是让候选一直有效。 |
| | | */ |
| | | static final Instant CANDIDATE_NEVER_EXPIRES = Instant.parse("9999-12-31T23:59:59Z"); |
| | | private final DeviceDiscoveryCandidateStore candidateStore; |
| | | private final TDetectionCabinetMapper detectionCabinetMapper; |
| | | private final TTestMapper testMapper; |
| | |
| | | .map(item -> new CabinetCandidate(item.adapterCode(), item.adapterId(), item.deviceCode(), |
| | | item.devtype(), item.devid(), item.ip(), item.mac(), item.name(), item.manufacturer(), item.model(), |
| | | item.channels().stream().map(channel -> new ChannelCandidate(channel.identity(), false, |
| | | DeviceWorkStatus.UNOBSERVABLE, failedAt)).toList(), failedAt, item.expiresAt())) |
| | | DeviceWorkStatus.UNOBSERVABLE, failedAt, null, channel.lastDataAt())).toList(), |
| | | failedAt, item.expiresAt(), item.lastDataAt())) |
| | | .toList()); |
| | | detectionCabinetMapper.markRegisteredFactsUnobservable(adapterCode); |
| | | detectionCabinetMapper.markRegisteredCabinetsOffline(adapterCode); |
| | |
| | | LinkedHashMap::new, java.util.stream.Collectors.toList())); |
| | | Duration lifetime = Duration.ofMillis(Math.max(MINIMUM_CANDIDATE_LIFETIME.toMillis(), |
| | | (long) source.pollIntervalMs() * 3)); |
| | | Instant expiresAt = source.persistentCandidates() ? CANDIDATE_NEVER_EXPIRES : observedAt.plus(lifetime); |
| | | List<CabinetCandidate> candidates = grouped.entrySet().stream() |
| | | .map(entry -> toCandidate(source, entry.getKey(), entry.getValue(), snapshots, |
| | | observedAt, observedAt.plus(lifetime))) |
| | | observedAt, expiresAt)) |
| | | .toList(); |
| | | candidateStore.recordCabinets(candidates); |
| | | if (detected.isEmpty()) markUnavailable(source.adapterCode()); |
| | |
| | | |
| | | /** |
| | | * 发现候选与正式设备是两份事实;正式登记后仍需把成功观测同步到 MySQL。 |
| | | * 离线轮询只更新当前状态,不覆盖最后成功观测时间;暂停期间不会进入此路径。 |
| | | * 暂停期间仍同步设备状态,但不请求该测试的测量;离线不覆盖最后成功观测时间。 |
| | | */ |
| | | private void synchronizeRegisteredFacts(DiscoverySource source, List<DetectedChannel> detected, |
| | | Map<ChannelIdentity, RealtimeSnapshot> snapshots, Instant observedAt) |
| | |
| | | RealtimeSnapshot snapshot = snapshots.get(item.identity()); |
| | | DeviceWorkStatus status = snapshot == null || snapshot.workStatus() == null |
| | | ? DeviceWorkStatus.UNKNOWN : snapshot.workStatus(); |
| | | return new ChannelCandidate(item.identity(), item.available(), status, observedAt, snapshots.get(item.identity())); |
| | | return new ChannelCandidate(item.identity(), item.available(), status, observedAt, |
| | | snapshots.get(item.identity()), item.lastDataAt()); |
| | | }).toList(); |
| | | Instant lastDataAt = channels.stream().map(DetectedChannel::lastDataAt) |
| | | .filter(java.util.Objects::nonNull).max(Instant::compareTo).orElse(null); |
| | | return new CabinetCandidate(source.adapterCode(), source.adapterId(), |
| | | source.adapterCode() + "-" + key.devtype() + "-" + key.devid(), |
| | | key.devtype(), key.devid(), ip, metadata.macAddress(), name, |
| | | metadata.manufacturer(), metadata.model(), channelCandidates, observedAt, expiresAt); |
| | | metadata.manufacturer(), metadata.model(), channelCandidates, observedAt, expiresAt, lastDataAt); |
| | | } |
| | | |
| | | /** 发现来源只携带候选标识和过期计算所需配置,不复用数据库实体。 */ |
| | | public record DiscoverySource(String adapterCode, Long adapterId, String adapterName, int pollIntervalMs) { } |
| | | public record DiscoverySource(String adapterCode, Long adapterId, String adapterName, int pollIntervalMs, |
| | | boolean persistentCandidates) |
| | | { |
| | | /** 兼容按轮询过期计算候选的既有来源。 */ |
| | | public DiscoverySource(String adapterCode, Long adapterId, String adapterName, int pollIntervalMs) |
| | | { |
| | | this(adapterCode, adapterId, adapterName, pollIntervalMs, false); |
| | | } |
| | | } |
| | | |
| | | private record DiscoveryObservation(List<DetectedChannel> channels, |
| | | Map<ChannelIdentity, RealtimeSnapshot> snapshots) { } |