package com.ruoyi.system.service.impl;
|
|
import java.time.Duration;
|
import java.time.Instant;
|
import java.time.ZoneId;
|
import java.util.List;
|
import java.util.Locale;
|
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.stereotype.Service;
|
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.device.api.DeviceAdapterException;
|
import com.ruoyi.device.bts.BtsProtocolException;
|
import com.ruoyi.device.bts.BtsTransportException;
|
import com.ruoyi.device.model.AdapterCapabilities;
|
import com.ruoyi.device.registry.AdapterInstanceConfig;
|
import com.ruoyi.device.registry.AdapterRegistryException;
|
import com.ruoyi.device.registry.DeviceAdapterRegistry;
|
import com.ruoyi.system.domain.TDetectionChannel;
|
import com.ruoyi.system.domain.TDeviceAdapter;
|
import com.ruoyi.system.domain.vo.AdapterDiscoveryResult;
|
import com.ruoyi.system.mapper.TDeviceAdapterMapper;
|
import com.ruoyi.system.service.DeviceAdapterErrorCodes;
|
import com.ruoyi.system.service.IDeviceAdapterService;
|
|
@Service
|
public class DeviceAdapterServiceImpl implements IDeviceAdapterService
|
{
|
private static final String ENABLED = "0";
|
private static final String DISABLED = "1";
|
private static final String READ_ONLY_CAPABILITIES =
|
"{\"discovery\":true,\"realtimeObservation\":true,\"historyDownload\":true,\"control\":false}";
|
|
private final TDeviceAdapterMapper mapper;
|
private final DeviceAdapterRegistry registry;
|
private final DeviceDiscoveryPersistenceService persistence;
|
|
public DeviceAdapterServiceImpl(TDeviceAdapterMapper mapper, DeviceAdapterRegistry registry,
|
DeviceDiscoveryPersistenceService persistence)
|
{
|
this.mapper = mapper;
|
this.registry = registry;
|
this.persistence = persistence;
|
}
|
|
DeviceAdapterServiceImpl(TDeviceAdapterMapper mapper, DeviceAdapterRegistry registry)
|
{
|
this(mapper, registry, new DeviceDiscoveryPersistenceService(mapper));
|
}
|
|
@Override
|
public List<TDeviceAdapter> selectList(TDeviceAdapter query) { return mapper.selectDeviceAdapterList(query); }
|
|
@Override
|
public TDeviceAdapter selectById(Long adapterId)
|
{
|
TDeviceAdapter adapter = mapper.selectDeviceAdapterById(adapterId);
|
if (adapter == null) throw error("设备适配器实例不存在", DeviceAdapterErrorCodes.NOT_FOUND);
|
return adapter;
|
}
|
|
@Override
|
public int insert(TDeviceAdapter adapter)
|
{
|
normalizeAndValidate(adapter);
|
if (mapper.selectDeviceAdapterByCode(adapter.getAdapterCode()) != null)
|
throw error("适配器编码已存在", DeviceAdapterErrorCodes.DUPLICATE_CODE);
|
adapter.setCapabilities(READ_ONLY_CAPABILITIES);
|
adapter.setStatus(DISABLED);
|
adapter.setConnectionStatus("DISCONNECTED");
|
adapter.setDelFlag("0");
|
try { return mapper.insertDeviceAdapter(adapter); }
|
catch (DuplicateKeyException exception) { throw error("适配器编码已存在", DeviceAdapterErrorCodes.DUPLICATE_CODE); }
|
}
|
|
@Override
|
public int update(TDeviceAdapter adapter)
|
{
|
TDeviceAdapter current = selectById(adapter.getAdapterId());
|
normalizeAndValidate(adapter);
|
adapter.setAdapterCode(current.getAdapterCode());
|
adapter.setCapabilities(READ_ONLY_CAPABILITIES);
|
int rows = mapper.updateDeviceAdapter(adapter);
|
if (ENABLED.equals(current.getStatus()))
|
{
|
adapter.setStatus(ENABLED);
|
registry.synchronize(toRuntimeConfig(adapter));
|
}
|
return rows;
|
}
|
|
@Override
|
public int changeStatus(Long adapterId, boolean enabled, String updateBy)
|
{
|
TDeviceAdapter adapter = selectById(adapterId);
|
String status = enabled ? ENABLED : DISABLED;
|
if (status.equals(adapter.getStatus())) return 1;
|
int rows = mapper.updateDeviceAdapterStatus(adapterId, status, updateBy);
|
adapter.setStatus(status);
|
registry.synchronize(toRuntimeConfig(adapter));
|
return rows;
|
}
|
|
@Override
|
public AdapterDiscoveryResult discover(Long adapterId)
|
{
|
TDeviceAdapter adapter = selectById(adapterId);
|
if (!ENABLED.equals(adapter.getStatus()))
|
throw error("设备适配器实例未启用", DeviceAdapterErrorCodes.DISABLED);
|
try
|
{
|
registry.synchronize(toRuntimeConfig(adapter));
|
List<com.ruoyi.device.model.DetectedChannel> channels = registry.require(adapter.getAdapterCode()).discoverChannels();
|
Instant observedAt = Instant.now();
|
AdapterDiscoveryResult result = persistence.persist(adapter, channels, observedAt);
|
mapper.updateDiscoverySuccess(adapterId, observedAt);
|
return result;
|
}
|
catch (BtsTransportException exception)
|
{
|
return failDiscovery(adapterId, exception.reason().name(), exception);
|
}
|
catch (BtsProtocolException exception)
|
{
|
return failDiscovery(adapterId, exception.reason().name(), exception);
|
}
|
catch (DeviceAdapterException | AdapterRegistryException exception)
|
{
|
return failDiscovery(adapterId, "ADAPTER_FAILURE", exception);
|
}
|
}
|
|
@Override
|
public List<TDetectionChannel> selectChannels(Long adapterId)
|
{
|
selectById(adapterId);
|
return mapper.selectDetectionChannelsByAdapterId(adapterId);
|
}
|
|
@Override
|
public void synchronizeEnabledAdapters()
|
{
|
mapper.selectEnabledDeviceAdapters().forEach(adapter -> registry.synchronize(toRuntimeConfig(adapter)));
|
}
|
|
@Override
|
public void pollDueAdapters()
|
{
|
Instant now = Instant.now();
|
for (TDeviceAdapter adapter : mapper.selectEnabledDeviceAdapters())
|
{
|
Instant lastAttempt = adapter.getLastAttemptAtUtc();
|
if (lastAttempt != null && Duration.between(lastAttempt, now).toMillis() < adapter.getPollIntervalMs()) continue;
|
try { discover(adapter.getAdapterId()); }
|
catch (ServiceException ignored) { /* 失败状态已持久化,下一轮按周期重试。 */ }
|
}
|
}
|
|
private AdapterDiscoveryResult failDiscovery(Long adapterId, String errorCode, RuntimeException exception)
|
{
|
mapper.updateDiscoveryFailure(adapterId, errorCode, exception.getMessage());
|
throw error("设备发现失败:" + exception.getMessage(), DeviceAdapterErrorCodes.DISCOVERY_FAILED);
|
}
|
|
private void normalizeAndValidate(TDeviceAdapter adapter)
|
{
|
adapter.setAdapterCode(StringUtils.trim(adapter.getAdapterCode()));
|
adapter.setAdapterName(StringUtils.trim(adapter.getAdapterName()));
|
adapter.setAdapterType(StringUtils.trim(adapter.getAdapterType()).toUpperCase(Locale.ROOT));
|
adapter.setHost(StringUtils.trim(adapter.getHost()));
|
adapter.setTimezoneId(StringUtils.trim(adapter.getTimezoneId()));
|
adapter.setRemark(StringUtils.trim(adapter.getRemark()));
|
if (!"BTS".equals(adapter.getAdapterType()))
|
throw error("不支持的适配器类型", DeviceAdapterErrorCodes.UNSUPPORTED_TYPE);
|
if (!Boolean.TRUE.equals(adapter.getReadOnly()))
|
throw error("当前里程碑只允许只读设备适配器", DeviceAdapterErrorCodes.INVALID_CONFIG);
|
try
|
{
|
if (adapter.getPort() == null || adapter.getPort() < 1 || adapter.getPort() > 65535
|
|| adapter.getPollIntervalMs() == null || adapter.getPollIntervalMs() < 1000
|
|| StringUtils.isBlank(adapter.getAdapterCode()) || StringUtils.isBlank(adapter.getAdapterName())
|
|| StringUtils.isBlank(adapter.getHost())) throw new IllegalArgumentException();
|
ZoneId.of(adapter.getTimezoneId());
|
}
|
catch (RuntimeException exception)
|
{
|
throw error("设备适配器配置不正确", DeviceAdapterErrorCodes.INVALID_CONFIG);
|
}
|
}
|
|
private AdapterInstanceConfig toRuntimeConfig(TDeviceAdapter adapter)
|
{
|
return new AdapterInstanceConfig(adapter.getAdapterCode(), adapter.getAdapterType(), adapter.getHost(),
|
adapter.getPort(), adapter.getPollIntervalMs(), adapter.getTimezoneId(),
|
ENABLED.equals(adapter.getStatus()), AdapterCapabilities.observeOnly());
|
}
|
|
private ServiceException error(String message, int code) { return new ServiceException(message, code); }
|
}
|