| | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsDeviceProfile; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsHistoryBatch; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsHistoryRecord; |
| | | import com.ruoyi.device.model.DeviceWorkStatus; |
| | | |
| | | /** 将开发模拟器完整状态写入 Redis,主状态不设置普通 TTL。 */ |
| | |
| | | static final String RECENT_REQUESTS_KEY = "dev:bts-simulator:recent-requests:v1"; |
| | | static final String HISTORY_KEYS_KEY = "dev:bts-simulator:collector-history-keys:v1"; |
| | | static final String HISTORY_KEY_PREFIX = "dev:bts-simulator:collector-history:v1:"; |
| | | static final String BTS_HISTORY_KEYS_KEY = "dev:bts-simulator:bts-history-keys:v1"; |
| | | static final String BTS_HISTORY_KEY_PREFIX = "dev:bts-simulator:bts-history:v1:"; |
| | | private static final long RECENT_REQUESTS_TTL_HOURS = 1; |
| | | |
| | | private final RedisTemplate<Object, Object> redisTemplate; |
| | |
| | | @Override |
| | | public SimulatorState resetHistoryForDemoStartup(SimulatorState state) |
| | | { |
| | | if (pendingEmbeddedHistory.isEmpty()) return state; |
| | | java.util.Map<String, Integer> embeddedCounts = new java.util.HashMap<>(); |
| | | pendingEmbeddedHistory.forEach(append -> embeddedCounts.put(append.collectorCode(), append.records().size())); |
| | | List<CollectorState> collectors = state.collectors().stream().map(collector -> { |
| | | int embeddedCount = embeddedCounts.getOrDefault(collector.deviceCode(), 0); |
| | | if (embeddedCount == 0) return collector; |
| | | long remaining = Math.max(0L, collector.historyRecordCount() - embeddedCount); |
| | | return new CollectorState(collector.collectorId(), collector.deviceCode(), collector.ip(), collector.mac(), |
| | | collector.name(), collector.manufacturer(), collector.model(), collector.channelCount(), |
| | | collector.online(), collector.sampleIntervalMs(), collector.nextSequence(), collector.lastSampledAt(), |
| | | collector.channels(), collector.latestMeasurements().stream() |
| | | .filter(item -> collector.deviceCode().equals(item.collectorCode())) |
| | | .toList(), remaining, collector.historyFaults()); |
| | | }).toList(); |
| | | // 不删除独立历史 Redis key;这里只阻止旧内嵌数据在本次启动时迁移回来。 |
| | | pendingEmbeddedHistory = List.of(); |
| | | Set<Object> historyKeys = redisTemplate.opsForSet().members(HISTORY_KEYS_KEY); |
| | | if (historyKeys != null && !historyKeys.isEmpty()) redisTemplate.delete(historyKeys); |
| | | redisTemplate.delete(HISTORY_KEYS_KEY); |
| | | |
| | | List<com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorState> collectors = state.collectors() |
| | | .stream() |
| | | .map(collector -> new com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorState( |
| | | collector.collectorId(), collector.deviceCode(), collector.ip(), collector.mac(), |
| | | collector.name(), collector.manufacturer(), collector.model(), collector.channelCount(), |
| | | collector.online(), collector.sampleIntervalMs(), 1L, |
| | | null, collector.channels(), List.of(), 0L, |
| | | collector.historyFaults())) |
| | | .toList(); |
| | | SimulatorState reset = new SimulatorState(state.version(), state.devices(), collectors, |
| | | state.nextDeviceId(), state.nextCollectorId(), state.tombstones()); |
| | | redisTemplate.opsForValue().set(STATE_KEY, JSON.toJSONString(reset)); |
| | | return reset; |
| | | SimulatorState cleaned = new SimulatorState(state.version(), state.devices(), collectors, state.nextDeviceId(), |
| | | state.nextCollectorId(), state.tombstones()); |
| | | save(cleaned); |
| | | return cleaned; |
| | | } |
| | | |
| | | private SimulatorState restoreState(Object stored) |
| | |
| | | } |
| | | |
| | | @Override |
| | | public BtsHistoryBatch loadBtsHistoryPage(String historyKey, long offset, int limit) |
| | | { |
| | | if (offset < 0 || limit < 1 || limit > MAX_HISTORY_BATCH_SIZE) |
| | | throw new IllegalArgumentException("invalid BTS history page"); |
| | | String key = BTS_HISTORY_KEY_PREFIX + historyKey; |
| | | Long size = redisTemplate.opsForList().size(key); |
| | | long total = size == null ? 0 : size; |
| | | long from = Math.min(offset, total); |
| | | int count = (int) Math.min(limit, total - from); |
| | | List<Object> values = count == 0 ? List.of() : redisTemplate.opsForList().range(key, from, from + count - 1); |
| | | List<BtsHistoryRecord> records = values == null ? List.of() : values.stream() |
| | | .map(item -> JSON.parseObject(item.toString(), BtsHistoryRecord.class)).toList(); |
| | | long next = from + records.size(); |
| | | return new BtsHistoryBatch(offset, next, next < total, total, records); |
| | | } |
| | | |
| | | @Override |
| | | @SuppressWarnings({ "rawtypes", "unchecked" }) |
| | | public void appendBtsHistory(String historyKey, BtsHistoryRecord record) |
| | | { |
| | | String key = BTS_HISTORY_KEY_PREFIX + historyKey; |
| | | RedisOperations<Object, Object> operations = redisTemplate; |
| | | operations.opsForList().rightPush(key, JSON.toJSONString(record)); |
| | | operations.opsForList().trim(key, -MAX_HISTORY_RECORDS, -1); |
| | | operations.opsForSet().add(BTS_HISTORY_KEYS_KEY, key); |
| | | } |
| | | |
| | | @Override |
| | | @SuppressWarnings({ "rawtypes", "unchecked" }) |
| | | public void saveAndDeleteCollectorHistory(SimulatorState state, String collectorCode) |
| | | { |
| | |
| | | Set<Object> historyKeys = redisTemplate.opsForSet().members(HISTORY_KEYS_KEY); |
| | | if (historyKeys != null && !historyKeys.isEmpty()) redisTemplate.delete(historyKeys); |
| | | redisTemplate.delete(HISTORY_KEYS_KEY); |
| | | Set<Object> btsHistoryKeys = redisTemplate.opsForSet().members(BTS_HISTORY_KEYS_KEY); |
| | | if (btsHistoryKeys != null && !btsHistoryKeys.isEmpty()) redisTemplate.delete(btsHistoryKeys); |
| | | redisTemplate.delete(BTS_HISTORY_KEYS_KEY); |
| | | redisTemplate.delete(RECENT_REQUESTS_KEY); |
| | | save(defaultState); |
| | | } |