huang
2025-11-26 792236ef78c2cdd3a989fb40a7f2e2487c4e17b6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
package com.mes.device.service.impl;
 
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mes.device.entity.DeviceConfig;
import com.mes.device.service.DeviceConfigService;
import com.mes.device.service.DeviceGroupRelationService;
import com.mes.device.service.DevicePlcOperationService;
import com.mes.device.util.ConfigJsonHelper;
import com.mes.device.vo.DeviceGroupVO;
import com.mes.device.vo.DevicePlcVO;
import com.mes.service.PlcTestWriteService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
 
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
 
/**
 * 设备 PLC 操作服务实现
 *
 * @author mes
 * @since 2025-11-17
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DevicePlcOperationServiceImpl implements DevicePlcOperationService {
 
    private static final String PLC_PROJECT_ID_KEY = "plcProjectId";
 
    private final DeviceConfigService deviceConfigService;
    private final DeviceGroupRelationService deviceGroupRelationService;
    private final PlcTestWriteService plcTestWriteService;
    private final ObjectMapper objectMapper;
 
    public static enum PlcOperationType {
        /** PLC请求操作 */
        REQUEST("PLC请求", "PLC 请求发送成功", "PLC 请求发送失败"),
        /** PLC汇报操作 */
        REPORT("PLC汇报", "PLC 汇报模拟成功", "PLC 汇报模拟失败"),
        /** PLC重置操作 */
        RESET("PLC重置", "PLC 状态已重置", "PLC 状态重置失败");
 
        /** 操作显示名称 */
        private final String display;
        /** 操作成功提示信息 */
        private final String successMsg;
        /** 操作失败提示信息 */
        private final String failedMsg;
 
        /**
         * 构造方法
         * @param display 操作显示名称
         * @param successMsg 成功提示信息
         * @param failedMsg 失败提示信息
         */
        PlcOperationType(String display, String successMsg, String failedMsg) {
            this.display = display;
            this.successMsg = successMsg;
            this.failedMsg = failedMsg;
        }
    }
 
    @Override
    public DevicePlcVO.OperationResult triggerRequest(Long deviceId) {
        return executeOperation(deviceId, PlcOperationType.REQUEST);
    }
 
    @Override
    public List<DevicePlcVO.OperationResult> triggerRequest(List<Long> deviceIds) {
        return executeBatch(deviceIds, PlcOperationType.REQUEST);
    }
 
    @Override
    public List<DevicePlcVO.OperationResult> triggerRequestByGroup(Long groupId) {
        return executeBatch(getDeviceIdsByGroup(groupId), PlcOperationType.REQUEST);
    }
 
    @Override
    public DevicePlcVO.OperationResult triggerReport(Long deviceId) {
        return executeOperation(deviceId, PlcOperationType.REPORT);
    }
 
    @Override
    public List<DevicePlcVO.OperationResult> triggerReport(List<Long> deviceIds) {
        return executeBatch(deviceIds, PlcOperationType.REPORT);
    }
 
    @Override
    public List<DevicePlcVO.OperationResult> triggerReportByGroup(Long groupId) {
        return executeBatch(getDeviceIdsByGroup(groupId), PlcOperationType.REPORT);
    }
 
    @Override
    public DevicePlcVO.OperationResult resetDevice(Long deviceId) {
        return executeOperation(deviceId, PlcOperationType.RESET);
    }
 
    @Override
    public List<DevicePlcVO.OperationResult> resetDevices(List<Long> deviceIds) {
        return executeBatch(deviceIds, PlcOperationType.RESET);
    }
 
    @Override
    public DevicePlcVO.StatusInfo readStatus(Long deviceId) {
        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
        if (device == null) {
            return DevicePlcVO.StatusInfo.builder()
                    .deviceId(deviceId)
                    .deviceName("未知设备")
                    .fieldValues(Collections.emptyMap())
                    .timestamp(LocalDateTime.now())
                    .build();
        }
 
        try {
            Map<String, Object> data = plcTestWriteService.readPlcStatusByDevice(deviceId);
            return DevicePlcVO.StatusInfo.builder()
                    .deviceId(device.getId())
                    .deviceName(device.getDeviceName())
                    .deviceCode(device.getDeviceCode())
                    .projectId(String.valueOf(device.getProjectId()))
                    .fieldValues(data)
                    .timestamp(LocalDateTime.now())
                    .build();
        } catch (Exception e) {
            log.error("读取设备 PLC 状态失败, deviceId={}", deviceId, e);
            return DevicePlcVO.StatusInfo.builder()
                    .deviceId(device.getId())
                    .deviceName(device.getDeviceName())
                    .deviceCode(device.getDeviceCode())
                    .projectId(null)
                    .fieldValues(Collections.emptyMap())
                    .timestamp(LocalDateTime.now())
                    .build();
        }
    }
 
    @Override
    public List<DevicePlcVO.StatusInfo> readStatusByGroup(Long groupId) {
        List<Long> deviceIds = getDeviceIdsByGroup(groupId);
        if (CollectionUtils.isEmpty(deviceIds)) {
            return Collections.emptyList();
        }
        return deviceIds.stream()
                .map(this::readStatus)
                .collect(Collectors.toList());
    }
 
    private List<DevicePlcVO.OperationResult> executeBatch(List<Long> deviceIds, PlcOperationType type) {
        if (CollectionUtils.isEmpty(deviceIds)) {
            return Collections.emptyList();
        }
        return deviceIds.stream()
                .filter(Objects::nonNull)
                .distinct()
                .map(id -> executeOperation(id, type))
                .collect(Collectors.toList());
    }
 
    private DevicePlcVO.OperationResult executeOperation(Long deviceId, PlcOperationType type) {
        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
        if (device == null) {
            return buildResult(deviceId, null, null, type, false, "设备不存在");
        }
 
        try {
            boolean success = invokeOperation(type, deviceId);
            String message = success ? type.successMsg : type.failedMsg;
            return buildResult(device.getId(), device, String.valueOf(device.getProjectId()), type, success, message);
        } catch (Exception e) {
            log.error("执行 PLC 操作失败, deviceId={}, operation={}", deviceId, type, e);
            return buildResult(device.getId(), device, null, type, false, e.getMessage());
        }
    }
 
    private boolean invokeOperation(PlcOperationType type, Long deviceId) {
        switch (type) {
            case REQUEST:
                return plcTestWriteService.simulatePlcRequestByDevice(deviceId);
            case REPORT:
                return plcTestWriteService.simulatePlcReportByDevice(deviceId);
            case RESET:
                return plcTestWriteService.resetPlcByDevice(deviceId);
            default:
                return false;
        }
    }
 
    private DevicePlcVO.OperationResult buildResult(Long deviceId, DeviceConfig device, String projectId,
                                                    PlcOperationType type, boolean success, String message) {
        return DevicePlcVO.OperationResult.builder()
                .deviceId(deviceId)
                .deviceName(device != null ? device.getDeviceName() : "未知设备")
                .deviceCode(device != null ? device.getDeviceCode() : null)
                .projectId(projectId)
                .operation(type.display)
                .success(success)
                .message(message)
                .timestamp(LocalDateTime.now())
                .build();
    }
 
    private List<Long> getDeviceIdsByGroup(Long groupId) {
        if (groupId == null) {
            return Collections.emptyList();
        }
        try {
            List<DeviceGroupVO.DeviceInfo> devices = deviceGroupRelationService.getGroupDevices(groupId);
            if (CollectionUtils.isEmpty(devices)) {
                return Collections.emptyList();
            }
            return devices.stream()
                    .map(DeviceGroupVO.DeviceInfo::getId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("获取设备组设备失败, groupId={}", groupId, e);
            return Collections.emptyList();
        }
    }
 
    @Override
    public DevicePlcVO.OperationResult writeFields(Long deviceId, Map<String, Object> fieldValues, String operationName) {
        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
        if (device == null) {
            return buildResult(deviceId, null, null, PlcOperationType.REQUEST, false, "设备不存在");
        }
        try {
            boolean success = plcTestWriteService.writeFieldsByDevice(deviceId, fieldValues);
            String opName = operationName != null ? operationName : "PLC写入";
            return DevicePlcVO.OperationResult.builder()
                    .deviceId(device.getId())
                    .deviceName(device.getDeviceName())
                    .deviceCode(device.getDeviceCode())
                    .projectId(String.valueOf(device.getProjectId()))
                    .operation(opName)
                    .success(success)
                    .message(success ? opName + "成功" : opName + "失败")
                    .timestamp(LocalDateTime.now())
                    .build();
        } catch (Exception e) {
            log.error("写入PLC字段失败, deviceId={}", deviceId, e);
            return DevicePlcVO.OperationResult.builder()
                    .deviceId(device.getId())
                    .deviceName(device.getDeviceName())
                    .deviceCode(device.getDeviceCode())
                    .projectId(null)
                    .operation(operationName)
                    .success(false)
                    .message(e.getMessage())
                    .timestamp(LocalDateTime.now())
                    .build();
        }
    }
 
    @Override
    public String resolveProjectId(Long deviceId) {
        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
        if (device == null) {
            throw new IllegalArgumentException("设备不存在: " + deviceId);
        }
        return resolveProjectId(device);
    }
 
    private String resolveProjectId(DeviceConfig device) {
        if (device == null) {
            throw new IllegalArgumentException("设备信息为空");
        }
 
        // 优先从configJson中获取
        Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper);
        Object plcProjectId = configParams.get(PLC_PROJECT_ID_KEY);
        if (plcProjectId != null) {
            return String.valueOf(plcProjectId);
        }
 
        // 其次从扩展参数中获取(兼容旧配置)
        String extra = device.getExtraParams();
        if (extra != null && !extra.isEmpty()) {
            try {
                Map<String, Object> extraParams = objectMapper.readValue(extra, new TypeReference<Map<String, Object>>() {});
                Object plcProjectIdFromExtra = extraParams.get(PLC_PROJECT_ID_KEY);
                if (plcProjectIdFromExtra != null) {
                    return String.valueOf(plcProjectIdFromExtra);
                }
            } catch (Exception e) {
                log.warn("解析设备扩展参数失败, deviceId={}", device.getId(), e);
            }
        }
 
        if (device.getProjectId() != null) {
            return String.valueOf(device.getProjectId());
        }
 
        if (device.getDeviceCode() != null && !device.getDeviceCode().isEmpty()) {
            return device.getDeviceCode();
        }
 
        throw new IllegalStateException("无法解析设备的 PLC 项目标识, deviceId=" + device.getId());
    }
 
 
}