32个文件已修改
36个文件已添加
1个文件已删除
| New file |
| | |
| | | package com.mes.config; |
| | | |
| | | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
| | | import org.apache.ibatis.reflection.MetaObject; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * ç»ä¸å¤çå
Œ
±å段ï¼created_timeãupdated_timeãcreated_byãupdated_byï¼çèªå¨å¡«å
|
| | | */ |
| | | @Component |
| | | public class MybatisMetaObjectHandler implements MetaObjectHandler { |
| | | |
| | | @Override |
| | | public void insertFill(MetaObject metaObject) { |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | String operator = resolveOperator(); |
| | | |
| | | strictInsertFill(metaObject, "createdTime", LocalDateTime.class, now); |
| | | strictInsertFill(metaObject, "updatedTime", LocalDateTime.class, now); |
| | | strictInsertFill(metaObject, "createdBy", String.class, operator); |
| | | strictInsertFill(metaObject, "updatedBy", String.class, operator); |
| | | } |
| | | |
| | | @Override |
| | | public void updateFill(MetaObject metaObject) { |
| | | strictUpdateFill(metaObject, "updatedTime", LocalDateTime.class, LocalDateTime.now()); |
| | | strictUpdateFill(metaObject, "updatedBy", String.class, resolveOperator()); |
| | | } |
| | | |
| | | private String resolveOperator() { |
| | | // TODO: ä¹å坿¥å
¥ç»å½ä¸ä¸æï¼è¿é临æ¶åé为 system |
| | | return "system"; |
| | | } |
| | | } |
| | | |
| | |
| | | package com.mes.device.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.request.DeviceConfigRequest; |
| | | import com.mes.device.service.DeviceConfigService; |
| | | import com.mes.device.vo.DeviceConfigVO; |
| | | import com.mes.device.vo.StatisticsVO; |
| | | import com.mes.vo.Result; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import io.swagger.annotations.ApiParam; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("device/config") |
| | | @Tag(name = "设å¤é
置管ç", description = "设å¤é
置管çç¸å
³æ¥å£") |
| | | @Api(tags = "设å¤é
置管ç") |
| | | public class DeviceConfigController { |
| | | |
| | | @Autowired |
| | | private DeviceConfigService deviceConfigService; |
| | | |
| | | @Autowired |
| | | private ObjectMapper objectMapper; |
| | | |
| | | /** |
| | | * å建设å¤é
ç½® |
| | | */ |
| | | @PostMapping("/devices") |
| | | @Operation(summary = "å建设å¤é
ç½®", description = "å建æ°ç设å¤é
ç½®") |
| | | @ApiOperation("å建设å¤é
ç½®") |
| | | public Result<DeviceConfig> createDevice( |
| | | @Valid @RequestBody DeviceConfig deviceConfig) { |
| | | try { |
| | |
| | | * æ´æ°è®¾å¤é
ç½® |
| | | */ |
| | | @PostMapping("/devices/update") |
| | | @Operation(summary = "æ´æ°è®¾å¤é
ç½®", description = "æ´æ°æå®IDç设å¤é
ç½®") |
| | | @ApiOperation("æ´æ°è®¾å¤é
ç½®") |
| | | public Result<DeviceConfig> updateDevice( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | | DeviceConfig deviceConfig = (DeviceConfig) request.getDeviceConfig(); |
| | | DeviceConfig deviceConfig; |
| | | Object deviceConfigObj = request.getDeviceConfig(); |
| | | |
| | | // 妿 deviceConfig æ¯ Map ç±»åï¼JSON ååºåååç LinkedHashMapï¼ï¼éè¦è½¬æ¢ä¸º DeviceConfig |
| | | if (deviceConfigObj instanceof Map) { |
| | | deviceConfig = objectMapper.convertValue(deviceConfigObj, DeviceConfig.class); |
| | | } else if (deviceConfigObj instanceof DeviceConfig) { |
| | | deviceConfig = (DeviceConfig) deviceConfigObj; |
| | | } else { |
| | | log.error("䏿¯æç deviceConfig ç±»å: {}", deviceConfigObj != null ? deviceConfigObj.getClass() : "null"); |
| | | return Result.error("设å¤é
ç½®æ°æ®æ ¼å¼é误"); |
| | | } |
| | | |
| | | deviceConfig.setId(request.getDeviceId()); |
| | | boolean success = deviceConfigService.updateDevice(deviceConfig); |
| | | if (success) { |
| | |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("æ´æ°è®¾å¤é
置失败", e); |
| | | return Result.error("æ´æ°è®¾å¤é
置失败"); |
| | | return Result.error("æ´æ°è®¾å¤é
置失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | |
| | | * å é¤è®¾å¤é
ç½® |
| | | */ |
| | | @PostMapping("/devices/delete") |
| | | @Operation(summary = "å é¤è®¾å¤é
ç½®", description = "å 餿å®IDç设å¤é
ç½®") |
| | | @ApiOperation("å é¤è®¾å¤é
ç½®") |
| | | public Result<Void> deleteDevice( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | |
| | | * æ ¹æ®IDè·å设å¤é
ç½® |
| | | */ |
| | | @PostMapping("/devices/detail") |
| | | @Operation(summary = "è·å设å¤é
置详æ
", description = "æ ¹æ®IDè·å设å¤é
ç½®ç详ç»ä¿¡æ¯") |
| | | @ApiOperation("è·å设å¤é
置详æ
") |
| | | public Result<DeviceConfig> getDeviceById( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | |
| | | * å页æ¥è¯¢è®¾å¤é
ç½®å表 |
| | | */ |
| | | @PostMapping("/devices/list") |
| | | @Operation(summary = "å页æ¥è¯¢è®¾å¤é
ç½®", description = "å页æ¥è¯¢è®¾å¤é
ç½®å表") |
| | | @ApiOperation("å页æ¥è¯¢è®¾å¤é
ç½®") |
| | | public Result<Page<DeviceConfigVO.DeviceInfo>> getDeviceList( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | |
| | | * å¯ç¨è®¾å¤ |
| | | */ |
| | | @PostMapping("/devices/enable") |
| | | @Operation(summary = "å¯ç¨è®¾å¤", description = "å¯ç¨æå®IDç设å¤") |
| | | @ApiOperation("å¯ç¨è®¾å¤") |
| | | public Result<Void> enableDevice( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | |
| | | * ç¦ç¨è®¾å¤ |
| | | */ |
| | | @PostMapping("/devices/disable") |
| | | @Operation(summary = "ç¦ç¨è®¾å¤", description = "ç¦ç¨æå®IDç设å¤") |
| | | @ApiOperation("ç¦ç¨è®¾å¤") |
| | | public Result<Void> disableDevice( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | |
| | | * æ¹éå¯ç¨è®¾å¤ |
| | | */ |
| | | @PostMapping("/devices/batch-enable") |
| | | @Operation(summary = "æ¹éå¯ç¨è®¾å¤", description = "æ¹éå¯ç¨æå®IDå表ç设å¤") |
| | | @ApiOperation("æ¹éå¯ç¨è®¾å¤") |
| | | public Result<Void> batchEnableDevices( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | |
| | | * æ¹éç¦ç¨è®¾å¤ |
| | | */ |
| | | @PostMapping("/devices/batch-disable") |
| | | @Operation(summary = "æ¹éç¦ç¨è®¾å¤", description = "æ¹éç¦ç¨æå®IDå表ç设å¤") |
| | | @ApiOperation("æ¹éç¦ç¨è®¾å¤") |
| | | public Result<Void> batchDisableDevices( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | |
| | | * è·å设å¤ç»è®¡ä¿¡æ¯ |
| | | */ |
| | | @PostMapping("/statistics/devices") |
| | | @Operation(summary = "è·å设å¤ç»è®¡ä¿¡æ¯", description = "è·å设å¤ç¸å
³çç»è®¡ä¿¡æ¯") |
| | | @ApiOperation("è·å设å¤ç»è®¡ä¿¡æ¯") |
| | | public Result<StatisticsVO.DeviceStatistics> getDeviceStatistics( |
| | | @Parameter(description = "设å¤é
置请æ±") @RequestBody(required = false) DeviceConfigRequest request) { |
| | | @ApiParam("设å¤é
置请æ±") @RequestBody(required = false) DeviceConfigRequest request) { |
| | | try { |
| | | StatisticsVO.DeviceStatistics statistics = deviceConfigService.getDeviceStatistics(request != null ? request.getProjectId() : null); |
| | | return Result.success(statistics); |
| | |
| | | * æ£æ¥è®¾å¤ç¼ç æ¯å¦å·²åå¨ |
| | | */ |
| | | @PostMapping("/devices/check-code") |
| | | @Operation(summary = "æ£æ¥è®¾å¤ç¼ç ", description = "æ£æ¥è®¾å¤ç¼ç æ¯å¦å·²åå¨") |
| | | @ApiOperation("æ£æ¥è®¾å¤ç¼ç ") |
| | | public Result<Boolean> checkDeviceCodeExists( |
| | | @Parameter(description = "设å¤é
置请æ±") @RequestBody DeviceConfigRequest request) { |
| | | @ApiParam("设å¤é
置请æ±") @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | | boolean exists = deviceConfigService.isDeviceCodeExists(request.getDeviceCode(), request.getDeviceId()); |
| | | return Result.success(exists); |
| | |
| | | * è·å设å¤ç±»åå表 |
| | | */ |
| | | @PostMapping("/devices/types") |
| | | @Operation(summary = "è·å设å¤ç±»åå表", description = "è·åææå¯ç¨ç设å¤ç±»å") |
| | | @ApiOperation("è·å设å¤ç±»åå表") |
| | | public Result<List<String>> getDeviceTypes(@RequestBody(required = false) Map<String, Object> request) { |
| | | try { |
| | | List<String> deviceTypes = deviceConfigService.getAllDeviceTypes(); |
| | |
| | | * è·å设å¤ç¶æå表 |
| | | */ |
| | | @PostMapping("/devices/statuses") |
| | | @Operation(summary = "è·å设å¤ç¶æå表", description = "è·åææå¯ç¨ç设å¤ç¶æ") |
| | | @ApiOperation("è·å设å¤ç¶æå表") |
| | | public Result<List<String>> getDeviceStatuses(@RequestBody(required = false) Map<String, Object> request) { |
| | | try { |
| | | List<String> deviceStatuses = deviceConfigService.getAllDeviceStatuses(); |
| | |
| | | * è·å设å¤é
ç½®æ ç»æ |
| | | */ |
| | | @PostMapping("/devices/tree") |
| | | @Operation(summary = "è·å设å¤é
ç½®æ ç»æ", description = "è·å设å¤å设å¤ç»çæ å½¢ç»ææ°æ®") |
| | | @ApiOperation("è·å设å¤é
ç½®æ ç»æ") |
| | | public Result<List<DeviceConfigVO.DeviceTreeNode>> getDeviceTree( |
| | | @Parameter(description = "设å¤é
置请æ±") @RequestBody(required = false) DeviceConfigRequest request) { |
| | | @ApiParam("设å¤é
置请æ±") @RequestBody(required = false) DeviceConfigRequest request) { |
| | | try { |
| | | List<DeviceConfigVO.DeviceTreeNode> treeData = deviceConfigService.getDeviceTree(request != null ? request.getProjectId() : null); |
| | | return Result.success(treeData); |
| | |
| | | * 设å¤å¥åº·æ£æ¥ |
| | | */ |
| | | @PostMapping("/devices/health-check") |
| | | @Operation(summary = "设å¤å¥åº·æ£æ¥", description = "对æå®è®¾å¤è¿è¡å¥åº·æ£æ¥") |
| | | @ApiOperation("设å¤å¥åº·æ£æ¥") |
| | | public Result<DeviceConfigVO.HealthCheckResult> performHealthCheck( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | |
| | | * 2. ç´æ¥ä¼ å
¥ plcIp / plcPort / timeout è¿è¡ä¸æ¬¡æ§æµè¯ |
| | | */ |
| | | @PostMapping("/devices/test-connection") |
| | | @Operation(summary = "æµè¯è®¾å¤PLCè¿æ¥", description = "æ ¹æ®è®¾å¤é
ç½®æµè¯PLCè¿æ¥æ¯å¦å¯è¾¾") |
| | | @ApiOperation("æµè¯è®¾å¤PLCè¿æ¥") |
| | | public Result<String> testDeviceConnection(@RequestBody Map<String, Object> body) { |
| | | try { |
| | | String plcIp = null; |
| | |
| | | import com.mes.device.service.DeviceControlProfileService; |
| | | import com.mes.device.vo.DeviceControlProfile; |
| | | import com.mes.vo.Result; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | @RestController |
| | | @RequestMapping("device/control") |
| | | @Tag(name = "è®¾å¤æ§å¶åæ°", description = "è®¾å¤æ§å¶åæ°é
ç½®æ¥å£") |
| | | @Api(tags = "è®¾å¤æ§å¶åæ°") |
| | | @RequiredArgsConstructor |
| | | @Validated |
| | | public class DeviceControlProfileController { |
| | |
| | | private final DeviceControlProfileService controlProfileService; |
| | | |
| | | @GetMapping("/{deviceId}") |
| | | @Operation(summary = "è·åè®¾å¤æ§å¶åæ°") |
| | | @ApiOperation("è·åè®¾å¤æ§å¶åæ°") |
| | | public Result<DeviceControlProfile> getProfile(@PathVariable Long deviceId) { |
| | | return Result.success(controlProfileService.getProfile(deviceId)); |
| | | } |
| | | |
| | | @PostMapping("/{deviceId}") |
| | | @Operation(summary = "æ´æ°è®¾å¤æ§å¶åæ°") |
| | | @ApiOperation("æ´æ°è®¾å¤æ§å¶åæ°") |
| | | public Result<Void> saveProfile(@PathVariable Long deviceId, |
| | | @RequestBody DeviceControlProfile profile) { |
| | | controlProfileService.updateProfile(deviceId, profile); |
| | |
| | | import com.mes.device.vo.DeviceGroupVO; |
| | | import com.mes.device.vo.StatisticsVO; |
| | | import com.mes.vo.Result; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("device/group") |
| | | @Tag(name = "设å¤ç»ç®¡ç", description = "设å¤ç»ç®¡çç¸å
³æ¥å£") |
| | | @Api(tags = "设å¤ç»ç®¡ç") |
| | | public class DeviceGroupController { |
| | | |
| | | @Resource |
| | |
| | | * å建设å¤ç» |
| | | */ |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建设å¤ç»", description = "å建设å¤ç»ä¿¡æ¯") |
| | | @ApiOperation("å建设å¤ç»") |
| | | public Result<DeviceGroupConfig> createGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * æ´æ°è®¾å¤ç»é
ç½® |
| | | */ |
| | | @PostMapping("/update") |
| | | @Operation(summary = "æ´æ°è®¾å¤ç»é
ç½®", description = "æ´æ°æå®IDç设å¤ç»é
ç½®") |
| | | @ApiOperation("æ´æ°è®¾å¤ç»é
ç½®") |
| | | public Result<DeviceGroupConfig> updateGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * å é¤è®¾å¤ç»é
ç½® |
| | | */ |
| | | @PostMapping("/delete") |
| | | @Operation(summary = "å é¤è®¾å¤ç»é
ç½®", description = "å 餿å®IDç设å¤ç»é
ç½®") |
| | | @ApiOperation("å é¤è®¾å¤ç»é
ç½®") |
| | | public Result<Void> deleteGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * æ ¹æ®IDè·å设å¤ç»é
ç½® |
| | | */ |
| | | @PostMapping("/detail") |
| | | @Operation(summary = "è·å设å¤ç»é
置详æ
", description = "æ ¹æ®IDè·å设å¤ç»é
ç½®ç详ç»ä¿¡æ¯") |
| | | @ApiOperation("è·å设å¤ç»é
置详æ
") |
| | | public Result<DeviceGroupConfig> getGroupById( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * å页æ¥è¯¢è®¾å¤ç»å表 |
| | | */ |
| | | @PostMapping("/list") |
| | | @Operation(summary = "å页æ¥è¯¢è®¾å¤ç»å表", description = "å页æ¥è¯¢è®¾å¤ç»å表") |
| | | @ApiOperation("å页æ¥è¯¢è®¾å¤ç»å表") |
| | | public Result<Page<DeviceGroupVO.GroupInfo>> getGroupList( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * å¯ç¨è®¾å¤ç» |
| | | */ |
| | | @PostMapping("/enable") |
| | | @Operation(summary = "å¯ç¨è®¾å¤ç»", description = "å¯ç¨æå®è®¾å¤ç»") |
| | | @ApiOperation("å¯ç¨è®¾å¤ç»") |
| | | public Result<Void> enableGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * ç¦ç¨è®¾å¤ç» |
| | | */ |
| | | @PostMapping("/disable") |
| | | @Operation(summary = "ç¦ç¨è®¾å¤ç»", description = "ç¦ç¨æå®è®¾å¤ç»") |
| | | @ApiOperation("ç¦ç¨è®¾å¤ç»") |
| | | public Result<Void> disableGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * æ¹éå¯ç¨è®¾å¤ç» |
| | | */ |
| | | @PostMapping("/batch-enable") |
| | | @Operation(summary = "æ¹éå¯ç¨è®¾å¤ç»", description = "æ¹éå¯ç¨æå®IDå表ç设å¤ç»") |
| | | @ApiOperation("æ¹éå¯ç¨è®¾å¤ç»") |
| | | public Result<Void> batchEnableGroups( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * æ¹éç¦ç¨è®¾å¤ç» |
| | | */ |
| | | @PostMapping("/batch-disable") |
| | | @Operation(summary = "æ¹éç¦ç¨è®¾å¤ç»", description = "æ¹éç¦ç¨æå®IDå表ç设å¤ç»") |
| | | @ApiOperation("æ¹éç¦ç¨è®¾å¤ç»") |
| | | public Result<Void> batchDisableGroups( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * è·å设å¤ç»ç»è®¡ä¿¡æ¯ |
| | | */ |
| | | @PostMapping("/statistics/groups") |
| | | @Operation(summary = "è·å设å¤ç»ç»è®¡ä¿¡æ¯", description = "è·å设å¤ç»ç¸å
³çç»è®¡ä¿¡æ¯") |
| | | @ApiOperation("è·å设å¤ç»ç»è®¡ä¿¡æ¯") |
| | | public Result<StatisticsVO.GroupStatistics> getGroupStatistics( |
| | | @RequestBody(required = false) Map<String, Object> request) { |
| | | try { |
| | |
| | | * æ£æ¥è®¾å¤ç»ç¼ç æ¯å¦å·²åå¨ |
| | | */ |
| | | @PostMapping("/check-code") |
| | | @Operation(summary = "æ£æ¥è®¾å¤ç»ç¼ç ", description = "æ£æ¥è®¾å¤ç»ç¼ç æ¯å¦å·²åå¨") |
| | | @ApiOperation("æ£æ¥è®¾å¤ç»ç¼ç ") |
| | | public Result<Boolean> checkGroupCodeExists( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * è·å设å¤ç»ç±»åå表 |
| | | */ |
| | | @PostMapping("/types") |
| | | @Operation(summary = "è·å设å¤ç»ç±»åå表", description = "è·åææå¯ç¨ç设å¤ç»ç±»å") |
| | | @ApiOperation("è·å设å¤ç»ç±»åå表") |
| | | public Result<List<String>> getGroupTypes() { |
| | | try { |
| | | List<String> groupTypes = deviceGroupConfigService.getAllGroupTypes(); |
| | |
| | | * è·å设å¤ç»ç¶æå表 |
| | | */ |
| | | @PostMapping("/statuses") |
| | | @Operation(summary = "è·å设å¤ç»ç¶æå表", description = "è·åææå¯ç¨ç设å¤ç»ç¶æ") |
| | | @ApiOperation("è·å设å¤ç»ç¶æå表") |
| | | public Result<List<String>> getGroupStatuses() { |
| | | try { |
| | | List<String> groupStatuses = deviceGroupConfigService.getAllGroupStatuses(); |
| | |
| | | * æ·»å 设å¤å°è®¾å¤ç» |
| | | */ |
| | | @PostMapping("/devices") |
| | | @Operation(summary = "æ·»å 设å¤å°è®¾å¤ç»", description = "å°æå®è®¾å¤æ·»å å°è®¾å¤ç»ä¸") |
| | | @ApiOperation("æ·»å 设å¤å°è®¾å¤ç»") |
| | | public Result<Void> addDeviceToGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * ä»è®¾å¤ç»ç§»é¤è®¾å¤ |
| | | */ |
| | | @PostMapping("/devices/remove") |
| | | @Operation(summary = "ä»è®¾å¤ç»ç§»é¤è®¾å¤", description = "ä»è®¾å¤ç»ä¸ç§»é¤æå®è®¾å¤") |
| | | @ApiOperation("ä»è®¾å¤ç»ç§»é¤è®¾å¤") |
| | | public Result<Void> removeDeviceFromGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * æ´æ°è®¾å¤è§è² |
| | | */ |
| | | @PostMapping("/devices/role") |
| | | @Operation(summary = "æ´æ°è®¾å¤è§è²", description = "æ´æ°è®¾å¤å¨è®¾å¤ç»ä¸çè§è²") |
| | | @ApiOperation("æ´æ°è®¾å¤è§è²") |
| | | public Result<Void> updateDeviceRole( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * è·å设å¤ç»è®¾å¤å表 |
| | | */ |
| | | @PostMapping("/devices/list") |
| | | @Operation(summary = "è·å设å¤ç»è®¾å¤å表", description = "è·åæå®è®¾å¤ç»ä¸çææè®¾å¤") |
| | | @ApiOperation("è·å设å¤ç»è®¾å¤å表") |
| | | public Result<List<DeviceGroupVO.DeviceInfo>> getGroupDevices( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * è·å设å¤è®¾å¤ç»å表 |
| | | */ |
| | | @PostMapping("/devices/groups") |
| | | @Operation(summary = "è·å设å¤è®¾å¤ç»å表", description = "è·åæå®è®¾å¤æå±çææè®¾å¤ç»") |
| | | @ApiOperation("è·å设å¤è®¾å¤ç»å表") |
| | | public Result<List<DeviceGroupVO.GroupInfo>> getDeviceGroups( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * æ¹éæ·»å 设å¤å°è®¾å¤ç» |
| | | */ |
| | | @PostMapping("/batch-add-devices") |
| | | @Operation(summary = "æ¹éæ·»å 设å¤å°è®¾å¤ç»", description = "æ¹éå°æå®è®¾å¤å表添å å°è®¾å¤ç»ä¸") |
| | | @ApiOperation("æ¹éæ·»å 设å¤å°è®¾å¤ç»") |
| | | public Result<Void> batchAddDevicesToGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * æ¹éä»è®¾å¤ç»ç§»é¤è®¾å¤ |
| | | */ |
| | | @PostMapping("/devices/batch-remove") |
| | | @Operation(summary = "æ¹éä»è®¾å¤ç»ç§»é¤è®¾å¤", description = "æ¹éä»è®¾å¤ç»ä¸ç§»é¤æå®è®¾å¤å表") |
| | | @ApiOperation("æ¹éä»è®¾å¤ç»ç§»é¤è®¾å¤") |
| | | public Result<Void> batchRemoveDevicesFromGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * 设å¤ç»å¥åº·æ£æ¥ |
| | | */ |
| | | @PostMapping("/health-check") |
| | | @Operation(summary = "设å¤ç»å¥åº·æ£æ¥", description = "对æå®è®¾å¤ç»è¿è¡å¥åº·æ£æ¥") |
| | | @ApiOperation("设å¤ç»å¥åº·æ£æ¥") |
| | | public Result<DeviceGroupVO.HealthCheckResult> performGroupHealthCheck( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | * è·å设å¤ç»æ§è½ç»è®¡ |
| | | */ |
| | | @PostMapping("/performance") |
| | | @Operation(summary = "è·å设å¤ç»æ§è½ç»è®¡", description = "è·åæå®è®¾å¤ç»çæ§è½ç»è®¡ä¿¡æ¯") |
| | | @ApiOperation("è·å设å¤ç»æ§è½ç»è®¡") |
| | | public Result<DeviceGroupVO.PerformanceStats> getGroupPerformance( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | |
| | | import com.mes.device.service.DeviceInteractionService; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | import com.mes.vo.Result; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import io.swagger.annotations.ApiParam; |
| | | import lombok.Data; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.validation.Valid; |
| | | import javax.validation.constraints.NotNull; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @RestController |
| | | @RequestMapping("device/interaction") |
| | | @Tag(name = "设å¤äº¤äº", description = "设å¤äº¤äºé»è¾æ§è¡æ¥å£") |
| | | @Api(tags = "设å¤äº¤äº") |
| | | @Validated |
| | | @RequiredArgsConstructor |
| | | public class DeviceInteractionController { |
| | |
| | | private final DeviceInteractionService deviceInteractionService; |
| | | |
| | | @PostMapping("/glass-feed") |
| | | @Operation(summary = "ç»ç䏿åå
¥") |
| | | @ApiOperation("ç»ç䏿åå
¥") |
| | | public Result<DevicePlcVO.OperationResult> feedGlass(@Valid @RequestBody DeviceGlassFeedRequest request) { |
| | | return Result.success(deviceInteractionService.feedGlass(request)); |
| | | } |
| | | |
| | | @PostMapping("/execute") |
| | | @ApiOperation("æ§è¡è®¾å¤é»è¾æä½") |
| | | public Result<DevicePlcVO.OperationResult> executeOperation( |
| | | @Valid @RequestBody DeviceOperationRequest request) { |
| | | return Result.success(deviceInteractionService.executeOperation( |
| | | request.getDeviceId(), |
| | | request.getOperation(), |
| | | request.getParams() |
| | | )); |
| | | } |
| | | |
| | | /** |
| | | * è®¾å¤æä½è¯·æ± |
| | | */ |
| | | @Data |
| | | public static class DeviceOperationRequest { |
| | | @NotNull(message = "设å¤IDä¸è½ä¸ºç©º") |
| | | @ApiParam(value = "设å¤ID", required = true) |
| | | private Long deviceId; |
| | | |
| | | @NotNull(message = "æä½ç±»åä¸è½ä¸ºç©º") |
| | | @ApiParam(value = "æä½ç±»åï¼å¦ï¼feedGlass, triggerRequest, triggerReportçï¼", required = true) |
| | | private String operation; |
| | | |
| | | @ApiParam(value = "æä½åæ°") |
| | | private Map<String, Object> params; |
| | | } |
| | | } |
| | | |
| | |
| | | import com.mes.device.service.DevicePlcOperationService; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | import com.mes.vo.Result; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.validation.annotation.Validated; |
| | |
| | | @RestController |
| | | @RequiredArgsConstructor |
| | | @RequestMapping("device/plc") |
| | | @Tag(name = "设å¤PLCæä½", description = "å¤è®¾å¤PLCåå
¥ä¸ç¶ææ¥è¯¢æ¥å£") |
| | | @Api(tags = "设å¤PLCæä½") |
| | | public class DevicePlcController { |
| | | |
| | | private final DevicePlcOperationService devicePlcOperationService; |
| | | |
| | | @PostMapping("/requests") |
| | | @Operation(summary = "æ¹é触åPLC请æ±", description = "对æå®è®¾å¤åéPLC请æ±å") |
| | | @ApiOperation("æ¹é触åPLC请æ±") |
| | | public Result<List<DevicePlcVO.OperationResult>> triggerRequests( |
| | | @Valid @RequestBody DevicePlcBatchRequest request) { |
| | | return Result.success(devicePlcOperationService.triggerRequest(request.getDeviceIds())); |
| | | } |
| | | |
| | | @PostMapping("/reports") |
| | | @Operation(summary = "æ¹é模æPLCæ±æ¥", description = "对æå®è®¾å¤æ¨¡æPLCä»»å¡å®ææ±æ¥") |
| | | @ApiOperation("æ¹é模æPLCæ±æ¥") |
| | | public Result<List<DevicePlcVO.OperationResult>> triggerReports( |
| | | @Valid @RequestBody DevicePlcBatchRequest request) { |
| | | return Result.success(devicePlcOperationService.triggerReport(request.getDeviceIds())); |
| | | } |
| | | |
| | | @PostMapping("/resets") |
| | | @Operation(summary = "æ¹ééç½®PLCç¶æ", description = "éç½®æå®è®¾å¤å
³èPLCçå
³é®å段") |
| | | @ApiOperation("æ¹ééç½®PLCç¶æ") |
| | | public Result<List<DevicePlcVO.OperationResult>> resetPlc( |
| | | @Valid @RequestBody DevicePlcBatchRequest request) { |
| | | return Result.success(devicePlcOperationService.resetDevices(request.getDeviceIds())); |
| | | } |
| | | |
| | | @PostMapping("/groups/{groupId}/request") |
| | | @Operation(summary = "设å¤ç»è§¦åPLC请æ±", description = "对设å¤ç»å
ææè®¾å¤åéPLC请æ±å") |
| | | @ApiOperation("设å¤ç»è§¦åPLC请æ±") |
| | | public Result<List<DevicePlcVO.OperationResult>> triggerGroupRequest( |
| | | @PathVariable Long groupId) { |
| | | return Result.success(devicePlcOperationService.triggerRequestByGroup(groupId)); |
| | | } |
| | | |
| | | @PostMapping("/groups/{groupId}/report") |
| | | @Operation(summary = "设å¤ç»æ¨¡æPLCæ±æ¥", description = "对设å¤ç»å
ææè®¾å¤æ¨¡æä»»å¡å®ææ±æ¥") |
| | | @ApiOperation("设å¤ç»æ¨¡æPLCæ±æ¥") |
| | | public Result<List<DevicePlcVO.OperationResult>> triggerGroupReport( |
| | | @PathVariable Long groupId) { |
| | | return Result.success(devicePlcOperationService.triggerReportByGroup(groupId)); |
| | | } |
| | | |
| | | @GetMapping("/status/{deviceId}") |
| | | @Operation(summary = "æ¥è¯¢è®¾å¤PLCç¶æ", description = "读ååå°è®¾å¤çPLCæ°æ®") |
| | | @ApiOperation("æ¥è¯¢è®¾å¤PLCç¶æ") |
| | | public Result<DevicePlcVO.StatusInfo> readStatus(@PathVariable Long deviceId) { |
| | | return Result.success(devicePlcOperationService.readStatus(deviceId)); |
| | | } |
| | | |
| | | @GetMapping("/groups/{groupId}/status") |
| | | @Operation(summary = "æ¥è¯¢è®¾å¤ç»PLCç¶æ", description = "读å设å¤ç»å
ææè®¾å¤çPLCæ°æ®") |
| | | @ApiOperation("æ¥è¯¢è®¾å¤ç»PLCç¶æ") |
| | | public Result<List<DevicePlcVO.StatusInfo>> readGroupStatus(@PathVariable Long groupId) { |
| | | return Result.success(devicePlcOperationService.readStatusByGroup(groupId)); |
| | | } |
| New file |
| | |
| | | # DeviceConfig è¡¨åæ®µè¯´æ |
| | | |
| | | ## æ°æ®åºè¡¨ï¼device_config |
| | | |
| | | ### åæ®µç»æè¯´æ |
| | | |
| | | #### 1. `config_json` å |
| | | **ç¨é**ï¼åå¨**éç¨é
ç½®åæ°**ï¼å端"é
ç½®åæ°"å¡çä¸çé®å¼å¯¹ï¼ |
| | | |
| | | **æ°æ®ç»æ**ï¼ |
| | | ```json |
| | | [ |
| | | { |
| | | "paramKey": "åæ°å1", |
| | | "paramValue": "åæ°å¼1", |
| | | "description": "æè¿°1" |
| | | }, |
| | | { |
| | | "paramKey": "åæ°å2", |
| | | "paramValue": "åæ°å¼2", |
| | | "description": "æè¿°2" |
| | | } |
| | | ] |
| | | ``` |
| | | |
| | | **å端ä½ç½®**ï¼è®¾å¤ç¼è¾å¼¹çª â "é
ç½®åæ°"å¡ç |
| | | |
| | | **ç¹ç¹**ï¼ |
| | | - éç¨çé®å¼å¯¹é
ç½® |
| | | - å¯ä»¥æ·»å ä»»æåæ° |
| | | - ç¨äºåå¨è®¾å¤ç¹å®çèªå®ä¹åæ° |
| | | |
| | | --- |
| | | |
| | | #### 2. `extra_params` å |
| | | **ç¨é**ï¼åå¨**æ©å±åæ°**ï¼ç»æåçJSONå¯¹è±¡ï¼ |
| | | |
| | | **æ°æ®ç»æ**ï¼ |
| | | ```json |
| | | { |
| | | "connectionConfig": { |
| | | "moduleCode": "模åç¼å·", |
| | | "protocolType": "é讯åè®®", |
| | | "timeout": 30, |
| | | "retryCount": 3, |
| | | "heartbeatInterval": 30 |
| | | }, |
| | | "plcConfig": { |
| | | "dbArea": "DB1", |
| | | "beginIndex": 0, |
| | | "autoModeInterval": 5000, |
| | | "plcType": "S7-1200" |
| | | }, |
| | | "plcProjectId": "项ç®ID", |
| | | "deviceLogic": { |
| | | // æ ¹æ®è®¾å¤ç±»åä¸åèä¸å |
| | | // ä¸å¤§è½¦è®¾å¤ï¼ |
| | | "vehicleCapacity": 6000, |
| | | "glassIntervalMs": 1000, |
| | | "autoFeed": true, |
| | | "maxRetryCount": 5, |
| | | "positionMapping": { |
| | | "POS1": 1, |
| | | "POS2": 2 |
| | | } |
| | | // 大çç设å¤ï¼ |
| | | // "glassSize": 2000, |
| | | // "processingTime": 5000, |
| | | // "autoProcess": true |
| | | // ç»çåå¨è®¾å¤ï¼ |
| | | // "storageCapacity": 100, |
| | | // "retrievalMode": "FIFO", |
| | | // "autoStore": true, |
| | | // "autoRetrieve": true |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | **å端ä½ç½®**ï¼è®¾å¤ç¼è¾å¼¹çªä¸çå¤ä¸ªé
ç½®åºå |
| | | |
| | | --- |
| | | |
| | | ### extra_params 详ç»è¯´æ |
| | | |
| | | #### 2.1 `connectionConfig` - è¿æ¥é
ç½® |
| | | **å端ä½ç½®**ï¼"è¿æ¥é
ç½®"å¡ç |
| | | |
| | | | åæ®µ | 说æ | å端æ¾ç¤ºä½ç½® | |
| | | |------|------|------------| |
| | | | `moduleCode` | 模åç¼å· | è¿æ¥é
ç½® â æ¨¡åç¼å· | |
| | | | `protocolType` | é讯åè®® | è¿æ¥é
ç½® â é讯åè®® | |
| | | | `timeout` | è¶
æ¶æ¶é´(ç§) | è¿æ¥é
ç½® â è¶
æ¶æ¶é´ | |
| | | | `retryCount` | éè¯æ¬¡æ° | è¿æ¥é
ç½® â éè¯æ¬¡æ° | |
| | | | `heartbeatInterval` | å¿è·³é´é(ç§) | è¿æ¥é
ç½® â å¿è·³é´é | |
| | | |
| | | #### 2.2 `plcConfig` - PLCå°åé
ç½® |
| | | **å端ä½ç½®**ï¼"PLC å°åé
ç½®"å¡ç |
| | | |
| | | | åæ®µ | 说æ | å端æ¾ç¤ºä½ç½® | |
| | | |------|------|------------| |
| | | | `dbArea` | DBå | PLCå°åé
ç½® â DBå | |
| | | | `beginIndex` | èµ·å§ç´¢å¼ | PLCå°åé
ç½® â èµ·å§ç´¢å¼ | |
| | | | `autoModeInterval` | èªå¨é´é(ms) | PLCå°åé
ç½® â èªå¨é´é | |
| | | | `plcType` | PLCç±»å | åºæ¬ä¿¡æ¯ â PLCç±»åï¼ä½ä¿åå¨è¿éï¼ | |
| | | |
| | | #### 2.3 `plcProjectId` - PLC项ç®ID |
| | | **说æ**ï¼ç¨äºæ è¯PLC项ç®ï¼å¨PLCæä½æ¶ä½¿ç¨ |
| | | |
| | | #### 2.4 `deviceLogic` - 设å¤é»è¾åæ° |
| | | **å端ä½ç½®**ï¼"设å¤é»è¾é
ç½®"å¡çï¼æ ¹æ®è®¾å¤ç±»å卿æ¾ç¤ºï¼ |
| | | |
| | | **ä¸å¤§è½¦è®¾å¤**ï¼ |
| | | - `vehicleCapacity`: 车è¾å®¹é |
| | | - `glassIntervalMs`: ç»çé´é(ms) |
| | | - `autoFeed`: èªå¨ä¸æ |
| | | - `maxRetryCount`: æå¤§éè¯æ¬¡æ° |
| | | - `positionMapping`: ä½ç½®æ å°å¯¹è±¡ |
| | | |
| | | **大çç设å¤**ï¼ |
| | | - `glassSize`: ç»ç尺寸 |
| | | - `processingTime`: å¤çæ¶é´(ms) |
| | | - `autoProcess`: èªå¨å¤ç |
| | | - `maxRetryCount`: æå¤§éè¯æ¬¡æ° |
| | | |
| | | **ç»çåå¨è®¾å¤**ï¼ |
| | | - `storageCapacity`: åå¨å®¹é |
| | | - `retrievalMode`: åè´§æ¨¡å¼ (FIFO/LIFO/RANDOM) |
| | | - `autoStore`: èªå¨åå¨ |
| | | - `autoRetrieve`: èªå¨åè´§ |
| | | - `maxRetryCount`: æå¤§éè¯æ¬¡æ° |
| | | |
| | | --- |
| | | |
| | | ## åæ®µå¯¹æ¯æ»ç» |
| | | |
| | | | åæ®µ | åå¨å
容 | å端ä½ç½® | ç¨é | |
| | | |------|---------|---------|------| |
| | | | `config_json` | éç¨é®å¼å¯¹åæ° | "é
ç½®åæ°"å¡ç | èªå®ä¹åæ°ï¼çµæ´»é
ç½® | |
| | | | `extra_params.connectionConfig` | è¿æ¥ç¸å
³é
ç½® | "è¿æ¥é
ç½®"å¡ç | éè®¯è¿æ¥åæ° | |
| | | | `extra_params.plcConfig` | PLCå°åé
ç½® | "PLCå°åé
ç½®"å¡ç | PLC读åå°ååæ° | |
| | | | `extra_params.deviceLogic` | 设å¤é»è¾åæ° | "设å¤é»è¾é
ç½®"å¡ç | 设å¤ä¸å¡é»è¾åæ° | |
| | | | `extra_params.plcProjectId` | 项ç®ID | éèåæ®µ | å
é¨ä½¿ç¨ | |
| | | |
| | | --- |
| | | |
| | | ## 使ç¨å»ºè®® |
| | | |
| | | 1. **config_json**ï¼ç¨äºåå¨ä¸å¸¸ç¨çãèªå®ä¹çé
ç½®åæ° |
| | | 2. **extra_params.connectionConfig**ï¼ç¨äºåå¨è¿æ¥ç¸å
³çæ åé
ç½® |
| | | 3. **extra_params.plcConfig**ï¼ç¨äºåå¨PLCå°åç¸å
³çé
ç½® |
| | | 4. **extra_params.deviceLogic**ï¼ç¨äºåå¨è®¾å¤ä¸å¡é»è¾ç¸å
³çé
ç½®ï¼æ ¹æ®è®¾å¤ç±»åä¸åï¼ |
| | | |
| | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("device_config") |
| | | @Schema(name = "DeviceConfig", description = "设å¤é
置信æ¯") |
| | | @ApiModel(value = "设å¤é
置信æ¯") |
| | | public class DeviceConfig { |
| | | |
| | | @Schema(description = "设å¤ID", example = "1") |
| | | @ApiModelProperty(value = "设å¤ID", example = "1") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @Schema(description = "设å¤å¯ä¸æ è¯", example = "DEVICE_001") |
| | | @ApiModelProperty(value = "设å¤å¯ä¸æ è¯", example = "DEVICE_001") |
| | | @TableField("device_id") |
| | | private String deviceId; |
| | | |
| | | @Schema(description = "设å¤åç§°", example = "ä¸å¤§è½¦è®¾å¤1") |
| | | @ApiModelProperty(value = "设å¤åç§°", example = "ä¸å¤§è½¦è®¾å¤1") |
| | | @TableField("device_name") |
| | | private String deviceName; |
| | | |
| | | @Schema(description = "设å¤ç¼å·", example = "DEV_001") |
| | | @ApiModelProperty(value = "设å¤ç¼å·", example = "DEV_001") |
| | | @TableField("device_code") |
| | | private String deviceCode; |
| | | |
| | | @Schema(description = "设å¤ç±»å", example = "ä¸å¤§è½¦/大çç/ç»çåå¨") |
| | | @ApiModelProperty(value = "设å¤ç±»å", example = "ä¸å¤§è½¦/大çç/ç»çåå¨") |
| | | @TableField("device_type") |
| | | private String deviceType; |
| | | |
| | | @Schema(description = "æå±é¡¹ç®ID", example = "1") |
| | | @ApiModelProperty(value = "æå±é¡¹ç®ID", example = "1") |
| | | @TableField("project_id") |
| | | private Long projectId; |
| | | |
| | | @Schema(description = "PLC IPå°å", example = "192.168.1.100") |
| | | @ApiModelProperty(value = "PLC IPå°å", example = "192.168.1.100") |
| | | @TableField("plc_ip") |
| | | private String plcIp; |
| | | |
| | | @Schema(description = "PLC端å£", example = "102") |
| | | @ApiModelProperty(value = "PLC端å£", example = "102") |
| | | @TableField("plc_port") |
| | | private Integer plcPort; |
| | | |
| | | @Schema(description = "设å¤ç¶æ", example = "å¨çº¿/离线/ç»´æ¤ä¸/æ
é") |
| | | @ApiModelProperty(value = "设å¤ç¶æ", example = "å¨çº¿/离线/ç»´æ¤ä¸/æ
é") |
| | | @TableField("status") |
| | | private String status; |
| | | |
| | | @Schema(description = "PLCç±»å", example = "S7-1200/S7-1500") |
| | | @ApiModelProperty(value = "PLCç±»å", example = "S7-1200/S7-1500") |
| | | @TableField("plc_type") |
| | | private String plcType; |
| | | |
| | | @Schema(description = "模ååç§°", example = "ä¸å¤§è½¦æ¨¡å") |
| | | @ApiModelProperty(value = "模ååç§°", example = "ä¸å¤§è½¦æ¨¡å") |
| | | @TableField("module_name") |
| | | private String moduleName; |
| | | |
| | | @Schema(description = "æ¯å¦ä¸»æ§è®¾å¤", example = "true") |
| | | @ApiModelProperty(value = "æ¯å¦ä¸»æ§è®¾å¤", example = "true") |
| | | @TableField("is_primary") |
| | | private Boolean isPrimary; |
| | | |
| | | @Schema(description = "æ¯å¦å¯ç¨", example = "true") |
| | | @ApiModelProperty(value = "æ¯å¦å¯ç¨", example = "true") |
| | | @TableField("enabled") |
| | | private Boolean enabled; |
| | | |
| | | @Schema(description = "设å¤ç¹å®é
ç½®JSON", example = "{\"vehicleCapacity\": 6000, \"glassIntervalMs\": 1000}") |
| | | @ApiModelProperty(value = "设å¤ç¹å®é
ç½®JSON", example = "{\"vehicleCapacity\": 6000, \"glassIntervalMs\": 1000}") |
| | | @TableField("config_json") |
| | | private String configJson; |
| | | |
| | | @Schema(description = "è®¾å¤æè¿°", example = "ä¸å¤§è½¦è®¾å¤1") |
| | | @ApiModelProperty(value = "è®¾å¤æè¿°", example = "ä¸å¤§è½¦è®¾å¤1") |
| | | @TableField("description") |
| | | private String description; |
| | | |
| | | @Schema(description = "æ©å±åæ°JSON", example = "{\"timeout\": 5000, \"retries\": 3}") |
| | | @ApiModelProperty(value = "æ©å±åæ°JSON", example = "{\"timeout\": 5000, \"retries\": 3}") |
| | | @TableField("extra_params") |
| | | private String extraParams; |
| | | |
| | | @Schema(description = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @ApiModelProperty(value = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @TableField("is_deleted") |
| | | @TableLogic |
| | | private Integer isDeleted; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @ApiModelProperty(value = "å建æ¶é´") |
| | | @TableField(value = "created_time", fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date createdTime; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @ApiModelProperty(value = "æ´æ°æ¶é´") |
| | | @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date updatedTime; |
| | | |
| | | @Schema(description = "å建人", example = "system") |
| | | @ApiModelProperty(value = "å建人", example = "system") |
| | | @TableField(value = "created_by", fill = FieldFill.INSERT) |
| | | private String createdBy; |
| | | |
| | | @Schema(description = "æ´æ°äºº", example = "system") |
| | | @ApiModelProperty(value = "æ´æ°äºº", example = "system") |
| | | @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE) |
| | | private String updatedBy; |
| | | |
| | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("device_group_config") |
| | | @Schema(name = "DeviceGroupConfig", description = "设å¤ç»é
置信æ¯") |
| | | @ApiModel(value = "DeviceGroupConfig", description = "设å¤ç»é
置信æ¯") |
| | | public class DeviceGroupConfig { |
| | | |
| | | @Schema(description = "设å¤ç»ID", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç»ID", example = "1") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @Schema(description = "设å¤ç»åç§°", example = "ç产线A") |
| | | @ApiModelProperty(value = "设å¤ç»åç§°", example = "ç产线A") |
| | | @TableField("group_name") |
| | | private String groupName; |
| | | |
| | | @Schema(description = "设å¤ç»ç¼å·", example = "GROUP_001") |
| | | @ApiModelProperty(value = "设å¤ç»ç¼å·", example = "GROUP_001") |
| | | @TableField("group_code") |
| | | private String groupCode; |
| | | |
| | | @Schema(description = "设å¤ç»ç±»åï¼1-ç产线ï¼2-æµè¯çº¿ï¼3-è¾
å©è®¾å¤ç»", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç»ç±»åï¼1-ç产线ï¼2-æµè¯çº¿ï¼3-è¾
å©è®¾å¤ç»", example = "1") |
| | | @TableField("group_type") |
| | | private Integer groupType; |
| | | |
| | | @Schema(description = "æå±é¡¹ç®ID", example = "1") |
| | | @ApiModelProperty(value = "æå±é¡¹ç®ID", example = "1") |
| | | @TableField("project_id") |
| | | private Long projectId; |
| | | |
| | | @Schema(description = "设å¤ç»ç¶æï¼0-åç¨ï¼1-å¯ç¨ï¼3-ç»´æ¤ä¸", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç»ç¶æï¼0-åç¨ï¼1-å¯ç¨ï¼3-ç»´æ¤ä¸", example = "1") |
| | | @TableField("status") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "æå¤§å¹¶åè®¾å¤æ°", example = "3") |
| | | @ApiModelProperty(value = "æå¤§å¹¶åè®¾å¤æ°", example = "3") |
| | | @TableField("max_concurrent_devices") |
| | | private Integer maxConcurrentDevices; |
| | | |
| | | @Schema(description = "å¿è·³æ£æµé´é(ç§)", example = "30") |
| | | @ApiModelProperty(value = "å¿è·³æ£æµé´é(ç§)", example = "30") |
| | | @TableField("heartbeat_interval") |
| | | private Integer heartbeatInterval; |
| | | |
| | | @Schema(description = "éä¿¡è¶
æ¶æ¶é´(毫ç§)", example = "5000") |
| | | @ApiModelProperty(value = "éä¿¡è¶
æ¶æ¶é´(毫ç§)", example = "5000") |
| | | @TableField("communication_timeout") |
| | | private Integer communicationTimeout; |
| | | |
| | | @Schema(description = "设å¤ç»æè¿°", example = "ç产线A设å¤ç»") |
| | | @ApiModelProperty(value = "设å¤ç»æè¿°", example = "ç产线A设å¤ç»") |
| | | @TableField("description") |
| | | private String description; |
| | | |
| | | @Schema(description = "æ©å±é
ç½®JSON", example = "{\"retryTimes\": 3, \"batchSize\": 100}") |
| | | @ApiModelProperty(value = "æ©å±é
ç½®JSON", example = "{\"retryTimes\": 3, \"batchSize\": 100}") |
| | | @TableField("extra_config") |
| | | private String extraConfig; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @ApiModelProperty(value = "å建æ¶é´") |
| | | @TableField(value = "created_time", fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date createdTime; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @ApiModelProperty(value = "æ´æ°æ¶é´") |
| | | @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date updatedTime; |
| | | |
| | | @Schema(description = "å建人", example = "system") |
| | | @ApiModelProperty(value = "å建人", example = "system") |
| | | @TableField(value = "created_by", fill = FieldFill.INSERT) |
| | | private String createdBy; |
| | | |
| | | @Schema(description = "æ´æ°äºº", example = "system") |
| | | @ApiModelProperty(value = "æ´æ°äºº", example = "system") |
| | | @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE) |
| | | private String updatedBy; |
| | | |
| | | @Schema(description = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @ApiModelProperty(value = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @TableField("is_deleted") |
| | | @TableLogic |
| | | private Integer isDeleted; |
| | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("device_group_relation") |
| | | @Schema(name = "DeviceGroupRelation", description = "设å¤ç»ä¸è®¾å¤å
³èå
³ç³»") |
| | | @ApiModel(value = "DeviceGroupRelation", description = "设å¤ç»ä¸è®¾å¤å
³èå
³ç³»") |
| | | public class DeviceGroupRelation { |
| | | |
| | | @Schema(description = "å
³èID", example = "1") |
| | | @ApiModelProperty(value = "å
³èID", example = "1") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @Schema(description = "设å¤ç»ID", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç»ID", example = "1") |
| | | @TableField("group_id") |
| | | private Long groupId; |
| | | |
| | | @Schema(description = "设å¤ID", example = "1") |
| | | @ApiModelProperty(value = "设å¤ID", example = "1") |
| | | @TableField("device_id") |
| | | private Long deviceId; |
| | | |
| | | @Schema(description = "设å¤å¨ç»å
çä¼å
级ï¼1-æé«ï¼10-æä½", example = "1") |
| | | @ApiModelProperty(value = "设å¤å¨ç»å
çä¼å
级ï¼1-æé«ï¼10-æä½", example = "1") |
| | | @TableField("priority") |
| | | private Integer priority; |
| | | |
| | | @Schema(description = "设å¤å¨ç»å
çè§è²ï¼1-主æ§ï¼2-åä½ï¼3-çæ§", example = "1") |
| | | @ApiModelProperty(value = "设å¤å¨ç»å
çè§è²ï¼1-主æ§ï¼2-åä½ï¼3-çæ§", example = "1") |
| | | @TableField("role") |
| | | private Integer role; |
| | | |
| | | @Schema(description = "设å¤å¨è¯¥ç»ä¸çç¶æï¼0-æªé
ç½®ï¼1-æ£å¸¸ï¼2-æ
éï¼3-ç»´æ¤", example = "1") |
| | | @ApiModelProperty(value = "设å¤å¨è¯¥ç»ä¸çç¶æï¼0-æªé
ç½®ï¼1-æ£å¸¸ï¼2-æ
éï¼3-ç»´æ¤", example = "1") |
| | | @TableField("status") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "è¿æ¥é¡ºåºï¼æ°å¼è¶å°è¶å
è¿æ¥", example = "1") |
| | | @ApiModelProperty(value = "è¿æ¥é¡ºåºï¼æ°å¼è¶å°è¶å
è¿æ¥", example = "1") |
| | | @TableField("connection_order") |
| | | private Integer connectionOrder; |
| | | |
| | | @Schema(description = "å
³èæè¿°", example = "主æ§è®¾å¤ï¼è´è´£æ´ä½åè°") |
| | | @ApiModelProperty(value = "å
³èæè¿°", example = "主æ§è®¾å¤ï¼è´è´£æ´ä½åè°") |
| | | @TableField("relation_desc") |
| | | private String relationDesc; |
| | | |
| | | @Schema(description = "æ©å±åæ°JSON", example = "{\"timeout\": 5000, \"retryPolicy\": \"exponential\"}") |
| | | @ApiModelProperty(value = "æ©å±åæ°JSON", example = "{\"timeout\": 5000, \"retryPolicy\": \"exponential\"}") |
| | | @TableField("extra_params") |
| | | private String extraParams; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @ApiModelProperty(value = "å建æ¶é´") |
| | | @TableField(value = "created_time", fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date createdTime; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @ApiModelProperty(value = "æ´æ°æ¶é´") |
| | | @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date updatedTime; |
| | | |
| | | @Schema(description = "å建人", example = "system") |
| | | @ApiModelProperty(value = "å建人", example = "system") |
| | | @TableField(value = "created_by", fill = FieldFill.INSERT) |
| | | private String createdBy; |
| | | |
| | | @Schema(description = "æ´æ°äºº", example = "system") |
| | | @ApiModelProperty(value = "æ´æ°äºº", example = "system") |
| | | @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE) |
| | | private String updatedBy; |
| | | |
| | | @Schema(description = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @ApiModelProperty(value = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @TableField("is_deleted") |
| | | @TableLogic |
| | | private Integer isDeleted; |
| | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("device_interaction_execution") |
| | | @Schema(name = "DeviceInteractionExecution", description = "设å¤äº¤äºæ§è¡è®°å½") |
| | | @ApiModel(value = "DeviceInteractionExecution", description = "设å¤äº¤äºæ§è¡è®°å½") |
| | | public class DeviceInteractionExecution { |
| | | |
| | | @Schema(description = "æ§è¡è®°å½ID", example = "1") |
| | | @ApiModelProperty(value = "æ§è¡è®°å½ID", example = "1") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @Schema(description = "å
³èç交äºé»è¾ID", example = "1") |
| | | @ApiModelProperty(value = "å
³èç交äºé»è¾ID", example = "1") |
| | | @TableField("logic_id") |
| | | private Long logicId; |
| | | |
| | | @Schema(description = "设å¤ç»ID", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç»ID", example = "1") |
| | | @TableField("group_id") |
| | | private Long groupId; |
| | | |
| | | @Schema(description = "æå±é¡¹ç®ID", example = "1") |
| | | @ApiModelProperty(value = "æå±é¡¹ç®ID", example = "1") |
| | | @TableField("project_id") |
| | | private Long projectId; |
| | | |
| | | @Schema(description = "æ§è¡æ¹æ¬¡å·", example = "EXEC_20241030_001") |
| | | @ApiModelProperty(value = "æ§è¡æ¹æ¬¡å·", example = "EXEC_20241030_001") |
| | | @TableField("batch_no") |
| | | private String batchNo; |
| | | |
| | | @Schema(description = "æ§è¡ç¶æï¼0-çå¾
ï¼1-æ§è¡ä¸ï¼2-æåï¼3-失败ï¼4-è¶
æ¶ï¼5-åæ¶", example = "0") |
| | | @ApiModelProperty(value = "æ§è¡ç¶æï¼0-çå¾
ï¼1-æ§è¡ä¸ï¼2-æåï¼3-失败ï¼4-è¶
æ¶ï¼5-åæ¶", example = "0") |
| | | @TableField("status") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "æ§è¡æ¨¡å¼ï¼1-æå¨ï¼2-èªå¨ï¼3-宿¶", example = "2") |
| | | @ApiModelProperty(value = "æ§è¡æ¨¡å¼ï¼1-æå¨ï¼2-èªå¨ï¼3-宿¶", example = "2") |
| | | @TableField("execution_mode") |
| | | private Integer executionMode; |
| | | |
| | | @Schema(description = "å¼å§æ§è¡æ¶é´") |
| | | @ApiModelProperty(value = "å¼å§æ§è¡æ¶é´") |
| | | @TableField("start_time") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date startTime; |
| | | |
| | | @Schema(description = "ç»ææ§è¡æ¶é´") |
| | | @ApiModelProperty(value = "ç»ææ§è¡æ¶é´") |
| | | @TableField("end_time") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date endTime; |
| | | |
| | | @Schema(description = "æ§è¡èæ¶(毫ç§)", example = "25000") |
| | | @ApiModelProperty(value = "æ§è¡èæ¶(毫ç§)", example = "25000") |
| | | @TableField("execution_duration") |
| | | private Long executionDuration; |
| | | |
| | | @Schema(description = "æ§è¡è¿åº¦ï¼0-100", example = "80") |
| | | @ApiModelProperty(value = "æ§è¡è¿åº¦ï¼0-100", example = "80") |
| | | @TableField("progress") |
| | | private Integer progress; |
| | | |
| | | @Schema(description = "å½åæ§è¡çæ¥éª¤åºå·", example = "3") |
| | | @ApiModelProperty(value = "å½åæ§è¡çæ¥éª¤åºå·", example = "3") |
| | | @TableField("current_step") |
| | | private Integer currentStep; |
| | | |
| | | @Schema(description = "æ»æ¥éª¤æ°", example = "10") |
| | | @ApiModelProperty(value = "æ»æ¥éª¤æ°", example = "10") |
| | | @TableField("total_steps") |
| | | private Integer totalSteps; |
| | | |
| | | @Schema(description = "æåæ§è¡çè®¾å¤æ°é", example = "3") |
| | | @ApiModelProperty(value = "æåæ§è¡çè®¾å¤æ°é", example = "3") |
| | | @TableField("success_devices") |
| | | private Integer successDevices; |
| | | |
| | | @Schema(description = "失败çè®¾å¤æ°é", example = "0") |
| | | @ApiModelProperty(value = "失败çè®¾å¤æ°é", example = "0") |
| | | @TableField("failed_devices") |
| | | private Integer failedDevices; |
| | | |
| | | @Schema(description = "è§¦åæ§è¡çæä½äºº", example = "admin") |
| | | @ApiModelProperty(value = "è§¦åæ§è¡çæä½äºº", example = "admin") |
| | | @TableField("triggered_by") |
| | | private String triggeredBy; |
| | | |
| | | @Schema(description = "æ§è¡ç»ææè¿°", example = "ææè®¾å¤æå宿èªå¨åæµè¯") |
| | | @ApiModelProperty(value = "æ§è¡ç»ææè¿°", example = "ææè®¾å¤æå宿èªå¨åæµè¯") |
| | | @TableField("result_message") |
| | | private String resultMessage; |
| | | |
| | | @Schema(description = "é误信æ¯JSON", example = "{\"deviceId\": 2, \"error\": \"Connection timeout\"}") |
| | | @ApiModelProperty(value = "é误信æ¯JSON", example = "{\"deviceId\": 2, \"error\": \"Connection timeout\"}") |
| | | @TableField("error_details") |
| | | private String errorDetails; |
| | | |
| | | @Schema(description = "æ§è¡æ°æ®ç»è®¡JSON", example = "{\"totalTime\": 25000, \"avgResponseTime\": 120}") |
| | | @ApiModelProperty(value = "æ§è¡æ°æ®ç»è®¡JSON", example = "{\"totalTime\": 25000, \"avgResponseTime\": 120}") |
| | | @TableField("execution_stats") |
| | | private String executionStats; |
| | | |
| | | @Schema(description = "æ©å±åæ°JSON", example = "{\"testDataId\": \"TD_001\", \"environment\": \"prod\"}") |
| | | @ApiModelProperty(value = "æ©å±åæ°JSON", example = "{\"testDataId\": \"TD_001\", \"environment\": \"prod\"}") |
| | | @TableField("extra_params") |
| | | private String extraParams; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @ApiModelProperty(value = "å建æ¶é´") |
| | | @TableField(value = "created_time", fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date createdTime; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @ApiModelProperty(value = "æ´æ°æ¶é´") |
| | | @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date updatedTime; |
| | | |
| | | @Schema(description = "å建人", example = "system") |
| | | @ApiModelProperty(value = "å建人", example = "system") |
| | | @TableField(value = "created_by", fill = FieldFill.INSERT) |
| | | private String createdBy; |
| | | |
| | | @Schema(description = "æ´æ°äºº", example = "system") |
| | | @ApiModelProperty(value = "æ´æ°äºº", example = "system") |
| | | @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE) |
| | | private String updatedBy; |
| | | |
| | | @Schema(description = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @ApiModelProperty(value = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @TableField("is_deleted") |
| | | @TableLogic |
| | | private Integer isDeleted; |
| | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("device_interaction_logic") |
| | | @Schema(name = "DeviceInteractionLogic", description = "设å¤äº¤äºé»è¾é
ç½®") |
| | | @ApiModel(value = "DeviceInteractionLogic", description = "设å¤äº¤äºé»è¾é
ç½®") |
| | | public class DeviceInteractionLogic { |
| | | |
| | | @Schema(description = "é»è¾ID", example = "1") |
| | | @ApiModelProperty(value = "é»è¾ID", example = "1") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @Schema(description = "é»è¾åç§°", example = "大车èªå¨åæµè¯é»è¾") |
| | | @ApiModelProperty(value = "é»è¾åç§°", example = "大车èªå¨åæµè¯é»è¾") |
| | | @TableField("logic_name") |
| | | private String logicName; |
| | | |
| | | @Schema(description = "é»è¾ç¼å·", example = "TRUCK_AUTO_TEST_001") |
| | | @ApiModelProperty(value = "é»è¾ç¼å·", example = "TRUCK_AUTO_TEST_001") |
| | | @TableField("logic_code") |
| | | private String logicCode; |
| | | |
| | | @Schema(description = "æå±æ¨¡åï¼1-ä¸å¤§è½¦ï¼2-ä¸å¤§è½¦ï¼3-转è¿ï¼4-æµè¯", example = "1") |
| | | @ApiModelProperty(value = "æå±æ¨¡åï¼1-ä¸å¤§è½¦ï¼2-ä¸å¤§è½¦ï¼3-转è¿ï¼4-æµè¯", example = "1") |
| | | @TableField("module_type") |
| | | private Integer moduleType; |
| | | |
| | | @Schema(description = "æå±è®¾å¤ç»ID", example = "1") |
| | | @ApiModelProperty(value = "æå±è®¾å¤ç»ID", example = "1") |
| | | @TableField("group_id") |
| | | private Long groupId; |
| | | |
| | | @Schema(description = "é»è¾ç±»åï¼1-é¡ºåºæ§è¡ï¼2-å¹¶è¡æ§è¡ï¼3-æ¡ä»¶æ§è¡ï¼4-å¾ªç¯æ§è¡", example = "1") |
| | | @ApiModelProperty(value = "é»è¾ç±»åï¼1-é¡ºåºæ§è¡ï¼2-å¹¶è¡æ§è¡ï¼3-æ¡ä»¶æ§è¡ï¼4-å¾ªç¯æ§è¡", example = "1") |
| | | @TableField("logic_type") |
| | | private Integer logicType; |
| | | |
| | | @Schema(description = "é»è¾ç¶æï¼0-ç¦ç¨ï¼1-å¯ç¨ï¼3-è°è¯ä¸", example = "1") |
| | | @ApiModelProperty(value = "é»è¾ç¶æï¼0-ç¦ç¨ï¼1-å¯ç¨ï¼3-è°è¯ä¸", example = "1") |
| | | @TableField("status") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "设å¤ç»å
该é»è¾çä¼å
级ï¼1-æé«ï¼10-æä½", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç»å
该é»è¾çä¼å
级ï¼1-æé«ï¼10-æä½", example = "1") |
| | | @TableField("priority") |
| | | private Integer priority; |
| | | |
| | | @Schema(description = "æ§è¡è¶
æ¶æ¶é´(毫ç§)", example = "30000") |
| | | @ApiModelProperty(value = "æ§è¡è¶
æ¶æ¶é´(毫ç§)", example = "30000") |
| | | @TableField("execution_timeout") |
| | | private Integer executionTimeout; |
| | | |
| | | @Schema(description = "éè¯æ¬¡æ°", example = "3") |
| | | @ApiModelProperty(value = "éè¯æ¬¡æ°", example = "3") |
| | | @TableField("retry_times") |
| | | private Integer retryTimes; |
| | | |
| | | @Schema(description = "é»è¾æè¿°", example = "大车èªå¨åæµè¯ç宿´æµç¨æ§å¶") |
| | | @ApiModelProperty(value = "é»è¾æè¿°", example = "大车èªå¨åæµè¯ç宿´æµç¨æ§å¶") |
| | | @TableField("description") |
| | | private String description; |
| | | |
| | | @Schema(description = "äº¤äºæ¥éª¤JSONæ°ç»", example = "[{\"step\": 1, \"deviceId\": 1, \"action\": \"START\", \"params\": {}}]") |
| | | @ApiModelProperty(value = "äº¤äºæ¥éª¤JSONæ°ç»", example = "[{\"step\": 1, \"deviceId\": 1, \"action\": \"START\", \"params\": {}}]") |
| | | @TableField("interaction_steps") |
| | | private String interactionSteps; |
| | | |
| | | @Schema(description = "æ¡ä»¶å¤æé»è¾JSON", example = "{\"conditions\": [{\"field\": \"status\", \"operator\": \"eq\", \"value\": 1}]}") |
| | | @ApiModelProperty(value = "æ¡ä»¶å¤æé»è¾JSON", example = "{\"conditions\": [{\"field\": \"status\", \"operator\": \"eq\", \"value\": 1}]}") |
| | | @TableField("condition_logic") |
| | | private String conditionLogic; |
| | | |
| | | @Schema(description = "æ©å±åæ°JSON", example = "{\"parallelLimit\": 5, \"errorHandling\": \"retry\"}") |
| | | @ApiModelProperty(value = "æ©å±åæ°JSON", example = "{\"parallelLimit\": 5, \"errorHandling\": \"retry\"}") |
| | | @TableField("extra_params") |
| | | private String extraParams; |
| | | |
| | | @Schema(description = "çæ¬å·", example = "1.0.0") |
| | | @ApiModelProperty(value = "çæ¬å·", example = "1.0.0") |
| | | @TableField("version") |
| | | private String version; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @ApiModelProperty(value = "å建æ¶é´") |
| | | @TableField(value = "created_time", fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date createdTime; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @ApiModelProperty(value = "æ´æ°æ¶é´") |
| | | @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date updatedTime; |
| | | |
| | | @Schema(description = "å建人", example = "system") |
| | | @ApiModelProperty(value = "å建人", example = "system") |
| | | @TableField(value = "created_by", fill = FieldFill.INSERT) |
| | | private String createdBy; |
| | | |
| | | @Schema(description = "æ´æ°äºº", example = "system") |
| | | @ApiModelProperty(value = "æ´æ°äºº", example = "system") |
| | | @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE) |
| | | private String updatedBy; |
| | | |
| | | @Schema(description = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @ApiModelProperty(value = "æ¯å¦å é¤ï¼0-å¦ï¼1-æ¯", example = "0") |
| | | @TableField("is_deleted") |
| | | @TableLogic |
| | | private Integer isDeleted; |
| | |
| | | package com.mes.device.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.entity.DeviceGroupRelation; |
| | | import com.mes.device.vo.DeviceGroupVO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | |
| | | "INNER JOIN device_group_relation dgr ON dgc.id = dgr.group_id " + |
| | | "WHERE dgr.device_id = #{deviceId} AND dgr.is_deleted = 0 AND dgc.is_deleted = 0") |
| | | List<DeviceGroupVO.GroupInfo> getDeviceGroups(@Param("deviceId") Long deviceId); |
| | | |
| | | /** |
| | | * è·åæè¿æ¥é¡ºåºæåºç设å¤é
ç½®å表 |
| | | * |
| | | * @param groupId 设å¤ç»ID |
| | | * @return 设å¤é
ç½®éå |
| | | */ |
| | | @Select("SELECT d.* " + |
| | | "FROM device_group_relation dgr " + |
| | | "INNER JOIN device_config d ON dgr.device_id = d.id " + |
| | | "WHERE dgr.group_id = #{groupId} " + |
| | | " AND dgr.is_deleted = 0 " + |
| | | " AND d.is_deleted = 0 " + |
| | | "ORDER BY IFNULL(dgr.connection_order, 0) ASC, dgr.id ASC") |
| | | List<DeviceConfig> getOrderedDeviceConfigs(@Param("groupId") Long groupId); |
| | | } |
| | |
| | | package com.mes.device.request; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | |
| | | * @since 2025-07-12 |
| | | */ |
| | | @Data |
| | | @Schema(description = "设å¤é
ç½®æä½è¯·æ±ä½") |
| | | @ApiModel(description = "设å¤é
ç½®æä½è¯·æ±ä½") |
| | | public class DeviceConfigRequest { |
| | | |
| | | @Schema(description = "设å¤ID", example = "1") |
| | | @ApiModelProperty(value = "设å¤ID", example = "1") |
| | | private Long deviceId; |
| | | |
| | | @Schema(description = "设å¤é
置信æ¯") |
| | | @ApiModelProperty(value = "设å¤é
置信æ¯") |
| | | private Object deviceConfig; |
| | | |
| | | @Schema(description = "设å¤IDå表") |
| | | @ApiModelProperty(value = "设å¤IDå表") |
| | | private List<Long> deviceIds; |
| | | |
| | | @Schema(description = "项ç®ID", example = "1") |
| | | @ApiModelProperty(value = "项ç®ID", example = "1") |
| | | private Long projectId; |
| | | |
| | | @Schema(description = "设å¤ç±»å", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç±»å", example = "1") |
| | | private String deviceType; |
| | | |
| | | @Schema(description = "设å¤ç¶æ", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç¶æ", example = "1") |
| | | private String deviceStatus; |
| | | |
| | | @Schema(description = "æç´¢å
³é®è¯", example = "设å¤1") |
| | | @ApiModelProperty(value = "æç´¢å
³é®è¯", example = "设å¤1") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "设å¤ç¼ç ", example = "DEVICE001") |
| | | @ApiModelProperty(value = "设å¤ç¼ç ", example = "DEVICE001") |
| | | private String deviceCode; |
| | | |
| | | @Schema(description = "页ç ", example = "1") |
| | | @ApiModelProperty(value = "页ç ", example = "1") |
| | | private Integer page; |
| | | |
| | | @Schema(description = "æ¯é¡µå¤§å°", example = "10") |
| | | @ApiModelProperty(value = "æ¯é¡µå¤§å°", example = "10") |
| | | private Integer size; |
| | | |
| | | // æé 彿° |
| | |
| | | package com.mes.device.request; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.ArraySchema; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import javax.validation.constraints.NotEmpty; |
| | |
| | | * ç»çåå
¥è¯·æ± |
| | | */ |
| | | @Data |
| | | @Schema(name = "DeviceGlassFeedRequest", description = "设å¤ç»çåå
¥è¯·æ±") |
| | | @ApiModel(value = "DeviceGlassFeedRequest", description = "设å¤ç»çåå
¥è¯·æ±") |
| | | public class DeviceGlassFeedRequest { |
| | | |
| | | @NotNull |
| | | @Schema(description = "设å¤ID", required = true) |
| | | @ApiModelProperty(value = "设å¤ID", required = true) |
| | | private Long deviceId; |
| | | |
| | | @ArraySchema(schema = @Schema(description = "ç»çIDå表", example = "GLS001"), minItems = 1) |
| | | @ApiModelProperty(value = "ç»çIDå表", example = "GLS001") |
| | | private List<String> glassIds; |
| | | |
| | | @Schema(description = "è¿çä½ç½®æ è¯ï¼ä¸æ§å¶åæ°ä¸ç positionMappings 对åºï¼") |
| | | @ApiModelProperty(value = "è¿çä½ç½®æ è¯ï¼ä¸æ§å¶åæ°ä¸ç positionMappings 对åºï¼") |
| | | private String positionCode; |
| | | |
| | | @Schema(description = "ç´æ¥æå®çä½ç½®å¼ï¼ä¼å
级é«äº positionCodeï¼") |
| | | @ApiModelProperty(value = "ç´æ¥æå®çä½ç½®å¼ï¼ä¼å
级é«äº positionCodeï¼") |
| | | private Integer positionValue; |
| | | |
| | | @Schema(description = "æ¯å¦èªå¨åå
¥è¯·æ±å", defaultValue = "true") |
| | | @ApiModelProperty(value = "æ¯å¦èªå¨åå
¥è¯·æ±å", example = "true") |
| | | private Boolean triggerRequest = true; |
| | | } |
| | | |
| | |
| | | package com.mes.device.request; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | |
| | | * @since 2025-07-12 |
| | | */ |
| | | @Data |
| | | @Schema(description = "设å¤ç»æä½è¯·æ±ä½") |
| | | @ApiModel(description = "设å¤ç»æä½è¯·æ±ä½") |
| | | public class DeviceGroupRequest { |
| | | |
| | | @Schema(description = "设å¤ç»ID", example = "1") |
| | | @ApiModelProperty(value = "设å¤ç»ID", example = "1") |
| | | private Long groupId; |
| | | |
| | | @Schema(description = "设å¤ID", example = "1") |
| | | @ApiModelProperty(value = "设å¤ID", example = "1") |
| | | private Long deviceId; |
| | | |
| | | @Schema(description = "设å¤IDå表") |
| | | @ApiModelProperty(value = "设å¤IDå表") |
| | | private List<Long> deviceIds; |
| | | |
| | | @Schema(description = "设å¤ç»IDå表") |
| | | @ApiModelProperty(value = "设å¤ç»IDå表") |
| | | private List<Long> groupIds; |
| | | |
| | | @Schema(description = "设å¤ç»é
置信æ¯") |
| | | @ApiModelProperty(value = "设å¤ç»é
置信æ¯") |
| | | private Object groupConfig; |
| | | |
| | | @Schema(description = "设å¤è§è²", example = "MEMBER") |
| | | @ApiModelProperty(value = "设å¤è§è²", example = "MEMBER") |
| | | private String deviceRole; |
| | | |
| | | // æé 彿° |
| | |
| | | package com.mes.device.request; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import javax.validation.constraints.NotEmpty; |
| | |
| | | * @since 2025-11-17 |
| | | */ |
| | | @Data |
| | | @Schema(name = "DevicePlcBatchRequest", description = "è®¾å¤ PLC æ¹éæä½è¯·æ±") |
| | | @ApiModel(value = "DevicePlcBatchRequest", description = "è®¾å¤ PLC æ¹éæä½è¯·æ±") |
| | | public class DevicePlcBatchRequest implements Serializable { |
| | | |
| | | @NotEmpty(message = "设å¤IDå表ä¸è½ä¸ºç©º") |
| | | @Schema(description = "设å¤IDå表", required = true) |
| | | @ApiModelProperty(value = "设å¤IDå表", required = true) |
| | | private List<Long> deviceIds; |
| | | } |
| | | |
| | |
| | | import com.mes.device.request.DeviceGlassFeedRequest; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 设å¤äº¤äºé»è¾æå¡ |
| | | */ |
| | | public interface DeviceInteractionService { |
| | | |
| | | /** |
| | | * æ§è¡ç»ç䏿åå
¥ |
| | | * æ§è¡ç»ç䏿åå
¥ï¼å
¼å®¹æ§æ¥å£ï¼ |
| | | */ |
| | | DevicePlcVO.OperationResult feedGlass(DeviceGlassFeedRequest request); |
| | | |
| | | /** |
| | | * æ§è¡è®¾å¤é»è¾æä½ï¼æ°æ¥å£ï¼ä½¿ç¨å¤ç卿¶æï¼ |
| | | * |
| | | * @param deviceId 设å¤ID |
| | | * @param operation æä½ç±»åï¼å¦ï¼feedGlass, triggerRequest, triggerReportçï¼ |
| | | * @param params æä½åæ° |
| | | * @return æä½ç»æ |
| | | */ |
| | | DevicePlcVO.OperationResult executeOperation(Long deviceId, String operation, Map<String, Object> params); |
| | | } |
| | | |
| | |
| | | package com.mes.device.service.impl; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.interaction.DeviceLogicHandler; |
| | | import com.mes.interaction.DeviceLogicHandlerFactory; |
| | | import com.mes.device.request.DeviceGlassFeedRequest; |
| | | import com.mes.device.service.DeviceConfigService; |
| | | import com.mes.device.service.DeviceControlProfileService; |
| | | import com.mes.device.service.DeviceInteractionService; |
| | | import com.mes.device.service.DevicePlcOperationService; |
| | |
| | | |
| | | private final DeviceControlProfileService controlProfileService; |
| | | private final DevicePlcOperationService devicePlcOperationService; |
| | | private final DeviceConfigService deviceConfigService; |
| | | private final DeviceLogicHandlerFactory handlerFactory; |
| | | |
| | | /** |
| | | * æ§è¡ç»ç䏿åå
¥ï¼å
¼å®¹æ§æ¥å£ï¼ä¿çåæé»è¾ï¼ |
| | | */ |
| | | @Override |
| | | public DevicePlcVO.OperationResult feedGlass(DeviceGlassFeedRequest request) { |
| | | // ä¼å
ä½¿ç¨æ°çå¤ç卿¶æ |
| | | DeviceConfig deviceConfig = deviceConfigService.getDeviceById(request.getDeviceId()); |
| | | if (deviceConfig != null) { |
| | | DeviceLogicHandler handler = handlerFactory.getHandler(deviceConfig.getDeviceType()); |
| | | if (handler != null) { |
| | | // ä½¿ç¨æ°æ¶ææ§è¡ |
| | | Map<String, Object> params = new HashMap<>(); |
| | | params.put("glassIds", request.getGlassIds()); |
| | | params.put("positionCode", request.getPositionCode()); |
| | | params.put("positionValue", request.getPositionValue()); |
| | | params.put("triggerRequest", request.getTriggerRequest()); |
| | | return handler.execute(deviceConfig, "feedGlass", params); |
| | | } |
| | | } |
| | | |
| | | // é级å°åæé»è¾ï¼å
¼å®¹æ§ä»£ç ï¼ |
| | | DeviceControlProfile profile = controlProfileService.getProfile(request.getDeviceId()); |
| | | Map<String, Object> payload = buildGlassPayload(profile, request); |
| | | String opName = "ç»ç䏿"; |
| | |
| | | return devicePlcOperationService.writeFields(request.getDeviceId(), payload, opName); |
| | | } |
| | | |
| | | /** |
| | | * æ§è¡è®¾å¤é»è¾æä½ï¼æ°æ¥å£ï¼ä½¿ç¨å¤ç卿¶æï¼ |
| | | */ |
| | | @Override |
| | | public DevicePlcVO.OperationResult executeOperation(Long deviceId, String operation, Map<String, Object> params) { |
| | | // è·å设å¤é
ç½® |
| | | DeviceConfig deviceConfig = deviceConfigService.getDeviceById(deviceId); |
| | | if (deviceConfig == null) { |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("设å¤ä¸åå¨: " + deviceId) |
| | | .build(); |
| | | } |
| | | |
| | | // è·å对åºçå¤çå¨ |
| | | DeviceLogicHandler handler = handlerFactory.getHandler(deviceConfig.getDeviceType()); |
| | | if (handler == null) { |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("䏿¯æç设å¤ç±»å: " + deviceConfig.getDeviceType()) |
| | | .build(); |
| | | } |
| | | |
| | | // æ§è¡æä½ |
| | | return handler.execute(deviceConfig, operation, params != null ? params : new HashMap<>()); |
| | | } |
| | | |
| | | /** |
| | | * æå»ºç»çä¸ææ°æ®ï¼å
¼å®¹æ§é»è¾ï¼ |
| | | */ |
| | | private Map<String, Object> buildGlassPayload(DeviceControlProfile profile, DeviceGlassFeedRequest request) { |
| | | if (CollectionUtils.isEmpty(profile.getGlassSlots())) { |
| | | throw new IllegalStateException("è®¾å¤æªé
ç½®ç»çæ§½ä½ä¿¡æ¯"); |
| | |
| | | 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; |
| | |
| | | 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 plcProjectId = extraParams.get(PLC_PROJECT_ID_KEY); |
| | | if (plcProjectId != null) { |
| | | return String.valueOf(plcProjectId); |
| | | Object plcProjectIdFromExtra = extraParams.get(PLC_PROJECT_ID_KEY); |
| | | if (plcProjectIdFromExtra != null) { |
| | | return String.valueOf(plcProjectIdFromExtra); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("è§£æè®¾å¤æ©å±åæ°å¤±è´¥, deviceId={}", device.getId(), e); |
| | |
| | | throw new IllegalStateException("æ æ³è§£æè®¾å¤ç PLC é¡¹ç®æ è¯, deviceId=" + device.getId()); |
| | | } |
| | | |
| | | private enum PlcOperationType { |
| | | public enum PlcOperationType { |
| | | REQUEST("PLC请æ±", "PLC 请æ±åéæå", "PLC 请æ±åé失败"), |
| | | REPORT("PLCæ±æ¥", "PLC æ±æ¥æ¨¡ææå", "PLC æ±æ¥æ¨¡æå¤±è´¥"), |
| | | RESET("PLCéç½®", "PLC ç¶æå·²éç½®", "PLC ç¶æé置失败"); |
| New file |
| | |
| | | package com.mes.device.util; |
| | | |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * å·¥å
·ç±»ï¼è§£æ DeviceConfig.configJson çå¤ç§æ ¼å¼ |
| | | * å
¼å®¹å¯¹è±¡ç»æå [{paramKey,paramValue}] æ°ç»ç»æ |
| | | */ |
| | | @Slf4j |
| | | public final class ConfigJsonHelper { |
| | | |
| | | private static final TypeReference<Map<String, Object>> MAP_TYPE = |
| | | new TypeReference<Map<String, Object>>() {}; |
| | | private static final TypeReference<List<Map<String, Object>>> LIST_TYPE = |
| | | new TypeReference<List<Map<String, Object>>>() {}; |
| | | |
| | | private ConfigJsonHelper() { |
| | | } |
| | | |
| | | /** |
| | | * å° configJson è§£æä¸º key-value ç Map |
| | | * |
| | | * @param configJson åå§ JSON å符串 |
| | | * @param objectMapper å
Œ
± ObjectMapper |
| | | * @return åæ°æ å°ï¼ä¸å¯ä¿®æ¹ |
| | | */ |
| | | public static Map<String, Object> parseToMap(String configJson, ObjectMapper objectMapper) { |
| | | if (configJson == null || configJson.trim().isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | String trimmed = configJson.trim(); |
| | | try { |
| | | if (trimmed.startsWith("[")) { |
| | | List<Map<String, Object>> items = objectMapper.readValue(trimmed, LIST_TYPE); |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | for (Map<String, Object> item : items) { |
| | | if (item == null) { |
| | | continue; |
| | | } |
| | | Object keyObj = firstNonNull(item.get("paramKey"), item.get("key")); |
| | | if (keyObj == null) { |
| | | continue; |
| | | } |
| | | Object valueObj = firstNonNull(item.get("paramValue"), item.get("value")); |
| | | result.put(String.valueOf(keyObj), valueObj); |
| | | } |
| | | return result; |
| | | } else if (trimmed.startsWith("{")) { |
| | | return objectMapper.readValue(trimmed, MAP_TYPE); |
| | | } else { |
| | | log.warn("æªç¥ç configJson æ ¼å¼: {}", trimmed); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("è§£æ configJson 失败: {}", trimmed, e); |
| | | } |
| | | return Collections.emptyMap(); |
| | | } |
| | | |
| | | private static Object firstNonNull(Object first, Object second) { |
| | | return Objects.nonNull(first) ? first : second; |
| | | } |
| | | } |
| | | |
| | |
| | | package com.mes.device.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @Schema(name = "DeviceControlProfile", description = "è®¾å¤æ§å¶åæ°é
ç½®") |
| | | @ApiModel(value = "DeviceControlProfile", description = "è®¾å¤æ§å¶åæ°é
ç½®") |
| | | public class DeviceControlProfile implements Serializable { |
| | | |
| | | @Schema(description = "èæ/线é度ï¼mm/sï¼") |
| | | @ApiModelProperty(value = "èæ/线é度ï¼mm/sï¼") |
| | | private Integer lineSpeed; |
| | | |
| | | @Schema(description = "ç»çé¿åº¦ï¼mmï¼") |
| | | @ApiModelProperty(value = "ç»çé¿åº¦ï¼mmï¼") |
| | | private Integer glassLength; |
| | | |
| | | @Schema(description = "ç¼åæ°é/æ§½ä½æ°é") |
| | | @ApiModelProperty(value = "ç¼åæ°é/æ§½ä½æ°é") |
| | | private Integer bufferCount; |
| | | |
| | | @Schema(description = "æ¯å¦èªå¨è§¦åPLC请æ±") |
| | | @ApiModelProperty(value = "æ¯å¦èªå¨è§¦åPLC请æ±") |
| | | private Boolean autoRequest; |
| | | |
| | | @Schema(description = "PLC请æ±å段å", defaultValue = "plcRequest") |
| | | @ApiModelProperty(value = "PLC请æ±å段å") |
| | | private String requestField = "plcRequest"; |
| | | |
| | | @Schema(description = "è¿çä½ç½®å段å", defaultValue = "inPosition") |
| | | @ApiModelProperty(value = "è¿çä½ç½®å段å") |
| | | private String positionField = "inPosition"; |
| | | |
| | | @Schema(description = "ç»çæ°éåæ®µå", defaultValue = "plcGlassCount") |
| | | @ApiModelProperty(value = "ç»çæ°éåæ®µå") |
| | | private String glassCountField = "plcGlassCount"; |
| | | |
| | | @Schema(description = "ç»çIDæ§½ä½å段å®ä¹") |
| | | @ApiModelProperty(value = "ç»çIDæ§½ä½å段å®ä¹") |
| | | private List<GlassSlot> glassSlots; |
| | | |
| | | @Schema(description = "ä½ç½®æ å°ï¼å¦ï¼{ \"station1\":1 }") |
| | | @ApiModelProperty(value = "ä½ç½®æ å°ï¼å¦ï¼{ \"station1\":1 }") |
| | | private Map<String, Integer> positionMappings; |
| | | |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @Builder |
| | | @Schema(name = "GlassSlot", description = "ç»çIDæ§½ä½") |
| | | @ApiModel(value = "GlassSlot", description = "ç»çIDæ§½ä½") |
| | | public static class GlassSlot implements Serializable { |
| | | @Schema(description = "æ§½ä½åºå·ï¼ä»1å¼å§") |
| | | @ApiModelProperty(value = "æ§½ä½åºå·ï¼ä»1å¼å§") |
| | | private Integer order; |
| | | |
| | | @Schema(description = "PLCåæ®µåï¼ä¾å¦ plcGlassId1") |
| | | @ApiModelProperty(value = "PLCåæ®µåï¼ä¾å¦ plcGlassId1") |
| | | private String field; |
| | | |
| | | @Schema(description = "åæ®µé¿åº¦ï¼å符串é¿åº¦ç") |
| | | @ApiModelProperty(value = "åæ®µé¿åº¦ï¼å符串é¿åº¦ç") |
| | | private Integer length; |
| | | |
| | | @Schema(description = "æ§½ä½æè¿°") |
| | | @ApiModelProperty(value = "æ§½ä½æè¿°") |
| | | private String description; |
| | | } |
| | | } |
| | |
| | | package com.mes.device.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @Schema(name = "DevicePlcOperationResult", description = "PLC æä½ç»æ") |
| | | @ApiModel(value = "DevicePlcOperationResult", description = "PLC æä½ç»æ") |
| | | public static class OperationResult implements Serializable { |
| | | private Long deviceId; |
| | | private String deviceName; |
| | |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @Schema(name = "DevicePlcStatus", description = "PLC ç¶ææ°æ®") |
| | | @ApiModel(value = "DevicePlcStatus", description = "PLC ç¶ææ°æ®") |
| | | public static class StatusInfo implements Serializable { |
| | | private Long deviceId; |
| | | private String deviceName; |
| New file |
| | |
| | | # MES Test Project å¤è®¾å¤èåæµè¯æ©å±æ¹æ¡ |
| | | |
| | | ## ð é¡¹ç®æ¦è¿° |
| | | |
| | | åºäºç°æçMES Test Projectï¼mes-web + mes-plcSendï¼ï¼æ©å±æ¯æå¤è®¾å¤èåæµè¯åè½ï¼å®ç°"ä¸å¤§è½¦è®¾å¤ â 大ççè®¾å¤ â ç»çåå¨è®¾å¤"ç宿´ç产æµç¨èªå¨åæµè¯ã |
| | | |
| | | ## ð¯ æ ¸å¿éæ± |
| | | |
| | | ### ä¸å¡åºæ¯ |
| | | 1. **ä¸å¤§è½¦å请æ±**ï¼æ£æµè½¦è¾å®¹éï¼6000mmå¯é
ç½®ï¼ãç»çè§æ ¼å¹é
ãèææ§å¶ |
| | | 2. **大çç交äº**ï¼ä¸MES大ççä¿¡æ¯æ¯å¯¹éªè¯ãæ¹éå¤çé»è¾ |
| | | 3. **å¤è®¾å¤åè°**ï¼è®¾å¤é´æ°æ®ä¼ éãç¶æåæ¥ãä¾èµç®¡ç |
| | | |
| | | ### ææ¯éæ± |
| | | - æ¯æå¤PLC设å¤å°åæ å° |
| | | - 设å¤ç»é
ç½®å管ç |
| | | - 串è¡/å¹¶è¡æ§è¡æ¨¡å¼ |
| | | - 设å¤é´æ°æ®å
񄧮 |
| | | - 宿¶ç¶æçæ§ |
| | | |
| | | ## ð æ©å±æ¶æè®¾è®¡ |
| | | |
| | | ### 1. å端æ©å±ç»æ |
| | | |
| | | #### 1.1 æ°å¢ç®å½ç»æ |
| | | ``` |
| | | mes-plcSend/src/main/java/com/mes/ |
| | | âââ device/ # 设å¤ç®¡çå± |
| | | â âââ entity/ |
| | | â â âââ DeviceConfig.java # 设å¤é
ç½®å®ä½ |
| | | â â âââ DeviceGroup.java # 设å¤ç»å®ä½ |
| | | â â âââ DeviceStatus.java # 设å¤ç¶æå®ä½ |
| | | â âââ service/ |
| | | â â âââ DeviceService.java # 设å¤ç®¡çæå¡ |
| | | â â âââ DeviceGroupService.java # 设å¤ç»æå¡ |
| | | â â âââ DeviceCoordinationService.java # 设å¤åè°æå¡ |
| | | â âââ controller/ |
| | | â âââ DeviceController.java # 设å¤ç®¡çAPI |
| | | â âââ DeviceGroupController.java # 设å¤ç»ç®¡çAPI |
| | | âââ interaction/ # 交äºé»è¾æ¨¡å |
| | | â âââ base/ |
| | | â â âââ BaseInteraction.java # åºç¡äº¤äºæ½è±¡ |
| | | â â âââ InteractionContext.java # 交äºä¸ä¸æ |
| | | â â âââ InteractionResult.java # 交äºç»æ |
| | | â âââ ä¸å¤§è½¦/ |
| | | â â âââ ä¸å¤§è½¦Interaction.java # ä¸å¤§è½¦äº¤äºé»è¾ |
| | | â â âââ ä¸å¤§è½¦Config.java # ä¸å¤§è½¦é
ç½® |
| | | â âââ 大çç/ |
| | | â â âââ 大ççInteraction.java # 大çç交äºé»è¾ |
| | | â â âââ 大ççConfig.java # 大ççé
ç½® |
| | | â âââ ç»çåå¨/ |
| | | â âââ ç»çåå¨Interaction.java # ç»çåå¨äº¤äºé»è¾ |
| | | â âââ ç»çåå¨Config.java # ç»çåå¨é
ç½® |
| | | âââ task/ # ä»»å¡ç®¡çå± |
| | | âââ entity/ |
| | | â âââ MultiDeviceTask.java # å¤è®¾å¤ä»»å¡å®ä½ |
| | | â âââ TaskStep.java # 任塿¥éª¤å®ä½ |
| | | âââ service/ |
| | | â âââ MultiDeviceTaskService.java # å¤è®¾å¤ä»»å¡æå¡ |
| | | â âââ TaskExecutionEngine.java # 任塿§è¡å¼æ |
| | | âââ controller/ |
| | | âââ MultiDeviceTaskController.java # å¤è®¾å¤ä»»å¡API |
| | | ``` |
| | | |
| | | #### 1.2 æ°æ®åºè¡¨æ©å± |
| | | |
| | | ```sql |
| | | -- 设å¤é
置表 |
| | | CREATE TABLE device_config ( |
| | | id BIGINT PRIMARY KEY AUTO_INCREMENT, |
| | | device_id VARCHAR(50) UNIQUE NOT NULL COMMENT '设å¤ID', |
| | | device_name VARCHAR(100) NOT NULL COMMENT '设å¤åç§°', |
| | | device_type VARCHAR(50) NOT NULL COMMENT '设å¤ç±»å(ä¸å¤§è½¦/大çç/ç»çåå¨)', |
| | | plc_ip VARCHAR(15) NOT NULL COMMENT 'PLC IPå°å', |
| | | plc_type VARCHAR(20) NOT NULL COMMENT 'PLCç±»å', |
| | | module_name VARCHAR(50) NOT NULL COMMENT '模ååç§°', |
| | | is_primary BOOLEAN DEFAULT FALSE COMMENT 'æ¯å¦ä¸»æ§è®¾å¤', |
| | | enabled BOOLEAN DEFAULT TRUE COMMENT 'æ¯å¦å¯ç¨', |
| | | config_json TEXT COMMENT '设å¤ç¹å®é
ç½®(JSON)', |
| | | created_time DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
| | | ); |
| | | |
| | | -- 设å¤ç»é
置表 |
| | | CREATE TABLE device_group ( |
| | | id BIGINT PRIMARY KEY AUTO_INCREMENT, |
| | | group_id VARCHAR(50) UNIQUE NOT NULL COMMENT '设å¤ç»ID', |
| | | group_name VARCHAR(100) NOT NULL COMMENT '设å¤ç»åç§°', |
| | | project_id VARCHAR(50) NOT NULL COMMENT 'å
³è项ç®ID', |
| | | execution_mode ENUM('SERIAL', 'PARALLEL') DEFAULT 'SERIAL' COMMENT 'æ§è¡æ¨¡å¼', |
| | | execution_config JSON COMMENT 'æ§è¡é
ç½®', |
| | | dependencies JSON COMMENT '设å¤ä¾èµå
³ç³»', |
| | | created_time DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
| | | ); |
| | | |
| | | -- 设å¤ç»ä¸è®¾å¤å
³ç³»è¡¨ |
| | | CREATE TABLE device_group_mapping ( |
| | | id BIGINT PRIMARY KEY AUTO_INCREMENT, |
| | | group_id VARCHAR(50) NOT NULL, |
| | | device_id VARCHAR(50) NOT NULL, |
| | | execution_order INT NOT NULL COMMENT 'æ§è¡é¡ºåº', |
| | | FOREIGN KEY (group_id) REFERENCES device_group(group_id) ON DELETE CASCADE, |
| | | FOREIGN KEY (device_id) REFERENCES device_config(device_id) ON DELETE CASCADE, |
| | | UNIQUE KEY uk_group_device (group_id, device_id) |
| | | ); |
| | | |
| | | -- å¤è®¾å¤ä»»å¡è¡¨ |
| | | CREATE TABLE multi_device_task ( |
| | | id BIGINT PRIMARY KEY AUTO_INCREMENT, |
| | | task_id VARCHAR(50) UNIQUE NOT NULL COMMENT 'ä»»å¡ID', |
| | | group_id VARCHAR(50) NOT NULL COMMENT '设å¤ç»ID', |
| | | project_id VARCHAR(50) NOT NULL COMMENT '项ç®ID', |
| | | status ENUM('PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'CANCELLED') DEFAULT 'PENDING', |
| | | current_step INT DEFAULT 0 COMMENT 'å½åæ¥éª¤', |
| | | total_steps INT DEFAULT 0 COMMENT 'æ»æ¥éª¤æ°', |
| | | start_time DATETIME COMMENT 'å¼å§æ¶é´', |
| | | end_time DATETIME COMMENT 'ç»ææ¶é´', |
| | | error_message TEXT COMMENT 'é误信æ¯', |
| | | result_data JSON COMMENT 'ç»ææ°æ®', |
| | | created_time DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
| | | ); |
| | | |
| | | -- 任塿¥éª¤è¯¦æ
表 |
| | | CREATE TABLE task_step_detail ( |
| | | id BIGINT PRIMARY KEY AUTO_INCREMENT, |
| | | task_id VARCHAR(50) NOT NULL COMMENT 'ä»»å¡ID', |
| | | step_order INT NOT NULL COMMENT 'æ¥éª¤é¡ºåº', |
| | | device_id VARCHAR(50) NOT NULL COMMENT '设å¤ID', |
| | | step_name VARCHAR(100) NOT NULL COMMENT 'æ¥éª¤åç§°', |
| | | status ENUM('PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'SKIPPED') DEFAULT 'PENDING', |
| | | start_time DATETIME COMMENT 'æ¥éª¤å¼å§æ¶é´', |
| | | end_time DATETIME COMMENT 'æ¥éª¤ç»ææ¶é´', |
| | | duration_ms BIGINT COMMENT 'æ§è¡èæ¶(毫ç§)', |
| | | input_data JSON COMMENT 'è¾å
¥æ°æ®', |
| | | output_data JSON COMMENT 'è¾åºæ°æ®', |
| | | error_message TEXT COMMENT 'é误信æ¯', |
| | | retry_count INT DEFAULT 0 COMMENT 'éè¯æ¬¡æ°', |
| | | created_time DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | FOREIGN KEY (task_id) REFERENCES multi_device_task(task_id) ON DELETE CASCADE |
| | | ); |
| | | ``` |
| | | |
| | | ### 2. å端æ©å±ç»æ |
| | | |
| | | #### 2.1 æ°å¢å端ç®å½ |
| | | ``` |
| | | mes-web/src/views/plcTest/ |
| | | âââ components/ |
| | | â âââ DeviceManagement/ # 设å¤ç®¡çç»ä»¶ |
| | | â â âââ DeviceList.vue # 设å¤å表 |
| | | â â âââ DeviceConfig.vue # 设å¤é
ç½® |
| | | â â âââ DeviceStatus.vue # 设å¤ç¶æ |
| | | â âââ DeviceGroup/ # 设å¤ç»ç»ä»¶ |
| | | â â âââ GroupList.vue # 设å¤ç»å表 |
| | | â â âââ GroupConfig.vue # 设å¤ç»é
ç½® |
| | | â â âââ GroupTopology.vue # 设å¤ç»ææå¾ |
| | | â âââ MultiDeviceTest/ # å¤è®¾å¤æµè¯ç»ä»¶ |
| | | â â âââ TestOrchestration.vue # æµè¯ç¼æ |
| | | â â âââ ExecutionMonitor.vue # æ§è¡çæ§ |
| | | â â âââ ResultAnalysis.vue # ç»æåæ |
| | | â âââ InteractionLogic/ # 交äºé»è¾ç»ä»¶ |
| | | â âââ ä¸å¤§è½¦Config.vue # ä¸å¤§è½¦é
ç½® |
| | | â âââ 大ççConfig.vue # 大ççé
ç½® |
| | | â âââ ç»çåå¨Config.vue # ç»çåå¨é
ç½® |
| | | ``` |
| | | |
| | | ## ð§ æ ¸å¿å®ç°è®¾è®¡ |
| | | |
| | | ### 3.1 设å¤ç®¡çå®ç° |
| | | |
| | | #### 设å¤é
ç½®å®ä½ |
| | | ```java |
| | | @Data |
| | | @Entity |
| | | @TableName("device_config") |
| | | public class DeviceConfig { |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @TableField("device_id") |
| | | private String deviceId; |
| | | |
| | | @TableField("device_name") |
| | | private String deviceName; |
| | | |
| | | @TableField("device_type") |
| | | private String deviceType; // "ä¸å¤§è½¦" / "大çç" / "ç»çåå¨" |
| | | |
| | | @TableField("plc_ip") |
| | | private String plcIp; |
| | | |
| | | @TableField("plc_type") |
| | | private String plcType; |
| | | |
| | | @TableField("module_name") |
| | | private String moduleName; |
| | | |
| | | @TableField("is_primary") |
| | | private Boolean isPrimary; |
| | | |
| | | @TableField("config_json") |
| | | private String configJson; // 设å¤ç¹å®é
ç½® |
| | | |
| | | // é
ç½®è§£ææ¹æ³ |
| | | public <T> T getConfig(Class<T> clazz) { |
| | | if (StringUtils.isBlank(configJson)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return JsonUtils.fromJson(configJson, clazz); |
| | | } catch (Exception e) { |
| | | log.error("è§£æè®¾å¤é
置失败: {}", deviceId, e); |
| | | return null; |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | #### 设å¤ç®¡çæå¡ |
| | | ```java |
| | | @Service |
| | | public class DeviceService { |
| | | |
| | | @Resource |
| | | private DeviceConfigMapper deviceConfigMapper; |
| | | |
| | | /** |
| | | * 注åè®¾å¤ |
| | | */ |
| | | @Transactional |
| | | public void registerDevice(DeviceConfig device) { |
| | | // éªè¯PLCè¿æ¥ |
| | | validatePlcConnection(device.getPlcIp(), device.getPlcType()); |
| | | |
| | | // ä¿å设å¤é
ç½® |
| | | deviceConfigMapper.insert(device); |
| | | |
| | | // æ´æ°å°åæ å° |
| | | updatePlcAddressMapping(device); |
| | | |
| | | log.info("è®¾å¤æ³¨åæå: {}", device.getDeviceId()); |
| | | } |
| | | |
| | | /** |
| | | * è·å设å¤çPLCå°åæ å° |
| | | */ |
| | | public Map<String, Integer> getDeviceAddressMapping(String deviceId) { |
| | | DeviceConfig device = getDeviceById(deviceId); |
| | | if (device == null) { |
| | | throw new RuntimeException("设å¤ä¸åå¨: " + deviceId); |
| | | } |
| | | |
| | | // æ ¹æ®è®¾å¤ç±»åè·å对åºçå°åæ å°é
ç½® |
| | | String mappingKey = device.getDeviceType() + "_" + device.getModuleName(); |
| | | return plcAddressService.getMappingByKey(mappingKey); |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.2 设å¤ç»ç®¡çå®ç° |
| | | |
| | | #### 设å¤ç»å®ä½ |
| | | ```java |
| | | @Data |
| | | @TableName("device_group") |
| | | public class DeviceGroup { |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @TableField("group_id") |
| | | private String groupId; |
| | | |
| | | @TableField("group_name") |
| | | private String groupName; |
| | | |
| | | @TableField("project_id") |
| | | private String projectId; |
| | | |
| | | @TableField("execution_mode") |
| | | private ExecutionMode executionMode; // SERIAL / PARALLEL |
| | | |
| | | @TableField("execution_config") |
| | | private String executionConfig; |
| | | |
| | | @TableField("dependencies") |
| | | private String dependencies; // JSONæ ¼å¼çä¾èµå
³ç³» |
| | | |
| | | // è·å设å¤ç»ä¸çææè®¾å¤ |
| | | public List<DeviceConfig> getDevices() { |
| | | return deviceGroupService.getDevicesByGroupId(groupId); |
| | | } |
| | | |
| | | // è·åæ§è¡é¡ºåº |
| | | public List<DeviceConfig> getExecutionSequence() { |
| | | return deviceGroupService.getDevicesByOrder(groupId); |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.3 交äºé»è¾å®ç° |
| | | |
| | | #### åºç¡äº¤äºæ¥å£ |
| | | ```java |
| | | /** |
| | | * 设å¤äº¤äºé»è¾æ¥å£ |
| | | */ |
| | | public interface DeviceInteraction { |
| | | |
| | | /** |
| | | * æ§è¡äº¤äºé»è¾ |
| | | */ |
| | | InteractionResult execute(InteractionContext context); |
| | | |
| | | /** |
| | | * éªè¯åç½®æ¡ä»¶ |
| | | */ |
| | | boolean validatePreCondition(InteractionContext context); |
| | | |
| | | /** |
| | | * è·å设å¤ç±»å |
| | | */ |
| | | String getDeviceType(); |
| | | |
| | | /** |
| | | * è·åé»è®¤é
ç½® |
| | | */ |
| | | DeviceInteractionConfig getDefaultConfig(); |
| | | } |
| | | ``` |
| | | |
| | | #### ä¸å¤§è½¦äº¤äºå®ç° |
| | | ```java |
| | | @Component("ä¸å¤§è½¦Interaction") |
| | | public class ä¸å¤§è½¦Interaction implements DeviceInteraction { |
| | | |
| | | @Override |
| | | public InteractionResult execute(InteractionContext context) { |
| | | ä¸å¤§è½¦Config config = context.getConfig(ä¸å¤§è½¦Config.class); |
| | | |
| | | try { |
| | | // 1. éªè¯åç½®æ¡ä»¶ |
| | | if (!validatePreCondition(context)) { |
| | | return InteractionResult.fail("åç½®æ¡ä»¶éªè¯å¤±è´¥"); |
| | | } |
| | | |
| | | // 2. è·å车è¾è§æ ¼ï¼å¯é
ç½®ï¼ |
| | | VehicleSpec vehicle = context.getVehicleSpec(); |
| | | double vehicleCapacity = config.getVehicleCapacity(); // é»è®¤6000mm |
| | | |
| | | // 3. æ£æ¥è½¦è¾å®¹é |
| | | if (vehicle.getMaxCapacity() > vehicleCapacity) { |
| | | return InteractionResult.fail("车è¾å®¹éè¶
åºéå¶: " + vehicle.getMaxCapacity()); |
| | | } |
| | | |
| | | // 4. è·åå½åç»çä¿¡æ¯ |
| | | GlassSpec currentGlass = context.getCurrentGlass(); |
| | | |
| | | // 5. 计ç®è£
è½½ç©ºé´ |
| | | double usedCapacity = calculateUsedCapacity(context); |
| | | double remainingCapacity = vehicleCapacity - usedCapacity; |
| | | |
| | | if (remainingCapacity >= currentGlass.getLength()) { |
| | | // 6. åé
车è¾ç©ºé´ |
| | | allocateVehicleSpace(context, currentGlass); |
| | | |
| | | // 7. èææ§å¶ï¼å¯é
ç½®é´éæ¶é´ï¼ |
| | | sleep(config.getGlassIntervalMs()); // é»è®¤1000ms |
| | | |
| | | // 8. 触åPLCåå
¥ |
| | | triggerPlcWrite(context, "车è¾è£
è½½", currentGlass); |
| | | |
| | | return InteractionResult.success("ä¸å¤§è½¦æå", |
| | | Map.of("remainingCapacity", remainingCapacity, |
| | | "allocatedGlass", currentGlass)); |
| | | } |
| | | |
| | | return InteractionResult.wait("çå¾
ä¸ä¸è¾è½¦ä¸å¤§è½¦", |
| | | Map.of("remainingCapacity", remainingCapacity)); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ä¸å¤§è½¦äº¤äºæ§è¡å¤±è´¥", e); |
| | | return InteractionResult.fail("æ§è¡å¼å¸¸: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public boolean validatePreCondition(InteractionContext context) { |
| | | // éªè¯è½¦è¾ä¿¡æ¯ãç»çä¿¡æ¯ãPLCè¿æ¥ç |
| | | return context.getVehicleSpec() != null && |
| | | context.getCurrentGlass() != null && |
| | | validatePlcConnection(context.getDeviceId()); |
| | | } |
| | | |
| | | @Override |
| | | public String getDeviceType() { |
| | | return "ä¸å¤§è½¦"; |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | #### 大çç交äºå®ç° |
| | | ```java |
| | | @Component("大ççInteraction") |
| | | public class 大ççInteraction implements DeviceInteraction { |
| | | |
| | | @Override |
| | | public InteractionResult execute(InteractionContext context) { |
| | | 大ççConfig config = context.getConfig(大ççConfig.class); |
| | | |
| | | try { |
| | | // 1. è·åä¸å¤§è½¦é¶æ®µä¼ éçæ°æ® |
| | | List<GlassSpec> glassesFromVehicle = context.getSharedData("glassesFromVehicle", List.class); |
| | | VehicleSpec vehicleInfo = context.getSharedData("vehicleInfo", VehicleSpec.class); |
| | | |
| | | if (glassesFromVehicle == null || glassesFromVehicle.isEmpty()) { |
| | | return InteractionResult.wait("çå¾
ä¸å¤§è½¦æ°æ®"); |
| | | } |
| | | |
| | | // 2. ä¸MES大ççä¿¡æ¯æ¯å¯¹éªè¯ |
| | | List<GlassSpec> mesGlasses = fetchMesGlassesInfo(vehicleInfo.getVehicleId()); |
| | | |
| | | for (GlassSpec vehicleGlass : glassesFromVehicle) { |
| | | boolean matched = false; |
| | | for (GlassSpec mesGlass : mesGlasses) { |
| | | if (isGlassMatched(vehicleGlass, mesGlass, config)) { |
| | | matched = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (!matched && config.isGlassMatchingEnabled()) { |
| | | return InteractionResult.fail("ç»çä¿¡æ¯ä¸å¹é
: " + vehicleGlass.getGlassId()); |
| | | } |
| | | } |
| | | |
| | | // 3. æ¹éå¤çï¼å¯é
ç½®ï¼ |
| | | if (config.isBatchProcessing()) { |
| | | return processBatch(glassesFromVehicle, context, config); |
| | | } else { |
| | | return processIndividual(glassesFromVehicle, context, config); |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("大ççäº¤äºæ§è¡å¤±è´¥", e); |
| | | return InteractionResult.fail("æ§è¡å¼å¸¸: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private InteractionResult processBatch(List<GlassSpec> glasses, InteractionContext context, 大ççConfig config) { |
| | | // æ¹éå¤çé»è¾ |
| | | for (GlassSpec glass : glasses) { |
| | | // æ¯çç»çå¤çé´é |
| | | sleep(config.getProcessingInterval()); // é»è®¤2000ms |
| | | triggerPlcWrite(context, "大ççå¤ç", glass); |
| | | } |
| | | |
| | | // ä¼ éå¤çç»æå°ä¸ä¸ä¸ªè®¾å¤ |
| | | context.setSharedData("processedGlasses", glasses); |
| | | |
| | | return InteractionResult.success("大ççæ¹éå¤ç宿", |
| | | Map.of("processedCount", glasses.size())); |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.4 å¤è®¾å¤ä»»å¡æ§è¡å¼æ |
| | | |
| | | #### 任塿§è¡å¼æ |
| | | ```java |
| | | @Service |
| | | public class TaskExecutionEngine { |
| | | |
| | | @Resource |
| | | private DeviceGroupService deviceGroupService; |
| | | |
| | | /** |
| | | * æ§è¡å¤è®¾å¤èåæµè¯ |
| | | */ |
| | | @Transactional |
| | | public MultiDeviceTaskResult executeMultiDeviceTask(String groupId, TaskParameters parameters) { |
| | | DeviceGroup group = deviceGroupService.getDeviceGroupById(groupId); |
| | | if (group == null) { |
| | | throw new RuntimeException("设å¤ç»ä¸åå¨: " + groupId); |
| | | } |
| | | |
| | | // 1. å建任å¡è®°å½ |
| | | MultiDeviceTask task = createTaskRecord(group, parameters); |
| | | |
| | | try { |
| | | task.setStatus("RUNNING"); |
| | | task.setStartTime(new Date()); |
| | | |
| | | // 2. æå»ºäº¤äºä¸ä¸æ |
| | | InteractionContext context = buildInteractionContext(group, parameters); |
| | | |
| | | // 3. ææ§è¡æ¨¡å¼æ§è¡ |
| | | MultiDeviceTaskResult result; |
| | | if (group.getExecutionMode() == ExecutionMode.SERIAL) { |
| | | result = executeSerialDevices(group, context, task); |
| | | } else { |
| | | result = executeParallelDevices(group, context, task); |
| | | } |
| | | |
| | | // 4. æ´æ°ä»»å¡ç»æ |
| | | task.setStatus(result.isSuccess() ? "COMPLETED" : "FAILED"); |
| | | task.setEndTime(new Date()); |
| | | task.setResultData(JsonUtils.toJson(result)); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("å¤è®¾å¤ä»»å¡æ§è¡å¤±è´¥: {}", groupId, e); |
| | | |
| | | task.setStatus("FAILED"); |
| | | task.setEndTime(new Date()); |
| | | task.setErrorMessage(e.getMessage()); |
| | | |
| | | return MultiDeviceTaskResult.fail("任塿§è¡å¤±è´¥: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 串è¡è®¾å¤æ§è¡ï¼ä¸å¤§è½¦ â 大çç â ç»çåå¨ |
| | | */ |
| | | private MultiDeviceTaskResult executeSerialDevices(DeviceGroup group, InteractionContext context, MultiDeviceTask task) { |
| | | MultiDeviceTaskResult finalResult = new MultiDeviceTaskResult(); |
| | | List<DeviceConfig> executionSequence = group.getExecutionSequence(); |
| | | |
| | | for (int i = 0; i < executionSequence.size(); i++) { |
| | | DeviceConfig device = executionSequence.get(i); |
| | | TaskStep step = createTaskStep(task.getTaskId(), i + 1, device); |
| | | |
| | | try { |
| | | step.setStatus("RUNNING"); |
| | | step.setStartTime(new Date()); |
| | | |
| | | // 设置å½å设å¤ä¸ä¸æ |
| | | context.setCurrentDevice(device); |
| | | context.setDeviceId(device.getDeviceId()); |
| | | |
| | | // æ§è¡è®¾å¤äº¤äºé»è¾ |
| | | DeviceInteraction interaction = getInteraction(device.getDeviceType()); |
| | | InteractionResult stepResult = interaction.execute(context); |
| | | |
| | | step.setEndTime(new Date()); |
| | | step.setDurationMs(System.currentTimeMillis() - step.getStartTime().getTime()); |
| | | |
| | | if (stepResult.isSuccess()) { |
| | | step.setStatus("COMPLETED"); |
| | | step.setOutputData(JsonUtils.toJson(stepResult.getData())); |
| | | |
| | | // ä¼ éæ°æ®å°ä¸ä¸ä¸ªè®¾å¤ |
| | | passDataToNextDevice(context, device, stepResult); |
| | | |
| | | finalResult.addStepResult(device.getDeviceName(), stepResult); |
| | | |
| | | } else { |
| | | step.setStatus("FAILED"); |
| | | step.setErrorMessage(stepResult.getMessage()); |
| | | |
| | | finalResult.addStepResult(device.getDeviceName(), stepResult); |
| | | finalResult.fail("è®¾å¤ " + device.getDeviceName() + " æ§è¡å¤±è´¥: " + stepResult.getMessage()); |
| | | break; |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | step.setStatus("FAILED"); |
| | | step.setErrorMessage(e.getMessage()); |
| | | step.setEndTime(new Date()); |
| | | |
| | | log.error("è®¾å¤æ§è¡å¼å¸¸: {}", device.getDeviceName(), e); |
| | | finalResult.fail("è®¾å¤ " + device.getDeviceName() + " æ§è¡å¼å¸¸: " + e.getMessage()); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return finalResult; |
| | | } |
| | | |
| | | /** |
| | | * æ°æ®ä¼ éå°ä¸ä¸ä¸ªè®¾å¤ |
| | | */ |
| | | private void passDataToNextDevice(InteractionContext context, DeviceConfig currentDevice, InteractionResult result) { |
| | | String deviceType = currentDevice.getDeviceType(); |
| | | |
| | | if ("ä¸å¤§è½¦".equals(deviceType)) { |
| | | // ä¸å¤§è½¦ â 大ççï¼ä¼ éç»çå表å车è¾ä¿¡æ¯ |
| | | context.setSharedData("glassesFromVehicle", result.getData("glasses")); |
| | | context.setSharedData("vehicleInfo", result.getData("vehicle")); |
| | | |
| | | } else if ("大çç".equals(deviceType)) { |
| | | // 大çç â ç»çåå¨ï¼ä¼ éå¤ç宿çç»ç |
| | | context.setSharedData("processedGlasses", result.getData("processedGlasses")); |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## ð¨ å端çé¢è®¾è®¡ |
| | | |
| | | ### 4.1 设å¤ç®¡ççé¢ |
| | | |
| | | #### 设å¤é
ç½®é¡µé¢ |
| | | ```vue |
| | | <template> |
| | | <div class="device-management"> |
| | | <!-- 设å¤å表 --> |
| | | <div class="device-list"> |
| | | <el-table :data="devices" style="width: 100%"> |
| | | <el-table-column prop="deviceName" label="设å¤åç§°" /> |
| | | <el-table-column prop="deviceType" label="设å¤ç±»å" /> |
| | | <el-table-column prop="plcIp" label="PLC IP" /> |
| | | <el-table-column prop="status" label="ç¶æ"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ row.status }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½"> |
| | | <template #default="{ row }"> |
| | | <el-button @click="editDevice(row)">ç¼è¾</el-button> |
| | | <el-button @click="testConnection(row)">æµè¯è¿æ¥</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <!-- 设å¤é
置表å --> |
| | | <el-dialog v-model="showConfigDialog" title="设å¤é
ç½®"> |
| | | <el-form :model="deviceForm" label-width="120px"> |
| | | <el-form-item label="设å¤ID"> |
| | | <el-input v-model="deviceForm.deviceId" /> |
| | | </el-form-item> |
| | | <el-form-item label="设å¤åç§°"> |
| | | <el-input v-model="deviceForm.deviceName" /> |
| | | </el-form-item> |
| | | <el-form-item label="设å¤ç±»å"> |
| | | <el-select v-model="deviceForm.deviceType"> |
| | | <el-option label="ä¸å¤§è½¦è®¾å¤" value="ä¸å¤§è½¦" /> |
| | | <el-option label="大çç设å¤" value="大çç" /> |
| | | <el-option label="ç»çåå¨è®¾å¤" value="ç»çåå¨" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="PLCé
ç½®"> |
| | | <div class="plc-config"> |
| | | <el-input v-model="deviceForm.plcIp" placeholder="PLC IPå°å" /> |
| | | <el-select v-model="deviceForm.plcType" placeholder="PLCç±»å"> |
| | | <el-option label="S7-1200" value="S7-1200" /> |
| | | <el-option label="S7-1500" value="S7-1500" /> |
| | | </el-select> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 设å¤ç¹å®é
ç½® --> |
| | | <div v-if="deviceForm.deviceType === 'ä¸å¤§è½¦'"> |
| | | <el-form-item label="车è¾å®¹é(mm)"> |
| | | <el-input-number v-model="deviceForm.config.vehicleCapacity" :min="1000" :max="12000" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç»çé´é(ms)"> |
| | | <el-input-number v-model="deviceForm.config.glassIntervalMs" :min="100" :max="10000" /> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <div v-if="deviceForm.deviceType === '大çç'"> |
| | | <el-form-item label="å¯ç¨ç»çæ¯å¯¹"> |
| | | <el-switch v-model="deviceForm.config.glassMatchingEnabled" /> |
| | | </el-form-item> |
| | | <el-form-item label="æ¹éå¤ç"> |
| | | <el-switch v-model="deviceForm.config.batchProcessing" /> |
| | | </el-form-item> |
| | | </div> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <el-button @click="showConfigDialog = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="saveDevice">ä¿å</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | devices: [], |
| | | showConfigDialog: false, |
| | | deviceForm: { |
| | | deviceId: '', |
| | | deviceName: '', |
| | | deviceType: '', |
| | | plcIp: '', |
| | | plcType: '', |
| | | config: { |
| | | vehicleCapacity: 6000, // é»è®¤6ç±³ |
| | | glassIntervalMs: 1000, // é»è®¤1ç§ |
| | | glassMatchingEnabled: true, |
| | | batchProcessing: true |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | async loadDevices() { |
| | | // å 载设å¤å表 |
| | | const response = await this.$api.device.getDeviceList(); |
| | | this.devices = response.data; |
| | | }, |
| | | |
| | | async saveDevice() { |
| | | try { |
| | | if (this.deviceForm.deviceId) { |
| | | await this.$api.device.updateDevice(this.deviceForm); |
| | | } else { |
| | | await this.$api.device.createDevice(this.deviceForm); |
| | | } |
| | | this.$message.success('ä¿åæå'); |
| | | this.showConfigDialog = false; |
| | | this.loadDevices(); |
| | | } catch (error) { |
| | | this.$message.error('ä¿å失败: ' + error.message); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | ### 4.2 设å¤ç»é
ç½®çé¢ |
| | | |
| | | ```vue |
| | | <template> |
| | | <div class="device-group-management"> |
| | | <!-- 设å¤ç»å表 --> |
| | | <div class="group-list"> |
| | | <div class="header"> |
| | | <h3>设å¤ç»é
ç½®</h3> |
| | | <el-button type="primary" @click="createGroup">æ°å»ºè®¾å¤ç»</el-button> |
| | | </div> |
| | | |
| | | <div class="groups"> |
| | | <el-card v-for="group in deviceGroups" :key="group.groupId" class="group-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>{{ group.groupName }}</span> |
| | | <el-tag :type="group.executionMode === 'SERIAL' ? 'primary' : 'success'"> |
| | | {{ group.executionMode === 'SERIAL' ? 'ä¸²è¡æ§è¡' : 'å¹¶è¡æ§è¡' }} |
| | | </el-tag> |
| | | </div> |
| | | </template> |
| | | |
| | | <div class="group-content"> |
| | | <!-- è®¾å¤ææå¾ --> |
| | | <div class="topology"> |
| | | <div v-for="(device, index) in group.devices" :key="device.deviceId" class="device-node"> |
| | | <div class="device-box" :class="device.deviceType"> |
| | | <div class="device-name">{{ device.deviceName }}</div> |
| | | <div class="device-type">{{ device.deviceType }}</div> |
| | | </div> |
| | | <div v-if="index < group.devices.length - 1" class="arrow">â</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ä¾èµå
³ç³» --> |
| | | <div class="dependencies" v-if="group.dependencies"> |
| | | <h4>设å¤ä¾èµå
³ç³»ï¼</h4> |
| | | <div v-for="(deps, device) in group.dependencies" :key="device" class="dependency-item"> |
| | | {{ device }} ä¾èµäº: {{ deps.join(', ') }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="card-footer"> |
| | | <el-button @click="editGroup(group)">ç¼è¾</el-button> |
| | | <el-button @click="startTest(group)">å¼å§æµè¯</el-button> |
| | | <el-button @click="deleteGroup(group)">å é¤</el-button> |
| | | </div> |
| | | </template> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 设å¤ç»é
ç½®å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="showGroupDialog" title="设å¤ç»é
ç½®" width="800px"> |
| | | <el-form :model="groupForm" label-width="120px"> |
| | | <el-form-item label="设å¤ç»åç§°"> |
| | | <el-input v-model="groupForm.groupName" /> |
| | | </el-form-item> |
| | | <el-form-item label="å
³è项ç®"> |
| | | <el-select v-model="groupForm.projectId"> |
| | | <el-option v-for="project in projects" :key="project.id" |
| | | :label="project.projectName" :value="project.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ§è¡æ¨¡å¼"> |
| | | <el-radio-group v-model="groupForm.executionMode"> |
| | | <el-radio label="SERIAL">ä¸²è¡æ§è¡</el-radio> |
| | | <el-radio label="PARALLEL">å¹¶è¡æ§è¡</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | |
| | | <!-- 设å¤éæ© --> |
| | | <el-form-item label="å
å«è®¾å¤"> |
| | | <el-transfer |
| | | v-model="selectedDevices" |
| | | :data="availableDevices" |
| | | :titles="['å¯ç¨è®¾å¤', 'å·²é设å¤']" |
| | | @change="updateDeviceOrder" /> |
| | | </el-form-item> |
| | | |
| | | <!-- æ§è¡é¡ºåºè°æ´ --> |
| | | <el-form-item label="æ§è¡é¡ºåº" v-if="groupForm.executionMode === 'SERIAL'"> |
| | | <div class="execution-order"> |
| | | <div v-for="(deviceId, index) in deviceOrder" :key="deviceId" class="order-item"> |
| | | <span>{{ index + 1 }}. {{ getDeviceName(deviceId) }}</span> |
| | | <el-button-group> |
| | | <el-button @click="moveUp(index)" :disabled="index === 0">â</el-button> |
| | | <el-button @click="moveDown(index)" :disabled="index === deviceOrder.length - 1">â</el-button> |
| | | </el-button-group> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | deviceGroups: [], |
| | | availableDevices: [], |
| | | selectedDevices: [], |
| | | deviceOrder: [], |
| | | showGroupDialog: false, |
| | | groupForm: { |
| | | groupId: '', |
| | | groupName: '', |
| | | projectId: '', |
| | | executionMode: 'SERIAL' |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | async loadDeviceGroups() { |
| | | const response = await this.$api.deviceGroup.getGroupList(); |
| | | this.deviceGroups = response.data; |
| | | }, |
| | | |
| | | async startTest(group) { |
| | | try { |
| | | // è·³è½¬å°æµè¯æ§è¡é¡µé¢ |
| | | this.$router.push({ |
| | | name: 'MultiDeviceTest', |
| | | query: { groupId: group.groupId } |
| | | }); |
| | | } catch (error) { |
| | | this.$message.error('å¯å¨æµè¯å¤±è´¥: ' + error.message); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | ### 4.3 å¤è®¾å¤æµè¯æ§è¡çé¢ |
| | | |
| | | ```vue |
| | | <template> |
| | | <div class="multi-device-test"> |
| | | <!-- æµè¯é
ç½® --> |
| | | <div class="test-config"> |
| | | <el-card> |
| | | <template #header> |
| | | <h3>æµè¯é
ç½® - {{ groupInfo.groupName }}</h3> |
| | | </template> |
| | | |
| | | <div class="config-content"> |
| | | <div class="execution-mode"> |
| | | <el-tag :type="groupInfo.executionMode === 'SERIAL' ? 'primary' : 'success'"> |
| | | {{ groupInfo.executionMode === 'SERIAL' ? 'ä¸²è¡æ§è¡' : 'å¹¶è¡æ§è¡' }} |
| | | </el-tag> |
| | | </div> |
| | | |
| | | <div class="parameters"> |
| | | <h4>æµè¯åæ°ï¼</h4> |
| | | <el-form label-width="150px"> |
| | | <el-form-item label="车è¾è§æ ¼"> |
| | | <el-input-number v-model="testParams.vehicleLength" :min="1000" :max="12000" /> mm |
| | | </el-form-item> |
| | | <el-form-item label="æµè¯ç»çæ°é"> |
| | | <el-input-number v-model="testParams.glassCount" :min="1" :max="50" /> |
| | | </el-form-item> |
| | | <el-form-item label="æ§è¡é´é"> |
| | | <el-input-number v-model="testParams.executionInterval" :min="500" :max="10000" /> ms |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <div class="test-controls"> |
| | | <el-button type="primary" size="large" @click="startTest" :loading="isRunning"> |
| | | {{ isRunning ? 'æµè¯æ§è¡ä¸...' : 'å¼å§æµè¯' }} |
| | | </el-button> |
| | | <el-button @click="stopTest" :disabled="!isRunning">忢æµè¯</el-button> |
| | | <el-button @click="pauseTest" :disabled="!isRunning">æåæµè¯</el-button> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- æ§è¡çæ§ --> |
| | | <div class="execution-monitor" v-if="isRunning || currentTask"> |
| | | <el-card> |
| | | <template #header> |
| | | <h3>æ§è¡çæ§</h3> |
| | | </template> |
| | | |
| | | <div class="monitor-content"> |
| | | <!-- ä»»å¡è¿åº¦ --> |
| | | <div class="task-progress"> |
| | | <el-progress |
| | | :percentage="getOverallProgress()" |
| | | :status="getTaskStatus()" /> |
| | | <div class="progress-info"> |
| | | æ¥éª¤ {{ currentStep }} / {{ totalSteps }} - {{ getCurrentStepName() }} |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 设å¤ç¶æ --> |
| | | <div class="device-status-grid"> |
| | | <div v-for="device in groupInfo.devices" :key="device.deviceId" |
| | | class="device-status-card"> |
| | | <div class="device-header"> |
| | | <span class="device-name">{{ device.deviceName }}</span> |
| | | <el-tag :type="getDeviceStatusType(device.deviceId)"> |
| | | {{ getDeviceStatus(device.deviceId) }} |
| | | </el-tag> |
| | | </div> |
| | | |
| | | <div class="device-details" v-if="getDeviceDetails(device.deviceId)"> |
| | | <div class="detail-item"> |
| | | <span>å½åç»ç:</span> {{ getDeviceDetails(device.deviceId).currentGlass || '-' }} |
| | | </div> |
| | | <div class="detail-item"> |
| | | <span>å¤çæ°é:</span> {{ getDeviceDetails(device.deviceId).processedCount || 0 }} |
| | | </div> |
| | | <div class="detail-item"> |
| | | <span>å©ä½å®¹é:</span> {{ getDeviceDetails(device.deviceId).remainingCapacity || '-' }} mm |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 宿¶æ¥å¿ --> |
| | | <div class="device-logs"> |
| | | <div v-for="log in getDeviceLogs(device.deviceId)" :key="log.timestamp" |
| | | class="log-item" :class="log.level"> |
| | | <span class="log-time">{{ formatTime(log.timestamp) }}</span> |
| | | <span class="log-message">{{ log.message }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æ°æ®æµå¯è§å --> |
| | | <div class="data-flow"> |
| | | <h4>æ°æ®æµç¶æï¼</h4> |
| | | <div class="flow-diagram"> |
| | | <div v-for="(device, index) in groupInfo.devices" :key="device.deviceId" class="flow-node"> |
| | | <div class="node" :class="['device-' + device.deviceType.toLowerCase(), getNodeStatus(device.deviceId)]"> |
| | | {{ device.deviceName }} |
| | | </div> |
| | | <div class="data-indicator" v-if="index < groupInfo.devices.length - 1"> |
| | | <div class="data-flow-arrow" :class="getFlowStatus(device.deviceId, index + 1)"> |
| | | {{ getFlowData(device.deviceId) }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- ç»æåæ --> |
| | | <div class="test-results" v-if="testResults"> |
| | | <el-card> |
| | | <template #header> |
| | | <h3>æµè¯ç»æ</h3> |
| | | </template> |
| | | |
| | | <div class="results-content"> |
| | | <!-- æ»ä½ç»æ --> |
| | | <div class="overall-result"> |
| | | <el-result |
| | | :icon="testResults.success ? 'success' : 'error'" |
| | | :title="testResults.success ? 'æµè¯æå' : 'æµè¯å¤±è´¥'" |
| | | :sub-title="testResults.message"> |
| | | <template #extra> |
| | | <div class="result-stats"> |
| | | <div class="stat-item"> |
| | | <span class="label">æ§è¡æ¶é´:</span> |
| | | <span class="value">{{ formatDuration(testResults.duration) }}</span> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <span class="label">å¤çç»ç:</span> |
| | | <span class="value">{{ testResults.processedGlassCount }} ç</span> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <span class="label">æåç:</span> |
| | | <span class="value">{{ testResults.successRate }}%</span> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-result> |
| | | </div> |
| | | |
| | | <!-- 详ç»ç»æ --> |
| | | <div class="detailed-results"> |
| | | <h4>åè®¾å¤æ§è¡è¯¦æ
ï¼</h4> |
| | | <el-collapse> |
| | | <el-collapse-item v-for="(result, deviceName) in testResults.deviceResults" |
| | | :key="deviceName" :title="deviceName"> |
| | | <div class="device-result-detail"> |
| | | <div class="result-summary"> |
| | | <el-tag :type="result.success ? 'success' : 'error'"> |
| | | {{ result.success ? 'æ§è¡æå' : 'æ§è¡å¤±è´¥' }} |
| | | </el-tag> |
| | | <span class="duration">èæ¶: {{ formatDuration(result.duration) }}</span> |
| | | </div> |
| | | |
| | | <div class="result-data"> |
| | | <h5>è¾åºæ°æ®ï¼</h5> |
| | | <pre>{{ JSON.stringify(result.outputData, null, 2) }}</pre> |
| | | </div> |
| | | |
| | | <div class="result-logs"> |
| | | <h5>æ§è¡æ¥å¿ï¼</h5> |
| | | <div class="log-list"> |
| | | <div v-for="log in result.logs" :key="log.timestamp" class="log-line"> |
| | | <span class="log-time">{{ formatTime(log.timestamp) }}</span> |
| | | <span class="log-level">{{ log.level }}</span> |
| | | <span class="log-message">{{ log.message }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-collapse-item> |
| | | </el-collapse> |
| | | </div> |
| | | |
| | | <!-- 导åºç»æ --> |
| | | <div class="export-results"> |
| | | <el-button @click="exportResults('json')">导åºJSON</el-button> |
| | | <el-button @click="exportResults('excel')">导åºExcel</el-button> |
| | | <el-button @click="exportResults('pdf')">导åºPDFæ¥å</el-button> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | ``` |
| | | |
| | | ## ð 宿½è®¡å |
| | | |
| | | ### 第ä¸é¶æ®µï¼åºç¡æ¶ææå»ºï¼1-2å¨ï¼ |
| | | |
| | | 1. **æ°æ®åºè¡¨å建** |
| | | - å建设å¤ç®¡çç¸å
³è¡¨ |
| | | - 建ç«è¡¨å
³ç³»åç´¢å¼ |
| | | - è¿ç§»ç°ææ°æ®ï¼å¦éè¦ï¼ |
| | | |
| | | 2. **å端åºç¡ç»ä»¶** |
| | | - 设å¤é
ç½®å®ä½å管çæå¡ |
| | | - 设å¤ç»ç®¡çç»ä»¶ |
| | | - åºç¡äº¤äºæ¥å£å®ä¹ |
| | | |
| | | 3. **å端åºç¡çé¢** |
| | | - 设å¤ç®¡çé¡µé¢ |
| | | - 设å¤ç»é
ç½®é¡µé¢ |
| | | - åºç¡ç»ä»¶å¼å |
| | | |
| | | ### 第äºé¶æ®µï¼æ ¸å¿äº¤äºé»è¾ï¼2-3å¨ï¼ |
| | | |
| | | 1. **设å¤äº¤äºå®ç°** |
| | | - ä¸å¤§è½¦äº¤äºé»è¾ |
| | | - 大çç交äºé»è¾ |
| | | - ç»çåå¨äº¤äºé»è¾ |
| | | |
| | | 2. **任塿§è¡å¼æ** |
| | | - ä¸²è¡æ§è¡å¼æ |
| | | - å¤è®¾å¤åè°æºå¶ |
| | | - é误å¤çåéè¯é»è¾ |
| | | |
| | | 3. **PLCå°åæ å°æ©å±** |
| | | - å¤è®¾å¤å°åæ å°æ¯æ |
| | | - å°åé
置管ç |
| | | - PLCéä¿¡éé
|
| | | |
| | | ### 第ä¸é¶æ®µï¼å端å®åï¼1-2å¨ï¼ |
| | | |
| | | 1. **æµè¯æ§è¡çé¢** |
| | | - å¤è®¾å¤æµè¯ç¼æ |
| | | - 宿¶çæ§é¢æ¿ |
| | | - ç»æåæå±ç¤º |
| | | |
| | | 2. **ç¨æ·äº¤äºä¼å** |
| | | - é
ç½®å导 |
| | | - å¯è§åè®¾å¤ææ |
| | | - 宿¶æ°æ®æµå±ç¤º |
| | | |
| | | ### 第åé¶æ®µï¼éææµè¯ï¼1å¨ï¼ |
| | | |
| | | 1. **åè½æµè¯** |
| | | - å¤è®¾å¤èåæµè¯æµç¨ |
| | | - å¼å¸¸æ
åµå¤ç |
| | | - æ§è½æµè¯ |
| | | |
| | | 2. **ç³»ç»éæ** |
| | | - ä¸ç°æç³»ç»éæ |
| | | - æ°æ®è¿ç§»éªè¯ |
| | | - ç¨æ·éªæ¶æµè¯ |
| | | |
| | | ## ð ææ¯è¦ç¹æ»ç» |
| | | |
| | | ### æ ¸å¿ä¼å¿ |
| | | 1. **模åå设计**ï¼æ¯ä¸ªè®¾å¤ç±»åç¬ç«å®ç°ï¼ä¾¿äºæ©å± |
| | | 2. **é
置驱å¨**ï¼ææåæ°å¯é
ç½®ï¼æ¯æä¸åä¸å¡åºæ¯ |
| | | 3. **æ°æ®æµç®¡ç**ï¼è®¾å¤é´æ°æ®ä¼ éåç¶æåæ¥ |
| | | 4. **å¯è§åçæ§**ï¼å®æ¶æ¾ç¤ºè®¾å¤ç¶æåæ°æ®æµ |
| | | 5. **çµæ´»æ©å±**ï¼æ¯ææ°å¢è®¾å¤ç±»åå交äºé»è¾ |
| | | |
| | | ### ææ¯é¾ç¹ |
| | | 1. **设å¤åè°**ï¼ç¡®ä¿å¤è®¾å¤æ§è¡çæ£ç¡®é¡ºåº |
| | | 2. **æ°æ®åæ¥**ï¼è®¾å¤é´æ°æ®çåç¡®ä¼ é |
| | | 3. **å¼å¸¸å¤ç**ï¼å个设å¤å¤±è´¥å¯¹æ´ä¸ªæµç¨çå½±å |
| | | 4. **æ§è½ä¼å**ï¼å¤§æ¹éæ°æ®çå¤çæ§è½ |
| | | |
| | | ### é£é©æ§å¶ |
| | | 1. **ååå
¼å®¹**ï¼ä¸ç ´åç°æå设å¤åè½ |
| | | 2. **æ¸è¿å¼å级**ï¼åé¶æ®µå®æ½ï¼éä½é£é© |
| | | 3. **å
åæµè¯**ï¼æ¯ä¸ªé¶æ®µçå
¨é¢æµè¯éªè¯ |
| | | 4. **åæ»æºå¶**ï¼åºç°é®é¢æ¶çå¿«éåæ»æ¹æ¡ |
| | | |
| | | --- |
| | | |
| | | ## ð ç»è®º |
| | | |
| | | éè¿è¿ä¸ªæ©å±æ¹æ¡ï¼MES Test Projectå°å
·å¤å®æ´çå¤è®¾å¤èåæµè¯è½åï¼æ¯æå¤æçç产æµç¨èªå¨åæµè¯ãæ¹æ¡åºäºç°ææ¶æè®¾è®¡ï¼é£é©å¯æ§ï¼å®æ½é¾åº¦éä¸ï¼è½å¤å¾å¥½å°æ»¡è¶³ä¸å¡éæ±ã |
| | | |
| | | 建议ä¼å
å®ç°ç¬¬ä¸é¶æ®µçåºç¡æ¶æï¼ç¶å鿥å®å交äºé»è¾åå端çé¢ï¼ç¡®ä¿æ¯ä¸ªé¶æ®µé½è½äº¤ä»å¯ç¨çåè½ã |
| New file |
| | |
| | | package com.mes.interaction; |
| | | |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.service.DevicePlcOperationService; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 设å¤é»è¾å¤çå¨åºç±» |
| | | * æä¾éç¨çåè½å®ç° |
| | | * |
| | | * @author mes |
| | | * @since 2025-01-XX |
| | | */ |
| | | @Slf4j |
| | | @RequiredArgsConstructor |
| | | public abstract class BaseDeviceLogicHandler implements DeviceLogicHandler { |
| | | |
| | | protected final DevicePlcOperationService devicePlcOperationService; |
| | | protected final ObjectMapper objectMapper = new ObjectMapper(); |
| | | |
| | | @Override |
| | | public DevicePlcVO.OperationResult execute(DeviceConfig deviceConfig, String operation, Map<String, Object> params) { |
| | | try { |
| | | // éªè¯è®¾å¤é
ç½® |
| | | String validationError = validateLogicParams(deviceConfig); |
| | | if (validationError != null) { |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("设å¤é»è¾åæ°éªè¯å¤±è´¥: " + validationError) |
| | | .build(); |
| | | } |
| | | |
| | | // è§£æè®¾å¤é»è¾åæ°ï¼ä» extraParams.deviceLogic ä¸è¯»åï¼ |
| | | Map<String, Object> logicParams = parseLogicParams(deviceConfig); |
| | | |
| | | // æ§è¡å
·ä½æä½ï¼ç±åç±»å®ç°ï¼ |
| | | return doExecute(deviceConfig, operation, params, logicParams); |
| | | } catch (Exception e) { |
| | | log.error("æ§è¡è®¾å¤é»è¾æä½å¤±è´¥, deviceId={}, operation={}", deviceConfig.getId(), operation, e); |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("æ§è¡å¤±è´¥: " + e.getMessage()) |
| | | .build(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * åç±»å®ç°å
·ä½çæä½é»è¾ |
| | | * |
| | | * @param deviceConfig 设å¤é
ç½® |
| | | * @param operation æä½ç±»å |
| | | * @param params è¿è¡æ¶åæ°ï¼å¨æä¼ å
¥ï¼ |
| | | * @param logicParams é»è¾é
ç½®åæ°ï¼ä» extraParams.deviceLogic è§£æï¼ |
| | | * @return æä½ç»æ |
| | | */ |
| | | protected abstract DevicePlcVO.OperationResult doExecute( |
| | | DeviceConfig deviceConfig, |
| | | String operation, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams |
| | | ); |
| | | |
| | | /** |
| | | * è§£æè®¾å¤é»è¾åæ°ï¼ä» extraParams 䏿å deviceLogicï¼ |
| | | */ |
| | | protected Map<String, Object> parseLogicParams(DeviceConfig deviceConfig) { |
| | | String extraParams = deviceConfig.getExtraParams(); |
| | | if (extraParams == null || extraParams.trim().isEmpty()) { |
| | | return new HashMap<>(); |
| | | } |
| | | |
| | | try { |
| | | Map<String, Object> extra = objectMapper.readValue(extraParams, new TypeReference<Map<String, Object>>() {}); |
| | | @SuppressWarnings("unchecked") |
| | | Map<String, Object> deviceLogic = (Map<String, Object>) extra.get("deviceLogic"); |
| | | return deviceLogic != null ? deviceLogic : new HashMap<>(); |
| | | } catch (Exception e) { |
| | | log.warn("è§£æè®¾å¤é»è¾åæ°å¤±è´¥, deviceId={}", deviceConfig.getId(), e); |
| | | return new HashMap<>(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·åé»è¾åæ°ä¸çå¼ï¼å¸¦é»è®¤å¼ï¼ |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | protected <T> T getLogicParam(Map<String, Object> logicParams, String key, T defaultValue) { |
| | | Object value = logicParams.get(key); |
| | | if (value == null) { |
| | | return defaultValue; |
| | | } |
| | | try { |
| | | return (T) value; |
| | | } catch (ClassCastException e) { |
| | | log.warn("é»è¾åæ°ç±»å转æ¢å¤±è´¥, key={}, value={}", key, value, e); |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·åé»è¾åæ°ä¸çå¼ï¼ä¸å¸¦é»è®¤å¼ï¼ |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | protected <T> T getLogicParam(Map<String, Object> logicParams, String key) { |
| | | Object value = logicParams.get(key); |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return (T) value; |
| | | } catch (ClassCastException e) { |
| | | log.warn("é»è¾åæ°ç±»å转æ¢å¤±è´¥, key={}, value={}", key, value, e); |
| | | return null; |
| | | } |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction; |
| | | |
| | | import com.mes.interaction.base.InteractionContext; |
| | | import com.mes.interaction.base.InteractionResult; |
| | | |
| | | /** |
| | | * å¤è®¾å¤äº¤äºæ¥å£ |
| | | */ |
| | | public interface DeviceInteraction { |
| | | |
| | | /** |
| | | * 交äºå¯¹åºç设å¤ç±»å |
| | | */ |
| | | String getDeviceType(); |
| | | |
| | | /** |
| | | * æ§è¡äº¤äºé»è¾ |
| | | */ |
| | | InteractionResult execute(InteractionContext context); |
| | | |
| | | /** |
| | | * æ¯å¦æ¯ææå®æä½ |
| | | */ |
| | | default boolean supportsOperation(String operation) { |
| | | return true; |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * äº¤äºæ³¨åä¸å¿ |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class DeviceInteractionRegistry { |
| | | |
| | | private final Map<String, DeviceInteraction> interactionMap = new HashMap<>(); |
| | | |
| | | public DeviceInteractionRegistry(List<DeviceInteraction> interactions) { |
| | | if (interactions != null) { |
| | | for (DeviceInteraction interaction : interactions) { |
| | | if (interaction.getDeviceType() != null) { |
| | | interactionMap.put(interaction.getDeviceType(), interaction); |
| | | log.info("注å设å¤äº¤äº: {}", interaction.getDeviceType()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | public DeviceInteraction getInteraction(String deviceType) { |
| | | if (deviceType == null) { |
| | | return null; |
| | | } |
| | | return interactionMap.get(deviceType); |
| | | } |
| | | |
| | | public Map<String, DeviceInteraction> getInteractions() { |
| | | return Collections.unmodifiableMap(interactionMap); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 设å¤é»è¾å¤ç卿¥å£ |
| | | * ä¸å设å¤ç±»åå®ç°æ¤æ¥å£æ¥å¤çåèªçä¸å¡é»è¾ |
| | | * |
| | | * @author mes |
| | | * @since 2025-01-XX |
| | | */ |
| | | public interface DeviceLogicHandler { |
| | | |
| | | /** |
| | | * è·å设å¤ç±»åï¼ç¨äºå¹é
å¤çå¨ï¼ |
| | | * |
| | | * @return 设å¤ç±»åï¼å¦ï¼"ä¸å¤§è½¦"ã"大çç"ã"ç»çåå¨" |
| | | */ |
| | | String getDeviceType(); |
| | | |
| | | /** |
| | | * æ§è¡è®¾å¤é»è¾æä½ |
| | | * |
| | | * @param deviceConfig 设å¤é
ç½®ä¿¡æ¯ |
| | | * @param operation æä½ç±»åï¼å¦ï¼feedGlass, triggerRequest, triggerReportçï¼ |
| | | * @param params æä½åæ°ï¼è¿è¡æ¶ä¼ å
¥çå¨æåæ°ï¼ |
| | | * @return æä½ç»æ |
| | | */ |
| | | DevicePlcVO.OperationResult execute(DeviceConfig deviceConfig, String operation, Map<String, Object> params); |
| | | |
| | | /** |
| | | * éªè¯è®¾å¤é»è¾åæ°é
ç½®æ¯å¦ææ |
| | | * |
| | | * @param deviceConfig 设å¤é
ç½® |
| | | * @return éªè¯ç»æï¼null表示éªè¯éè¿ï¼å¦åè¿åéè¯¯ä¿¡æ¯ |
| | | */ |
| | | String validateLogicParams(DeviceConfig deviceConfig); |
| | | |
| | | /** |
| | | * è·å设å¤é»è¾åæ°çé»è®¤é
ç½® |
| | | * |
| | | * @return é»è®¤é
ç½®çJSONå符串 |
| | | */ |
| | | String getDefaultLogicParams(); |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.PostConstruct; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 设å¤é»è¾å¤çå¨å·¥åç±» |
| | | * æ ¹æ®è®¾å¤ç±»åè·å对åºçå¤çå¨ |
| | | * |
| | | * @author mes |
| | | * @since 2025-01-XX |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class DeviceLogicHandlerFactory { |
| | | |
| | | @Autowired |
| | | private List<DeviceLogicHandler> handlers; |
| | | |
| | | private final Map<String, DeviceLogicHandler> handlerMap = new HashMap<>(); |
| | | |
| | | /** |
| | | * åå§åå¤ç卿 å° |
| | | */ |
| | | @PostConstruct |
| | | public void init() { |
| | | if (handlers != null) { |
| | | for (DeviceLogicHandler handler : handlers) { |
| | | String deviceType = handler.getDeviceType(); |
| | | if (deviceType != null && !deviceType.isEmpty()) { |
| | | handlerMap.put(deviceType, handler); |
| | | log.info("注å设å¤é»è¾å¤çå¨: {} -> {}", deviceType, handler.getClass().getSimpleName()); |
| | | } |
| | | } |
| | | } |
| | | log.info("设å¤é»è¾å¤çå¨åå§å宿ï¼å
±æ³¨å {} 个å¤çå¨", handlerMap.size()); |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è®¾å¤ç±»åè·å对åºçå¤çå¨ |
| | | * |
| | | * @param deviceType 设å¤ç±»å |
| | | * @return 设å¤é»è¾å¤çå¨ï¼å¦ææªæ¾å°è¿ånull |
| | | */ |
| | | public DeviceLogicHandler getHandler(String deviceType) { |
| | | if (deviceType == null || deviceType.isEmpty()) { |
| | | return null; |
| | | } |
| | | return handlerMap.get(deviceType); |
| | | } |
| | | |
| | | /** |
| | | * æ£æ¥æ¯å¦æ¯ææå®ç设å¤ç±»å |
| | | * |
| | | * @param deviceType 设å¤ç±»å |
| | | * @return trueè¡¨ç¤ºæ¯æï¼falseè¡¨ç¤ºä¸æ¯æ |
| | | */ |
| | | public boolean supports(String deviceType) { |
| | | return deviceType != null && handlerMap.containsKey(deviceType); |
| | | } |
| | | |
| | | /** |
| | | * è·åææå·²æ³¨åç设å¤ç±»å |
| | | * |
| | | * @return 设å¤ç±»åéå |
| | | */ |
| | | public java.util.Set<String> getSupportedDeviceTypes() { |
| | | return handlerMap.keySet(); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | # 设å¤é»è¾å¤ç卿¶æè¯´æ |
| | | |
| | | ## æ¶ææ¦è¿° |
| | | |
| | | éç¨**çç¥æ¨¡å¼ + 工忍¡å¼**å®ç°è®¾å¤é»è¾å¤çï¼æ¯æä¸å设å¤ç±»åçå·®å¼åé»è¾å¤çã |
| | | |
| | | ## ç®å½ç»æ |
| | | |
| | | ``` |
| | | com.mes.interaction/ |
| | | âââ DeviceLogicHandler.java # å¤ç卿¥å£ |
| | | âââ BaseDeviceLogicHandler.java # åºç¡æ½è±¡ç±» |
| | | âââ DeviceLogicHandlerFactory.java # å·¥åç±» |
| | | âââ impl/ |
| | | âââ LoadVehicleLogicHandler.java # ä¸å¤§è½¦é»è¾å¤çå¨ |
| | | âââ LargeGlassLogicHandler.java # 大ççé»è¾å¤çå¨ |
| | | âââ GlassStorageLogicHandler.java # ç»çåå¨é»è¾å¤çå¨ |
| | | ``` |
| | | |
| | | ## æ ¸å¿æ¦å¿µ |
| | | |
| | | ### 1. åæ°é
ç½®ï¼extraParams.deviceLogicï¼ |
| | | |
| | | 设å¤é»è¾åæ°åå¨å¨ `DeviceConfig.extraParams` ç `deviceLogic` èç¹ä¸ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "connectionConfig": { ... }, |
| | | "plcConfig": { ... }, |
| | | "deviceLogic": { |
| | | "vehicleCapacity": 6000, |
| | | "glassIntervalMs": 1000, |
| | | "autoFeed": true, |
| | | "positionMapping": { |
| | | "POS1": 1, |
| | | "POS2": 2 |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 2. æ§è¡æµç¨ |
| | | |
| | | ``` |
| | | ç¨æ·è°ç¨æ¥å£ |
| | | â |
| | | Service å±è·å设å¤é
ç½®ï¼å
å« extraParamsï¼ |
| | | â |
| | | Factory æ ¹æ® deviceType 鿩坹åºç Handler |
| | | â |
| | | Handler ä» extraParams.deviceLogic 读åé
ç½®åæ° |
| | | â |
| | | Handler æ ¹æ®é
ç½®åæ°åè¿è¡æ¶åæ°æ§è¡é»è¾ |
| | | â |
| | | è°ç¨ DevicePlcOperationService.writeFields() åå
¥PLC |
| | | ``` |
| | | |
| | | ## ä½¿ç¨æ¹å¼ |
| | | |
| | | ### æ¹å¼1ï¼éè¿ DeviceInteractionServiceï¼æ¨èï¼ |
| | | |
| | | ```java |
| | | @Autowired |
| | | private DeviceInteractionService deviceInteractionService; |
| | | |
| | | // æ§è¡æä½ |
| | | Map<String, Object> params = new HashMap<>(); |
| | | params.put("glassIds", Arrays.asList("GLS001", "GLS002")); |
| | | params.put("positionCode", "POS1"); |
| | | |
| | | OperationResult result = deviceInteractionService.executeOperation( |
| | | deviceId, |
| | | "feedGlass", |
| | | params |
| | | ); |
| | | ``` |
| | | |
| | | ### æ¹å¼2ï¼ç´æ¥ä½¿ç¨ Handler |
| | | |
| | | ```java |
| | | @Autowired |
| | | private DeviceLogicHandlerFactory handlerFactory; |
| | | @Autowired |
| | | private DeviceConfigService deviceConfigService; |
| | | |
| | | // è·å设å¤åå¤çå¨ |
| | | DeviceConfig device = deviceConfigService.getDeviceById(deviceId); |
| | | DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType()); |
| | | |
| | | // æ§è¡æä½ |
| | | Map<String, Object> params = new HashMap<>(); |
| | | params.put("glassIds", Arrays.asList("GLS001")); |
| | | OperationResult result = handler.execute(device, "feedGlass", params); |
| | | ``` |
| | | |
| | | ## æ¯æçæä½ç±»å |
| | | |
| | | ### ä¸å¤§è½¦ï¼LoadVehicleLogicHandlerï¼ |
| | | - `feedGlass` - ç»ç䏿 |
| | | - `triggerRequest` - 触åè¯·æ± |
| | | - `triggerReport` - è§¦åæ±æ¥ |
| | | - `reset` - éç½® |
| | | |
| | | ### 大ççï¼LargeGlassLogicHandlerï¼ |
| | | - `processGlass` - ç»çå å·¥ |
| | | - `triggerRequest` - 触åè¯·æ± |
| | | - `triggerReport` - è§¦åæ±æ¥ |
| | | - `reset` - éç½® |
| | | |
| | | ### ç»çåå¨ï¼GlassStorageLogicHandlerï¼ |
| | | - `storeGlass` - åå¨ç»ç |
| | | - `retrieveGlass` - åè´§ |
| | | - `triggerRequest` - 触åè¯·æ± |
| | | - `triggerReport` - è§¦åæ±æ¥ |
| | | - `reset` - éç½® |
| | | |
| | | ## æ©å±æ°è®¾å¤ç±»å |
| | | |
| | | ### æ¥éª¤1ï¼å建å¤çå¨ç±» |
| | | |
| | | ```java |
| | | @Component |
| | | public class NewDeviceLogicHandler extends BaseDeviceLogicHandler { |
| | | |
| | | public NewDeviceLogicHandler(DevicePlcOperationService devicePlcOperationService) { |
| | | super(devicePlcOperationService); |
| | | } |
| | | |
| | | @Override |
| | | public String getDeviceType() { |
| | | return "æ°è®¾å¤ç±»å"; |
| | | } |
| | | |
| | | @Override |
| | | protected OperationResult doExecute(...) { |
| | | // å®ç°å
·ä½é»è¾ |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### æ¥éª¤2ï¼å¨ DeviceConfig.DeviceType 䏿·»å 常é |
| | | |
| | | ```java |
| | | public static final String NEW_DEVICE = "æ°è®¾å¤ç±»å"; |
| | | ``` |
| | | |
| | | ### æ¥éª¤3ï¼é
ç½®é»è®¤åæ° |
| | | |
| | | å®ç° `getDefaultLogicParams()` æ¹æ³ï¼è¿åé»è®¤çJSONé
ç½®ã |
| | | |
| | | ## åæ°è¯´æ |
| | | |
| | | ### é
ç½®åæ°ï¼ä» extraParams.deviceLogic 读åï¼ |
| | | - éæé
ç½®ï¼å¨è®¾å¤é
ç½®æ¶è®¾ç½® |
| | | - åå¨å¨æ°æ®åºä¸ |
| | | - 示ä¾ï¼vehicleCapacity, glassIntervalMs |
| | | |
| | | ### è¿è¡æ¶åæ°ï¼ä»æ¹æ³åæ°ä¼ å
¥ï¼ |
| | | - å¨æåæ°ï¼æ¯æ¬¡è°ç¨æ¶ä¼ å
¥ |
| | | - 示ä¾ï¼glassIds, positionCode |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | 1. **åæ°éªè¯**ï¼æ¯ä¸ª Handler å®ç° `validateLogicParams()` æ¹æ³è¿è¡åæ°éªè¯ |
| | | 2. **é»è®¤å¼å¤ç**ï¼ä½¿ç¨ `getLogicParam()` æ¹æ³è·ååæ°ï¼æ¯æé»è®¤å¼ |
| | | 3. **é误å¤ç**ï¼ææå¼å¸¸é½ä¼è¢«æè·å¹¶è¿å `OperationResult` |
| | | 4. **ååå
¼å®¹**ï¼ä¿çäºåæç `feedGlass()` æ¹æ³ï¼ä¼å
ä½¿ç¨æ°æ¶æï¼å¤±è´¥æ¶éçº§å°æ§é»è¾ |
| | | |
| New file |
| | |
| | | package com.mes.interaction.base; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.task.model.TaskExecutionContext; |
| | | import com.mes.task.dto.TaskParameters; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 交äºä¸ä¸æ |
| | | */ |
| | | @Getter |
| | | public class InteractionContext { |
| | | |
| | | private final DeviceConfig currentDevice; |
| | | private final TaskExecutionContext taskContext; |
| | | |
| | | public InteractionContext(DeviceConfig currentDevice, TaskExecutionContext taskContext) { |
| | | this.currentDevice = currentDevice; |
| | | this.taskContext = taskContext; |
| | | } |
| | | |
| | | public TaskParameters getParameters() { |
| | | return taskContext.getParameters(); |
| | | } |
| | | |
| | | public Map<String, Object> getSharedData() { |
| | | return taskContext.getSharedData(); |
| | | } |
| | | |
| | | public void setLoadedGlassIds(List<String> glassIds) { |
| | | taskContext.setLoadedGlassIds(glassIds); |
| | | } |
| | | |
| | | public void setProcessedGlassIds(List<String> glassIds) { |
| | | taskContext.setProcessedGlassIds(glassIds); |
| | | } |
| | | |
| | | public List<String> getLoadedGlassIds() { |
| | | return taskContext.getSafeLoadedGlassIds(); |
| | | } |
| | | |
| | | public List<String> getProcessedGlassIds() { |
| | | return taskContext.getSafeProcessedGlassIds(); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction.base; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * äº¤äºæ§è¡ç»æ |
| | | */ |
| | | @Getter |
| | | @Builder |
| | | @AllArgsConstructor |
| | | public class InteractionResult { |
| | | |
| | | public enum Status { |
| | | SUCCESS, WAITING, FAILED |
| | | } |
| | | |
| | | private final Status status; |
| | | private final String message; |
| | | @Builder.Default |
| | | private final Map<String, Object> data = new HashMap<>(); |
| | | |
| | | public static InteractionResult success(Map<String, Object> payload) { |
| | | return InteractionResult.builder() |
| | | .status(Status.SUCCESS) |
| | | .message("success") |
| | | .data(payload != null ? payload : Collections.emptyMap()) |
| | | .build(); |
| | | } |
| | | |
| | | public static InteractionResult waitResult(String message, Map<String, Object> payload) { |
| | | return InteractionResult.builder() |
| | | .status(Status.WAITING) |
| | | .message(message) |
| | | .data(payload != null ? payload : Collections.emptyMap()) |
| | | .build(); |
| | | } |
| | | |
| | | public static InteractionResult fail(String message) { |
| | | return InteractionResult.builder() |
| | | .status(Status.FAILED) |
| | | .message(message) |
| | | .data(Collections.emptyMap()) |
| | | .build(); |
| | | } |
| | | |
| | | public boolean isSuccess() { |
| | | return status == Status.SUCCESS; |
| | | } |
| | | |
| | | public boolean isWaiting() { |
| | | return status == Status.WAITING; |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction.flow; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.interaction.DeviceInteraction; |
| | | import com.mes.interaction.base.InteractionContext; |
| | | import com.mes.interaction.base.InteractionResult; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * ç»çåå¨äº¤äºå®ç° |
| | | */ |
| | | @Component |
| | | public class GlassStorageInteraction implements DeviceInteraction { |
| | | |
| | | @Override |
| | | public String getDeviceType() { |
| | | return DeviceConfig.DeviceType.GLASS_STORAGE; |
| | | } |
| | | |
| | | @Override |
| | | public InteractionResult execute(InteractionContext context) { |
| | | List<String> processed = context.getProcessedGlassIds(); |
| | | if (CollectionUtils.isEmpty(processed)) { |
| | | return InteractionResult.waitResult("没æå¯åå¨çç»ç", null); |
| | | } |
| | | |
| | | Map<String, Object> data = new HashMap<>(); |
| | | data.put("storedCount", processed.size()); |
| | | data.put("storedGlasses", processed); |
| | | return InteractionResult.success(data); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction.flow; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.interaction.DeviceInteraction; |
| | | import com.mes.interaction.base.InteractionContext; |
| | | import com.mes.interaction.base.InteractionResult; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 大çç交äºå®ç° |
| | | */ |
| | | @Component |
| | | public class LargeGlassInteraction implements DeviceInteraction { |
| | | |
| | | @Override |
| | | public String getDeviceType() { |
| | | return DeviceConfig.DeviceType.LARGE_GLASS; |
| | | } |
| | | |
| | | @Override |
| | | public InteractionResult execute(InteractionContext context) { |
| | | Object source = context.getSharedData().get("glassesFromVehicle"); |
| | | List<String> glassQueue = castList(source); |
| | | if (CollectionUtils.isEmpty(glassQueue)) { |
| | | return InteractionResult.waitResult("çå¾
ä¸å¤§è½¦è¾åº", null); |
| | | } |
| | | |
| | | List<String> processed = new ArrayList<>(glassQueue); |
| | | context.setProcessedGlassIds(processed); |
| | | context.getSharedData().put("processedGlasses", processed); |
| | | |
| | | Map<String, Object> data = new HashMap<>(); |
| | | data.put("processedCount", processed.size()); |
| | | data.put("processedGlasses", processed); |
| | | return InteractionResult.success(data); |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | private List<String> castList(Object value) { |
| | | if (value instanceof List) { |
| | | return (List<String>) value; |
| | | } |
| | | return null; |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction.flow; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.interaction.DeviceInteraction; |
| | | import com.mes.interaction.base.InteractionContext; |
| | | import com.mes.interaction.base.InteractionResult; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * ä¸å¤§è½¦äº¤äºå®ç° |
| | | */ |
| | | @Component |
| | | public class LoadVehicleInteraction implements DeviceInteraction { |
| | | |
| | | @Override |
| | | public String getDeviceType() { |
| | | return DeviceConfig.DeviceType.LOAD_VEHICLE; |
| | | } |
| | | |
| | | @Override |
| | | public InteractionResult execute(InteractionContext context) { |
| | | List<String> glassIds = context.getParameters().getGlassIds(); |
| | | if (CollectionUtils.isEmpty(glassIds)) { |
| | | return InteractionResult.waitResult("æªæä¾ç»çIDï¼çå¾
è¾å
¥", null); |
| | | } |
| | | |
| | | List<String> copied = new ArrayList<>(glassIds); |
| | | context.setLoadedGlassIds(copied); |
| | | context.getSharedData().put("glassesFromVehicle", copied); |
| | | |
| | | Map<String, Object> data = new HashMap<>(); |
| | | data.put("loaded", copied); |
| | | data.put("glassCount", copied.size()); |
| | | return InteractionResult.success(data); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction.impl; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.interaction.BaseDeviceLogicHandler; |
| | | import com.mes.device.service.DevicePlcOperationService; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * ç»çåå¨è®¾å¤é»è¾å¤çå¨ |
| | | * |
| | | * @author mes |
| | | * @since 2025-01-XX |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class GlassStorageLogicHandler extends BaseDeviceLogicHandler { |
| | | |
| | | public GlassStorageLogicHandler(DevicePlcOperationService devicePlcOperationService) { |
| | | super(devicePlcOperationService); |
| | | } |
| | | |
| | | @Override |
| | | public String getDeviceType() { |
| | | return DeviceConfig.DeviceType.GLASS_STORAGE; |
| | | } |
| | | |
| | | @Override |
| | | protected DevicePlcVO.OperationResult doExecute( |
| | | DeviceConfig deviceConfig, |
| | | String operation, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | log.info("æ§è¡ç»çåå¨è®¾å¤æä½: deviceId={}, operation={}", deviceConfig.getId(), operation); |
| | | |
| | | switch (operation) { |
| | | case "storeGlass": |
| | | return handleStoreGlass(deviceConfig, params, logicParams); |
| | | case "retrieveGlass": |
| | | return handleRetrieveGlass(deviceConfig, params, logicParams); |
| | | case "triggerRequest": |
| | | return handleTriggerRequest(deviceConfig, params, logicParams); |
| | | case "triggerReport": |
| | | return handleTriggerReport(deviceConfig, params, logicParams); |
| | | case "reset": |
| | | return handleReset(deviceConfig, params, logicParams); |
| | | default: |
| | | log.warn("䏿¯æçæä½ç±»å: {}", operation); |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("䏿¯æçæä½: " + operation) |
| | | .build(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¤çåå¨ç»çæä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleStoreGlass( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | // ä»é»è¾åæ°ä¸è·åé
ç½® |
| | | Integer storageCapacity = getLogicParam(logicParams, "storageCapacity", 100); |
| | | String retrievalMode = getLogicParam(logicParams, "retrievalMode", "FIFO"); |
| | | Boolean autoStore = getLogicParam(logicParams, "autoStore", true); |
| | | |
| | | // ä»è¿è¡æ¶åæ°ä¸è·åæ°æ® |
| | | String glassId = (String) params.get("glassId"); |
| | | Integer storagePosition = (Integer) params.get("storagePosition"); |
| | | Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoStore); |
| | | |
| | | // æå»ºåå
¥æ°æ® |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | |
| | | if (glassId != null) { |
| | | payload.put("plcGlassId", glassId); |
| | | } |
| | | |
| | | if (storagePosition != null) { |
| | | payload.put("storagePosition", storagePosition); |
| | | } |
| | | |
| | | // èªå¨è§¦åè¯·æ± |
| | | if (triggerRequest != null && triggerRequest) { |
| | | payload.put("plcRequest", 1); |
| | | } |
| | | |
| | | log.info("ç»çåå¨: deviceId={}, glassId={}, position={}", |
| | | deviceConfig.getId(), glassId, storagePosition); |
| | | |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "ç»çåå¨-åå¨ç»ç" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤çåè´§æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleRetrieveGlass( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | // ä»é»è¾åæ°ä¸è·åé
ç½® |
| | | String retrievalMode = getLogicParam(logicParams, "retrievalMode", "FIFO"); |
| | | Boolean autoRetrieve = getLogicParam(logicParams, "autoRetrieve", true); |
| | | |
| | | // ä»è¿è¡æ¶åæ°ä¸è·åæ°æ® |
| | | Integer storagePosition = (Integer) params.get("storagePosition"); |
| | | String glassId = (String) params.get("glassId"); |
| | | Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoRetrieve); |
| | | |
| | | // æå»ºåå
¥æ°æ® |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | |
| | | if (storagePosition != null) { |
| | | payload.put("retrievePosition", storagePosition); |
| | | } |
| | | |
| | | if (glassId != null) { |
| | | payload.put("retrieveGlassId", glassId); |
| | | } |
| | | |
| | | // èªå¨è§¦åè¯·æ± |
| | | if (triggerRequest != null && triggerRequest) { |
| | | payload.put("plcRequest", 1); |
| | | } |
| | | |
| | | log.info("ç»çåè´§: deviceId={}, position={}, glassId={}", |
| | | deviceConfig.getId(), storagePosition, glassId); |
| | | |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "ç»çåå¨-åè´§" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤ç触åè¯·æ±æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleTriggerRequest( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcRequest", 1); |
| | | |
| | | log.info("ç»çåå¨è§¦å请æ±: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "ç»çåå¨-触å请æ±" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤çè§¦åæ±æ¥æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleTriggerReport( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcReport", 1); |
| | | |
| | | log.info("ç»çåå¨è§¦åæ±æ¥: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "ç»çåå¨-è§¦åæ±æ¥" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤çéç½®æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleReset( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcRequest", 0); |
| | | payload.put("plcReport", 0); |
| | | |
| | | log.info("ç»çåå¨éç½®: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "ç»çåå¨-éç½®" |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | public String validateLogicParams(DeviceConfig deviceConfig) { |
| | | Map<String, Object> logicParams = parseLogicParams(deviceConfig); |
| | | |
| | | // éªè¯å¿
å¡«åæ° |
| | | Integer storageCapacity = getLogicParam(logicParams, "storageCapacity", null); |
| | | if (storageCapacity != null && storageCapacity <= 0) { |
| | | return "åå¨å®¹é(storageCapacity)å¿
须大äº0"; |
| | | } |
| | | |
| | | String retrievalMode = getLogicParam(logicParams, "retrievalMode", null); |
| | | if (retrievalMode != null && !retrievalMode.matches("FIFO|LIFO|RANDOM")) { |
| | | return "å货模å¼(retrievalMode)å¿
é¡»æ¯FIFOãLIFOæRANDOM"; |
| | | } |
| | | |
| | | return null; // éªè¯éè¿ |
| | | } |
| | | |
| | | @Override |
| | | public String getDefaultLogicParams() { |
| | | Map<String, Object> defaultParams = new HashMap<>(); |
| | | defaultParams.put("storageCapacity", 100); |
| | | defaultParams.put("retrievalMode", "FIFO"); |
| | | defaultParams.put("autoStore", true); |
| | | defaultParams.put("autoRetrieve", true); |
| | | defaultParams.put("maxRetryCount", 3); |
| | | |
| | | try { |
| | | return objectMapper.writeValueAsString(defaultParams); |
| | | } catch (Exception e) { |
| | | log.error("çæé»è®¤é»è¾åæ°å¤±è´¥", e); |
| | | return "{}"; |
| | | } |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction.impl; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.interaction.BaseDeviceLogicHandler; |
| | | import com.mes.device.service.DevicePlcOperationService; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 大çç设å¤é»è¾å¤çå¨ |
| | | * |
| | | * @author mes |
| | | * @since 2025-01-XX |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class LargeGlassLogicHandler extends BaseDeviceLogicHandler { |
| | | |
| | | public LargeGlassLogicHandler(DevicePlcOperationService devicePlcOperationService) { |
| | | super(devicePlcOperationService); |
| | | } |
| | | |
| | | @Override |
| | | public String getDeviceType() { |
| | | return DeviceConfig.DeviceType.LARGE_GLASS; |
| | | } |
| | | |
| | | @Override |
| | | protected DevicePlcVO.OperationResult doExecute( |
| | | DeviceConfig deviceConfig, |
| | | String operation, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | log.info("æ§è¡å¤§ççè®¾å¤æä½: deviceId={}, operation={}", deviceConfig.getId(), operation); |
| | | |
| | | switch (operation) { |
| | | case "processGlass": |
| | | return handleProcessGlass(deviceConfig, params, logicParams); |
| | | case "triggerRequest": |
| | | return handleTriggerRequest(deviceConfig, params, logicParams); |
| | | case "triggerReport": |
| | | return handleTriggerReport(deviceConfig, params, logicParams); |
| | | case "reset": |
| | | return handleReset(deviceConfig, params, logicParams); |
| | | default: |
| | | log.warn("䏿¯æçæä½ç±»å: {}", operation); |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("䏿¯æçæä½: " + operation) |
| | | .build(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¤çç»çå å·¥æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleProcessGlass( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | // ä»é»è¾åæ°ä¸è·åé
ç½® |
| | | Integer glassSize = getLogicParam(logicParams, "glassSize", 2000); |
| | | Integer processingTime = getLogicParam(logicParams, "processingTime", 5000); |
| | | Boolean autoProcess = getLogicParam(logicParams, "autoProcess", true); |
| | | |
| | | // ä»è¿è¡æ¶åæ°ä¸è·åæ°æ® |
| | | String glassId = (String) params.get("glassId"); |
| | | Integer processType = (Integer) params.get("processType"); |
| | | Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoProcess); |
| | | |
| | | // æå»ºåå
¥æ°æ® |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | |
| | | if (glassId != null) { |
| | | payload.put("plcGlassId", glassId); |
| | | } |
| | | |
| | | if (processType != null) { |
| | | payload.put("processType", processType); |
| | | } |
| | | |
| | | // èªå¨è§¦åè¯·æ± |
| | | if (triggerRequest != null && triggerRequest) { |
| | | payload.put("plcRequest", 1); |
| | | } |
| | | |
| | | log.info("大ççç»çå å·¥: deviceId={}, glassId={}, processType={}", |
| | | deviceConfig.getId(), glassId, processType); |
| | | |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "大çç-ç»çå å·¥" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤ç触åè¯·æ±æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleTriggerRequest( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcRequest", 1); |
| | | |
| | | log.info("大çç触å请æ±: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "大çç-触å请æ±" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤çè§¦åæ±æ¥æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleTriggerReport( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcReport", 1); |
| | | |
| | | log.info("大ççè§¦åæ±æ¥: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "大çç-è§¦åæ±æ¥" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤çéç½®æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleReset( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcRequest", 0); |
| | | payload.put("plcReport", 0); |
| | | |
| | | log.info("大ççéç½®: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "大çç-éç½®" |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | public String validateLogicParams(DeviceConfig deviceConfig) { |
| | | Map<String, Object> logicParams = parseLogicParams(deviceConfig); |
| | | |
| | | // éªè¯å¿
å¡«åæ° |
| | | Integer glassSize = getLogicParam(logicParams, "glassSize", null); |
| | | if (glassSize != null && glassSize <= 0) { |
| | | return "ç»ç尺寸(glassSize)å¿
须大äº0"; |
| | | } |
| | | |
| | | Integer processingTime = getLogicParam(logicParams, "processingTime", null); |
| | | if (processingTime != null && processingTime < 0) { |
| | | return "å¤çæ¶é´(processingTime)ä¸è½ä¸ºè´æ°"; |
| | | } |
| | | |
| | | return null; // éªè¯éè¿ |
| | | } |
| | | |
| | | @Override |
| | | public String getDefaultLogicParams() { |
| | | Map<String, Object> defaultParams = new HashMap<>(); |
| | | defaultParams.put("glassSize", 2000); |
| | | defaultParams.put("processingTime", 5000); |
| | | defaultParams.put("autoProcess", true); |
| | | defaultParams.put("maxRetryCount", 3); |
| | | |
| | | try { |
| | | return objectMapper.writeValueAsString(defaultParams); |
| | | } catch (Exception e) { |
| | | log.error("çæé»è®¤é»è¾åæ°å¤±è´¥", e); |
| | | return "{}"; |
| | | } |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.interaction.impl; |
| | | |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.interaction.BaseDeviceLogicHandler; |
| | | import com.mes.device.service.DevicePlcOperationService; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * ä¸å¤§è½¦è®¾å¤é»è¾å¤çå¨ |
| | | * |
| | | * @author mes |
| | | * @since 2025-01-XX |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class LoadVehicleLogicHandler extends BaseDeviceLogicHandler { |
| | | |
| | | public LoadVehicleLogicHandler(DevicePlcOperationService devicePlcOperationService) { |
| | | super(devicePlcOperationService); |
| | | } |
| | | |
| | | @Override |
| | | public String getDeviceType() { |
| | | return DeviceConfig.DeviceType.LOAD_VEHICLE; |
| | | } |
| | | |
| | | @Override |
| | | protected DevicePlcVO.OperationResult doExecute( |
| | | DeviceConfig deviceConfig, |
| | | String operation, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | log.info("æ§è¡ä¸å¤§è½¦è®¾å¤æä½: deviceId={}, operation={}", deviceConfig.getId(), operation); |
| | | |
| | | switch (operation) { |
| | | case "feedGlass": |
| | | return handleFeedGlass(deviceConfig, params, logicParams); |
| | | case "triggerRequest": |
| | | return handleTriggerRequest(deviceConfig, params, logicParams); |
| | | case "triggerReport": |
| | | return handleTriggerReport(deviceConfig, params, logicParams); |
| | | case "reset": |
| | | return handleReset(deviceConfig, params, logicParams); |
| | | default: |
| | | log.warn("䏿¯æçæä½ç±»å: {}", operation); |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("䏿¯æçæä½: " + operation) |
| | | .build(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¤çç»ç䏿æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleFeedGlass( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | // ä»é»è¾åæ°ä¸è·åé
ç½®ï¼ä» extraParams.deviceLogic 读åï¼ |
| | | Integer vehicleCapacity = getLogicParam(logicParams, "vehicleCapacity", 6000); |
| | | Integer glassIntervalMs = getLogicParam(logicParams, "glassIntervalMs", 1000); |
| | | Boolean autoFeed = getLogicParam(logicParams, "autoFeed", true); |
| | | Integer maxRetryCount = getLogicParam(logicParams, "maxRetryCount", 5); |
| | | |
| | | // ä»è¿è¡æ¶åæ°ä¸è·åæ°æ®ï¼ä»æ¥å£è°ç¨æ¶ä¼ å
¥ï¼ |
| | | List<GlassInfo> glassInfos = extractGlassInfos(params); |
| | | if (glassInfos.isEmpty()) { |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("æªæä¾ææçç»çä¿¡æ¯") |
| | | .build(); |
| | | } |
| | | |
| | | String positionCode = (String) params.get("positionCode"); |
| | | Integer positionValue = (Integer) params.get("positionValue"); |
| | | Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoFeed); |
| | | |
| | | List<GlassInfo> plannedGlasses = planGlassLoading(glassInfos, vehicleCapacity, |
| | | getLogicParam(logicParams, "defaultGlassLength", 2000)); |
| | | if (plannedGlasses.isEmpty()) { |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("å½åç»ç尺寸è¶
åºè½¦è¾å®¹éï¼æ æ³è£
è½½") |
| | | .build(); |
| | | } |
| | | |
| | | // æå»ºåå
¥æ°æ® |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | |
| | | // åå
¥ç»çID |
| | | int plcSlots = Math.min(plannedGlasses.size(), 6); |
| | | for (int i = 0; i < plcSlots; i++) { |
| | | String fieldName = "plcGlassId" + (i + 1); |
| | | payload.put(fieldName, plannedGlasses.get(i).getGlassId()); |
| | | } |
| | | payload.put("plcGlassCount", plcSlots); |
| | | |
| | | // åå
¥ä½ç½®ä¿¡æ¯ |
| | | if (positionValue != null) { |
| | | payload.put("inPosition", positionValue); |
| | | } else if (positionCode != null) { |
| | | // ä»ä½ç½®æ å°ä¸è·åä½ç½®å¼ |
| | | @SuppressWarnings("unchecked") |
| | | Map<String, Integer> positionMapping = getLogicParam(logicParams, "positionMapping", new HashMap<>()); |
| | | Integer mappedValue = positionMapping.get(positionCode); |
| | | if (mappedValue != null) { |
| | | payload.put("inPosition", mappedValue); |
| | | } |
| | | } |
| | | |
| | | // èªå¨è§¦å请æ±å |
| | | if (triggerRequest != null && triggerRequest) { |
| | | payload.put("plcRequest", 1); |
| | | } |
| | | |
| | | String operationName = "ä¸å¤§è½¦-ç»ç䏿"; |
| | | if (positionCode != null) { |
| | | operationName += "(" + positionCode + ")"; |
| | | } |
| | | |
| | | log.info("ä¸å¤§è½¦ç»ç䏿: deviceId={}, glassCount={}, position={}, plannedGlassIds={}", |
| | | deviceConfig.getId(), plcSlots, positionCode, plannedGlasses); |
| | | |
| | | if (glassIntervalMs != null && glassIntervalMs > 0) { |
| | | try { |
| | | Thread.sleep(glassIntervalMs); |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | } |
| | | } |
| | | |
| | | return devicePlcOperationService.writeFields(deviceConfig.getId(), payload, operationName); |
| | | } |
| | | |
| | | /** |
| | | * å¤ç触åè¯·æ±æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleTriggerRequest( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcRequest", 1); |
| | | |
| | | log.info("ä¸å¤§è½¦è§¦å请æ±: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "ä¸å¤§è½¦-触å请æ±" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤çè§¦åæ±æ¥æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleTriggerReport( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcReport", 1); |
| | | |
| | | log.info("ä¸å¤§è½¦è§¦åæ±æ¥: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "ä¸å¤§è½¦-è§¦åæ±æ¥" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * å¤çéç½®æä½ |
| | | */ |
| | | private DevicePlcVO.OperationResult handleReset( |
| | | DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("plcRequest", 0); |
| | | payload.put("plcReport", 0); |
| | | |
| | | log.info("ä¸å¤§è½¦éç½®: deviceId={}", deviceConfig.getId()); |
| | | return devicePlcOperationService.writeFields( |
| | | deviceConfig.getId(), |
| | | payload, |
| | | "ä¸å¤§è½¦-éç½®" |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | public String validateLogicParams(DeviceConfig deviceConfig) { |
| | | Map<String, Object> logicParams = parseLogicParams(deviceConfig); |
| | | |
| | | // éªè¯å¿
å¡«åæ° |
| | | Integer vehicleCapacity = getLogicParam(logicParams, "vehicleCapacity", null); |
| | | if (vehicleCapacity == null || vehicleCapacity <= 0) { |
| | | return "车è¾å®¹é(vehicleCapacity)å¿
须大äº0"; |
| | | } |
| | | |
| | | Integer glassIntervalMs = getLogicParam(logicParams, "glassIntervalMs", null); |
| | | if (glassIntervalMs != null && glassIntervalMs < 0) { |
| | | return "ç»çé´éæ¶é´(glassIntervalMs)ä¸è½ä¸ºè´æ°"; |
| | | } |
| | | |
| | | return null; // éªè¯éè¿ |
| | | } |
| | | |
| | | @Override |
| | | public String getDefaultLogicParams() { |
| | | Map<String, Object> defaultParams = new HashMap<>(); |
| | | defaultParams.put("vehicleCapacity", 6000); |
| | | defaultParams.put("glassIntervalMs", 1000); |
| | | defaultParams.put("autoFeed", true); |
| | | defaultParams.put("maxRetryCount", 5); |
| | | defaultParams.put("defaultGlassLength", 2000); |
| | | |
| | | Map<String, Integer> positionMapping = new HashMap<>(); |
| | | positionMapping.put("POS1", 1); |
| | | positionMapping.put("POS2", 2); |
| | | defaultParams.put("positionMapping", positionMapping); |
| | | |
| | | try { |
| | | return objectMapper.writeValueAsString(defaultParams); |
| | | } catch (Exception e) { |
| | | log.error("çæé»è®¤é»è¾åæ°å¤±è´¥", e); |
| | | return "{}"; |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | private List<GlassInfo> extractGlassInfos(Map<String, Object> params) { |
| | | List<GlassInfo> result = new ArrayList<>(); |
| | | Object rawGlassInfos = params.get("glassInfos"); |
| | | if (rawGlassInfos instanceof List) { |
| | | List<?> list = (List<?>) rawGlassInfos; |
| | | for (Object item : list) { |
| | | GlassInfo info = convertToGlassInfo(item); |
| | | if (info != null) { |
| | | result.add(info); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (result.isEmpty()) { |
| | | List<String> glassIds = (List<String>) params.get("glassIds"); |
| | | if (glassIds != null) { |
| | | for (String glassId : glassIds) { |
| | | result.add(new GlassInfo(glassId, null)); |
| | | } |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private GlassInfo convertToGlassInfo(Object source) { |
| | | if (source instanceof GlassInfo) { |
| | | return (GlassInfo) source; |
| | | } |
| | | if (source instanceof Map) { |
| | | Map<?, ?> map = (Map<?, ?>) source; |
| | | Object id = map.get("glassId"); |
| | | if (id == null) { |
| | | id = map.get("id"); |
| | | } |
| | | if (id == null) { |
| | | return null; |
| | | } |
| | | Integer length = parseLength(map.get("length")); |
| | | if (length == null) { |
| | | length = parseLength(map.get("size")); |
| | | } |
| | | return new GlassInfo(String.valueOf(id), length); |
| | | } |
| | | if (source instanceof String) { |
| | | return new GlassInfo((String) source, null); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Integer parseLength(Object value) { |
| | | if (value instanceof Number) { |
| | | return ((Number) value).intValue(); |
| | | } |
| | | if (value instanceof String) { |
| | | try { |
| | | return Integer.parseInt((String) value); |
| | | } catch (NumberFormatException ignored) { |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private List<GlassInfo> planGlassLoading(List<GlassInfo> source, |
| | | int vehicleCapacity, |
| | | Integer defaultGlassLength) { |
| | | List<GlassInfo> planned = new ArrayList<>(); |
| | | int usedLength = 0; |
| | | int capacity = Math.max(vehicleCapacity, 1); |
| | | int fallbackLength = defaultGlassLength != null && defaultGlassLength > 0 ? defaultGlassLength : 2000; |
| | | |
| | | for (GlassInfo info : source) { |
| | | int length = info.getLength() != null && info.getLength() > 0 ? info.getLength() : fallbackLength; |
| | | if (planned.isEmpty()) { |
| | | planned.add(info.withLength(length)); |
| | | usedLength = length; |
| | | continue; |
| | | } |
| | | if (usedLength + length <= capacity) { |
| | | planned.add(info.withLength(length)); |
| | | usedLength += length; |
| | | } else { |
| | | break; |
| | | } |
| | | } |
| | | return planned; |
| | | } |
| | | |
| | | private static class GlassInfo { |
| | | private final String glassId; |
| | | private final Integer length; |
| | | |
| | | GlassInfo(String glassId, Integer length) { |
| | | this.glassId = glassId; |
| | | this.length = length; |
| | | } |
| | | |
| | | public String getGlassId() { |
| | | return glassId; |
| | | } |
| | | |
| | | public Integer getLength() { |
| | | return length; |
| | | } |
| | | |
| | | public GlassInfo withLength(Integer newLength) { |
| | | return new GlassInfo(this.glassId, newLength); |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return glassId; |
| | | } |
| | | |
| | | @Override |
| | | public int hashCode() { |
| | | return Objects.hash(glassId, length); |
| | | } |
| | | |
| | | @Override |
| | | public boolean equals(Object obj) { |
| | | if (this == obj) return true; |
| | | if (obj == null || getClass() != obj.getClass()) return false; |
| | | GlassInfo other = (GlassInfo) obj; |
| | | return Objects.equals(glassId, other.glassId) && Objects.equals(length, other.length); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.github.xingshuangs.iot.common.enums.EDataType; |
| | | import com.github.xingshuangs.iot.protocol.s7.serializer.S7Parameter; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.entity.PlcAddress; |
| | | import com.mes.s7.enhanced.EnhancedS7Serializer; |
| | | |
| | |
| | | * @param s7Serializer S7åºååå¨ |
| | | */ |
| | | void writePlcField(PlcAddress config, String fieldName, Object value, EnhancedS7Serializer s7Serializer); |
| | | |
| | | /** |
| | | * æ ¹æ®DeviceConfigé
ç½®ååæ®µå称读åPLCæ°æ® |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @param fieldNames è¦è¯»åçåæ®µåç§°å表 |
| | | * @param s7Serializer S7åºååå¨ |
| | | * @return åæ®µå->å¼ çMap |
| | | */ |
| | | Map<String, Object> readPlcData(DeviceConfig device, List<String> fieldNames, EnhancedS7Serializer s7Serializer); |
| | | |
| | | /** |
| | | * æ ¹æ®DeviceConfigé
ç½®åæ°æ®Mapåå
¥PLC |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @param dataMap åæ®µå->å¼ çMap |
| | | * @param s7Serializer S7åºååå¨ |
| | | */ |
| | | void writePlcData(DeviceConfig device, Map<String, Object> dataMap, EnhancedS7Serializer s7Serializer); |
| | | |
| | | /** |
| | | * 读åPLCææå段ï¼åºäºDeviceConfigï¼ |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @param s7Serializer S7åºååå¨ |
| | | * @return ææå段çå¼ |
| | | */ |
| | | Map<String, Object> readAllPlcData(DeviceConfig device, EnhancedS7Serializer s7Serializer); |
| | | |
| | | /** |
| | | * 读ååä¸ªåæ®µï¼åºäºDeviceConfigï¼ |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @param fieldName åæ®µå |
| | | * @param s7Serializer S7åºååå¨ |
| | | * @return åæ®µå¼ |
| | | */ |
| | | Object readPlcField(DeviceConfig device, String fieldName, EnhancedS7Serializer s7Serializer); |
| | | |
| | | /** |
| | | * åå
¥åä¸ªåæ®µï¼åºäºDeviceConfigï¼ |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @param fieldName åæ®µå |
| | | * @param value åæ®µå¼ |
| | | * @param s7Serializer S7åºååå¨ |
| | | */ |
| | | void writePlcField(DeviceConfig device, String fieldName, Object value, EnhancedS7Serializer s7Serializer); |
| | | } |
| | |
| | | package com.mes.service; |
| | | |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType; |
| | | import com.github.xingshuangs.iot.protocol.s7.service.S7PLC; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.service.DeviceConfigService; |
| | | import com.mes.device.util.ConfigJsonHelper; |
| | | import com.mes.entity.PlcBaseData; |
| | | import com.mes.entity.PlcAddress; |
| | | import com.mes.service.PlcDynamicDataService; |
| | | import com.mes.s7.enhanced.EnhancedS7Serializer; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.Collections; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.ConcurrentMap; |
| | | |
| | |
| | | |
| | | @Resource |
| | | private PlcAddressService plcAddressService; |
| | | |
| | | @Resource |
| | | private DeviceConfigService deviceConfigService; |
| | | |
| | | @Resource |
| | | private PlcDynamicDataService plcDynamicDataService; |
| | | |
| | | private final ObjectMapper objectMapper = new ObjectMapper(); |
| | | private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<Map<String, Object>>() {}; |
| | | |
| | | private static final int ON = 1; |
| | | private static final int OFF = 0; |
| | |
| | | */ |
| | | public boolean simulatePlcRequest(String projectId) { |
| | | try { |
| | | // è·å项ç®é
ç½®ï¼æ°æ®åºå®ä½ï¼ |
| | | PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); |
| | | |
| | | // è·å对åºçS7Serializer |
| | | EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); |
| | | |
| | | // 读åå½åPLCç¶æ |
| | | PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); |
| | | |
| | | if (currentData.getOnlineState() == OFF) { |
| | | log.info("å½åPLCèæºæ¨¡å¼ä¸º0ï¼åæ¢èæº"); |
| | | if (config == null) { |
| | | log.error("项ç®é
ç½®ä¸åå¨: projectId={}", projectId); |
| | | return false; |
| | | }else if (currentData.getPlcReport() == ON){ |
| | | log.info("å½åä¸çPLCæ±æ¥å为1ï¼é置为0"); |
| | | currentData.setPlcReport(OFF); |
| | | } |
| | | // 设置PLC请æ±å为1ï¼è§¦åMESä»»å¡å¤çï¼ |
| | | currentData.setPlcRequest(ON); |
| | | s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); |
| | | log.info("模æPLCåé请æ±åæåï¼plcRequest=1, projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return true; |
| | | |
| | | EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); |
| | | if (s7Serializer == null) { |
| | | log.error("æ æ³å建S7Serializer: projectId={}", projectId); |
| | | return false; |
| | | } |
| | | return simulatePlcRequestInternal(projectId, config, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("模æPLC请æ±å失败", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private boolean simulatePlcRequestInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { |
| | | PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); |
| | | if (currentData == null) { |
| | | log.error("读åPLCæ°æ®å¤±è´¥ï¼è¿ånull: projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return false; |
| | | } |
| | | |
| | | if (currentData.getOnlineState() == OFF) { |
| | | log.info("å½åPLCèæºæ¨¡å¼ä¸º0ï¼åæ¢èæº"); |
| | | return false; |
| | | } else if (currentData.getPlcReport() == ON) { |
| | | log.info("å½åä¸çPLCæ±æ¥å为1ï¼é置为0"); |
| | | currentData.setPlcReport(OFF); |
| | | } |
| | | currentData.setPlcRequest(ON); |
| | | s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); |
| | | log.info("模æPLCåé请æ±åæåï¼plcRequest=1, projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | |
| | | try { |
| | | // è·å项ç®é
ç½®ï¼æ°æ®åºå®ä½ï¼ |
| | | PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); |
| | | if (config == null) { |
| | | log.error("项ç®é
ç½®ä¸åå¨: projectId={}", projectId); |
| | | return false; |
| | | } |
| | | |
| | | // è·å对åºçS7Serializer |
| | | EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); |
| | | if (s7Serializer == null) { |
| | | log.error("æ æ³å建S7Serializer: projectId={}", projectId); |
| | | return false; |
| | | } |
| | | |
| | | PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); |
| | | |
| | | // 设置PLCæ±æ¥å为1ï¼ä»»å¡å®æï¼ |
| | | currentData.setPlcReport(ON); |
| | | // 请æ±åæ¸
0 |
| | | currentData.setPlcRequest(OFF); |
| | | |
| | | // è®¾ç½®å®ææ°éçæ°æ® |
| | | currentData.setMesGlassCount(10); |
| | | |
| | | s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); |
| | | log.info("模æPLCä»»å¡å®ææ±æ¥ï¼plcReport=1, mesGlassCount=10, projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return true; |
| | | |
| | | return simulatePlcReportInternal(projectId, config, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("模æPLCä»»å¡å®ææ±æ¥å¤±è´¥", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private boolean simulatePlcReportInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { |
| | | PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); |
| | | if (currentData == null) { |
| | | log.error("读åPLCæ°æ®å¤±è´¥ï¼è¿ånull: projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return false; |
| | | } |
| | | |
| | | currentData.setPlcReport(ON); |
| | | currentData.setPlcRequest(OFF); |
| | | currentData.setMesGlassCount(10); |
| | | |
| | | s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); |
| | | log.info("模æPLCä»»å¡å®ææ±æ¥ï¼plcReport=1, mesGlassCount=10, projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | |
| | | try { |
| | | // è·å项ç®é
ç½®ï¼æ°æ®åºå®ä½ï¼ |
| | | PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); |
| | | if (config == null) { |
| | | log.error("项ç®é
ç½®ä¸åå¨: projectId={}", projectId); |
| | | return false; |
| | | } |
| | | |
| | | // è·å对åºçS7Serializer |
| | | EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); |
| | | if (s7Serializer == null) { |
| | | log.error("æ æ³å建S7Serializer: projectId={}", projectId); |
| | | return false; |
| | | } |
| | | |
| | | PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); |
| | | |
| | | // 1:èæº 0:è±æº |
| | | currentData.setOnlineState(onlineState); |
| | | |
| | | s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); |
| | | log.info("模æPLCèæºç¶æï¼onlineState={}, projectId={}, dbArea={}, beginIndex={}", |
| | | onlineState, projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return true; |
| | | |
| | | return simulateOnlineStatusInternal(onlineState, projectId, config, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("模æPLCèæºç¶æå¤±è´¥", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private boolean simulateOnlineStatusInternal(int onlineState, String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { |
| | | PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); |
| | | if (currentData == null) { |
| | | log.error("读åPLCæ°æ®å¤±è´¥ï¼è¿ånull: projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return false; |
| | | } |
| | | |
| | | currentData.setOnlineState(onlineState); |
| | | s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); |
| | | log.info("模æPLCèæºç¶æï¼onlineState={}, projectId={}, dbArea={}, beginIndex={}", |
| | | onlineState, projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | |
| | | try { |
| | | // è·å项ç®é
ç½®ï¼æ°æ®åºå®ä½ï¼ |
| | | PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); |
| | | if (config == null) { |
| | | log.error("项ç®é
ç½®ä¸åå¨: projectId={}", projectId); |
| | | return false; |
| | | } |
| | | |
| | | // è·å对åºçS7Serializer |
| | | EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); |
| | | |
| | | PlcBaseData resetData = new PlcBaseData(); |
| | | |
| | | // éç½®ææå
³é®å段 |
| | | resetData.setPlcRequest(OFF); |
| | | resetData.setPlcReport(OFF); |
| | | resetData.setMesSend(OFF); |
| | | resetData.setMesConfirm(OFF); |
| | | // é»è®¤èæº |
| | | resetData.setOnlineState(ON); |
| | | resetData.setMesGlassCount(0); |
| | | // æ¸
餿¥è¦ |
| | | resetData.setAlarmInfo(OFF); |
| | | |
| | | s7Serializer.write(resetData, config.getDbArea(), config.getBeginIndex()); |
| | | log.info("PLCç¶æå·²éç½®, projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return true; |
| | | |
| | | if (s7Serializer == null) { |
| | | log.error("æ æ³å建S7Serializer: projectId={}", projectId); |
| | | return false; |
| | | } |
| | | return resetPlcInternal(projectId, config, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("éç½®PLCç¶æå¤±è´¥", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private boolean resetPlcInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { |
| | | PlcBaseData resetData = new PlcBaseData(); |
| | | resetData.setPlcRequest(OFF); |
| | | resetData.setPlcReport(OFF); |
| | | resetData.setMesSend(OFF); |
| | | resetData.setMesConfirm(OFF); |
| | | resetData.setOnlineState(ON); |
| | | resetData.setMesGlassCount(0); |
| | | resetData.setAlarmInfo(OFF); |
| | | |
| | | s7Serializer.write(resetData, config.getDbArea(), config.getBeginIndex()); |
| | | log.info("PLCç¶æå·²éç½®, projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | |
| | | try { |
| | | // è·å项ç®é
ç½®ï¼æ°æ®åºå®ä½ï¼ |
| | | PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); |
| | | if (config == null) { |
| | | log.error("项ç®é
ç½®ä¸åå¨: projectId={}", projectId); |
| | | return null; |
| | | } |
| | | |
| | | // è·å对åºçS7Serializer |
| | | EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); |
| | | if (s7Serializer == null) { |
| | | log.error("æ æ³å建S7Serializer: projectId={}", projectId); |
| | | return null; |
| | | } |
| | | |
| | | return s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); |
| | | return readPlcStatusInternal(projectId, config, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("读åPLCç¶æå¤±è´¥", e); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private PlcBaseData readPlcStatusInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { |
| | | PlcBaseData data = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); |
| | | if (data == null) { |
| | | log.error("读åPLCç¶æè¿ånull: projectId={}, dbArea={}, beginIndex={}", |
| | | projectId, config.getDbArea(), config.getBeginIndex()); |
| | | } |
| | | return data; |
| | | } |
| | | |
| | | /** |
| | |
| | | serializerCache.clear(); |
| | | log.info("å·²æ¸
餿æS7Serializerç¼å"); |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è®¾å¤ID模æPLCåé请æ±å |
| | | * |
| | | * @param deviceId 设å¤ID |
| | | * @return æ¯å¦æå |
| | | */ |
| | | public boolean simulatePlcRequestByDevice(Long deviceId) { |
| | | DeviceConfig device = deviceConfigService.getDeviceById(deviceId); |
| | | if (device == null) { |
| | | log.error("设å¤ä¸åå¨: deviceId={}", deviceId); |
| | | return false; |
| | | } |
| | | try { |
| | | String projectId = resolveProjectId(device); |
| | | PlcAddress config = buildPlcAddressFromDevice(device); |
| | | EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); |
| | | return simulatePlcRequestInternal(projectId, config, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("æ ¹æ®è®¾å¤æ¨¡æPLC请æ±å失败: deviceId={}", deviceId, e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è®¾å¤ID模æPLCä»»å¡å®ææ±æ¥ |
| | | * |
| | | * @param deviceId 设å¤ID |
| | | * @return æ¯å¦æå |
| | | */ |
| | | public boolean simulatePlcReportByDevice(Long deviceId) { |
| | | DeviceConfig device = deviceConfigService.getDeviceById(deviceId); |
| | | if (device == null) { |
| | | log.error("设å¤ä¸åå¨: deviceId={}", deviceId); |
| | | return false; |
| | | } |
| | | try { |
| | | String projectId = resolveProjectId(device); |
| | | PlcAddress config = buildPlcAddressFromDevice(device); |
| | | EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); |
| | | return simulatePlcReportInternal(projectId, config, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("æ ¹æ®è®¾å¤æ¨¡æPLCæ±æ¥å¤±è´¥: deviceId={}", deviceId, e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è®¾å¤IDéç½®PLCææç¶æ |
| | | * |
| | | * @param deviceId 设å¤ID |
| | | * @return æ¯å¦æå |
| | | */ |
| | | public boolean resetPlcByDevice(Long deviceId) { |
| | | DeviceConfig device = deviceConfigService.getDeviceById(deviceId); |
| | | if (device == null) { |
| | | log.error("设å¤ä¸åå¨: deviceId={}", deviceId); |
| | | return false; |
| | | } |
| | | try { |
| | | String projectId = resolveProjectId(device); |
| | | PlcAddress config = buildPlcAddressFromDevice(device); |
| | | EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); |
| | | return resetPlcInternal(projectId, config, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("æ ¹æ®è®¾å¤éç½®PLCç¶æå¤±è´¥: deviceId={}", deviceId, e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è®¾å¤ID读åPLCå½åç¶æ |
| | | * |
| | | * @param deviceId 设å¤ID |
| | | * @return PLCç¶ææ°æ® |
| | | */ |
| | | public Map<String, Object> readPlcStatusByDevice(Long deviceId) { |
| | | DeviceConfig device = deviceConfigService.getDeviceById(deviceId); |
| | | if (device == null) { |
| | | log.error("设å¤ä¸åå¨: deviceId={}", deviceId); |
| | | return null; |
| | | } |
| | | try { |
| | | String projectId = resolveProjectId(device); |
| | | PlcAddress config = buildPlcAddressFromDevice(device); |
| | | EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); |
| | | PlcBaseData data = readPlcStatusInternal(projectId, config, s7Serializer); |
| | | if (data == null) { |
| | | return null; |
| | | } |
| | | String json = objectMapper.writeValueAsString(data); |
| | | return objectMapper.readValue(json, MAP_TYPE); |
| | | } catch (Exception e) { |
| | | log.error("读å设å¤PLCç¶æå¤±è´¥: deviceId={}", deviceId, e); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è®¾å¤IDåå
¥PLCåæ®µ |
| | | * |
| | | * @param deviceId 设å¤ID |
| | | * @param fieldValues åæ®µå->å¼ çMap |
| | | * @return æ¯å¦æå |
| | | */ |
| | | public boolean writeFieldsByDevice(Long deviceId, Map<String, Object> fieldValues) { |
| | | DeviceConfig device = deviceConfigService.getDeviceById(deviceId); |
| | | if (device == null) { |
| | | log.error("设å¤ä¸åå¨: deviceId={}", deviceId); |
| | | return false; |
| | | } |
| | | |
| | | try { |
| | | // ä»è®¾å¤é
ç½®ä¸è·åé¡¹ç®æ è¯ |
| | | String projectId = resolveProjectId(device); |
| | | |
| | | // è·å对åºçS7Serializerï¼ä½¿ç¨è®¾å¤é
ç½®ï¼ |
| | | EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); |
| | | |
| | | // 使ç¨å¨ææ°æ®æå¡åå
¥å段ï¼åºäºDeviceConfigï¼ |
| | | plcDynamicDataService.writePlcData(device, fieldValues, s7Serializer); |
| | | |
| | | log.info("åå
¥PLCåæ®µæå: deviceId={}, projectId={}, fields={}", deviceId, projectId, fieldValues.keySet()); |
| | | return true; |
| | | } catch (Exception e) { |
| | | log.error("åå
¥PLCåæ®µå¤±è´¥: deviceId={}", deviceId, e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·å设å¤å¯¹åºçS7Serializerå®ä¾ |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @return S7Serializerå®ä¾ |
| | | */ |
| | | private EnhancedS7Serializer getSerializerForDevice(DeviceConfig device) { |
| | | String cacheKey = "device:" + (device.getId() != null ? device.getId() : resolveProjectId(device)); |
| | | return serializerCache.computeIfAbsent(cacheKey, id -> { |
| | | // è§£æPLCç±»åï¼ä»
åå®ä½åæ®µï¼ |
| | | EPlcType plcType = EPlcType.S1200; |
| | | String plcTypeValue = device.getPlcType(); |
| | | if (plcTypeValue == null || plcTypeValue.isEmpty()) { |
| | | log.warn("è®¾å¤æªé
ç½®PLCç±»åï¼ä½¿ç¨é»è®¤ç±»åS1200, deviceId={}", device.getId()); |
| | | } else { |
| | | try { |
| | | plcType = EPlcType.valueOf(plcTypeValue); |
| | | } catch (IllegalArgumentException e) { |
| | | log.warn("æªç¥çPLCç±»å: {}, 使ç¨é»è®¤ç±»å S1200", plcTypeValue); |
| | | } |
| | | } |
| | | |
| | | // å建S7PLCå®ä¾ï¼ä»
åå®ä½åæ®µï¼ |
| | | String plcIp = device.getPlcIp(); |
| | | if (plcIp == null || plcIp.isEmpty()) { |
| | | log.warn("è®¾å¤æªé
ç½®PLC IPï¼ä½¿ç¨é»è®¤ 192.168.10.21, deviceId={}", device.getId()); |
| | | plcIp = "192.168.10.21"; |
| | | } |
| | | S7PLC s7Plc = new S7PLC(plcType, plcIp); |
| | | |
| | | // å建并è¿åEnhancedS7Serializerå®ä¾ |
| | | return EnhancedS7Serializer.newInstance(s7Plc); |
| | | }); |
| | | } |
| | | |
| | | private PlcAddress buildPlcAddressFromDevice(DeviceConfig device) { |
| | | Map<String, Object> plcConfig = getPlcConfigParams(device); |
| | | String dbArea = plcConfig.get("dbArea") != null ? String.valueOf(plcConfig.get("dbArea")) : "DB12"; |
| | | int beginIndex = plcConfig.get("beginIndex") != null ? parseInteger(plcConfig.get("beginIndex")) : 0; |
| | | |
| | | String plcIp = device.getPlcIp(); |
| | | if (plcIp == null || plcIp.isEmpty()) { |
| | | log.warn("è®¾å¤æªé
ç½®PLC IPï¼ä½¿ç¨é»è®¤ 192.168.10.21, deviceId={}", device.getId()); |
| | | plcIp = "192.168.10.21"; |
| | | } |
| | | |
| | | String plcType = device.getPlcType(); |
| | | if (plcType == null || plcType.isEmpty()) { |
| | | log.warn("è®¾å¤æªé
ç½®PLCç±»åï¼ä½¿ç¨é»è®¤S1200, deviceId={}", device.getId()); |
| | | plcType = EPlcType.S1200.name(); |
| | | } |
| | | |
| | | String addressMapping = resolveAddressMapping(device); |
| | | |
| | | PlcAddress config = new PlcAddress(); |
| | | config.setProjectId(resolveProjectId(device)); |
| | | config.setDbArea(dbArea); |
| | | config.setBeginIndex(beginIndex); |
| | | config.setPlcIp(plcIp); |
| | | config.setPlcType(plcType); |
| | | config.setAddressMapping(addressMapping); |
| | | return config; |
| | | } |
| | | |
| | | private String resolveAddressMapping(DeviceConfig device) { |
| | | Map<String, Object> mapping = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper); |
| | | if (!mapping.isEmpty()) { |
| | | try { |
| | | return objectMapper.writeValueAsString(mapping); |
| | | } catch (Exception e) { |
| | | log.warn("åºååconfigJsonåæ®µæ å°å¤±è´¥, deviceId={}", device.getId(), e); |
| | | } |
| | | } |
| | | Map<String, Object> extraParams = parseExtraParams(device); |
| | | Object addressMapping = extraParams.get("addressMapping"); |
| | | if (addressMapping instanceof String) { |
| | | return (String) addressMapping; |
| | | } |
| | | if (addressMapping != null) { |
| | | try { |
| | | return objectMapper.writeValueAsString(addressMapping); |
| | | } catch (Exception e) { |
| | | log.warn("åºååextraParams.addressMapping失败, deviceId={}", device.getId(), e); |
| | | } |
| | | } |
| | | throw new IllegalStateException("è®¾å¤æªé
ç½®PLCåæ®µæ å°, deviceId=" + device.getId()); |
| | | } |
| | | |
| | | private Map<String, Object> parseExtraParams(DeviceConfig device) { |
| | | if (device.getExtraParams() == null || device.getExtraParams().trim().isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | try { |
| | | return objectMapper.readValue(device.getExtraParams(), MAP_TYPE); |
| | | } catch (Exception e) { |
| | | log.warn("è§£æè®¾å¤extraParams失败, deviceId={}", device.getId(), e); |
| | | return Collections.emptyMap(); |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | private Map<String, Object> getPlcConfigParams(DeviceConfig device) { |
| | | Map<String, Object> extraParams = parseExtraParams(device); |
| | | Object plcConfig = extraParams.get("plcConfig"); |
| | | if (plcConfig instanceof Map) { |
| | | return (Map<String, Object>) plcConfig; |
| | | } |
| | | if (plcConfig instanceof String) { |
| | | try { |
| | | return objectMapper.readValue((String) plcConfig, MAP_TYPE); |
| | | } catch (Exception e) { |
| | | log.warn("è§£æextraParams.plcConfig失败, deviceId={}", device.getId(), e); |
| | | } |
| | | } |
| | | return Collections.emptyMap(); |
| | | } |
| | | |
| | | private int parseInteger(Object value) { |
| | | if (value instanceof Number) { |
| | | return ((Number) value).intValue(); |
| | | } |
| | | try { |
| | | return Integer.parseInt(String.valueOf(value)); |
| | | } catch (NumberFormatException ex) { |
| | | log.warn("æ æ³è§£ææ´åå¼: {}", value); |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * ä»è®¾å¤é
ç½®ä¸è§£æé¡¹ç®æ è¯ |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @return é¡¹ç®æ è¯ |
| | | */ |
| | | private String resolveProjectId(DeviceConfig device) { |
| | | if (device == null) { |
| | | throw new IllegalArgumentException("设å¤ä¿¡æ¯ä¸ºç©º"); |
| | | } |
| | | |
| | | // 1. ä¼å
使ç¨å®ä½ä¸çprojectId |
| | | if (device.getProjectId() != null) { |
| | | return String.valueOf(device.getProjectId()); |
| | | } |
| | | |
| | | // 2. ä»extraParamsä¸è¯»å |
| | | Map<String, Object> extraParams = parseExtraParams(device); |
| | | Object plcProjectId = extraParams.get("plcProjectId"); |
| | | if (plcProjectId != null) { |
| | | return String.valueOf(plcProjectId); |
| | | } |
| | | |
| | | // 3. å
¼å®¹æ§ç»æï¼configJsonæextraParamså
åµ |
| | | Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper); |
| | | Object legacyProjectId = configParams.get("plcProjectId"); |
| | | if (legacyProjectId != null) { |
| | | return String.valueOf(legacyProjectId); |
| | | } |
| | | |
| | | // æå使ç¨è®¾å¤ç¼å· |
| | | if (device.getDeviceCode() != null && !device.getDeviceCode().isEmpty()) { |
| | | return device.getDeviceCode(); |
| | | } |
| | | |
| | | throw new IllegalStateException("æ æ³è§£æè®¾å¤çPLCé¡¹ç®æ è¯, deviceId=" + device.getId()); |
| | | } |
| | | } |
| | |
| | | package com.mes.service.impl; |
| | | |
| | | import com.mes.service.IPlcAutoTestService; |
| | | import com.mes.service.IPlcTestWriteService; |
| | | import com.mes.service.PlcAutoTestService; |
| | | import com.mes.service.PlcTestWriteService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class PlcAutoTestServiceImpl implements PlcAutoTestService { |
| | | public class PlcAutoTestServiceImpl extends PlcAutoTestService { |
| | | |
| | | @Resource |
| | | private IPlcTestWriteService plcTestWriteService; |
| | | private PlcTestWriteService plcTestWriteService; |
| | | |
| | | // èªå¨æµè¯å¼å
³ |
| | | @Value("${plc.auto.test.enabled:false}") |
| | |
| | | package com.mes.service.impl; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.github.xingshuangs.iot.common.enums.EDataType; |
| | | import com.github.xingshuangs.iot.protocol.s7.serializer.S7Parameter; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.util.ConfigJsonHelper; |
| | | import com.mes.entity.PlcAddress; |
| | | import com.mes.s7.enhanced.EnhancedS7Serializer; |
| | | import com.mes.service.PlcDynamicDataService; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | @Slf4j |
| | | @Service |
| | | public class PlcDynamicDataServiceImpl implements PlcDynamicDataService { |
| | | |
| | | private final ObjectMapper objectMapper = new ObjectMapper(); |
| | | private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<Map<String, Object>>() {}; |
| | | |
| | | /** |
| | | * æ ¹æ®PlcAddressé
ç½®ååæ®µå称读åPLCæ°æ® |
| | |
| | | |
| | | return resultMap; |
| | | } catch (Exception e) { |
| | | log.error("读åPLCæ°æ®å¤±è´¥ï¼è¯·æ£æ¥ï¼1.PLC IPå°åæ¯å¦æ£ç¡®[{}] 2.PLCè®¾å¤æ¯å¦å¨çº¿ 3.ç½ç»è¿æ¥æ¯å¦æ£å¸¸ï¼module: {}, 详ç»é误: {}", |
| | | config.getPlcIp(), config.getModule(), e.getMessage(), e); |
| | | log.error("读åPLCæ°æ®å¤±è´¥ï¼è¯·æ£æ¥ï¼1.PLC IPå°åæ¯å¦æ£ç¡®[{}] 2.PLCè®¾å¤æ¯å¦å¨çº¿ 3.ç½ç»è¿æ¥æ¯å¦æ£å¸¸ï¼è¯¦ç»é误: {}", |
| | | config.getPlcIp(), e.getMessage(), e); |
| | | return new HashMap<>(); |
| | | } |
| | | } |
| | |
| | | // åå
¥PLC |
| | | s7Serializer.write(parameters); |
| | | } catch (Exception e) { |
| | | log.error("åå
¥PLCæ°æ®å¤±è´¥ï¼è¯·æ£æ¥ï¼1.PLC IPå°åæ¯å¦æ£ç¡®[{}] 2.PLCè®¾å¤æ¯å¦å¨çº¿ 3.ç½ç»è¿æ¥æ¯å¦æ£å¸¸ï¼module: {}, 详ç»é误: {}", |
| | | config.getPlcIp(), config.getModule(), e.getMessage(), e); |
| | | log.error("åå
¥PLCæ°æ®å¤±è´¥ï¼è¯·æ£æ¥ï¼1.PLC IPå°åæ¯å¦æ£ç¡®[{}] 2.PLCè®¾å¤æ¯å¦å¨çº¿ 3.ç½ç»è¿æ¥æ¯å¦æ£å¸¸ï¼è¯¦ç»é误: {}", |
| | | config.getPlcIp(), e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | return parameters; |
| | | } |
| | | |
| | | /** |
| | | * ä»DeviceConfig䏿åå°åæ å°é
ç½® |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @return å°åæ å°JSONå符串 |
| | | */ |
| | | private String extractAddressMapping(DeviceConfig device) { |
| | | // configJson ç°å¨ä»
忾忮µå°åæ å°ï¼æ°ç»å½¢å¼ï¼ |
| | | Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper); |
| | | if (!configParams.isEmpty()) { |
| | | try { |
| | | return objectMapper.writeValueAsString(configParams); |
| | | } catch (Exception e) { |
| | | log.warn("åºååconfigJsonå°åæ å°å¤±è´¥, deviceId={}", device.getId(), e); |
| | | } |
| | | } |
| | | |
| | | // å
¶æ¬¡ä»extraParamsä¸è·åï¼å
¼å®¹æ§ç»æï¼ |
| | | Map<String, Object> extraParams = parseExtraParams(device); |
| | | Object addressMapping = extraParams.get("addressMapping"); |
| | | if (addressMapping != null) { |
| | | if (addressMapping instanceof String) { |
| | | return (String) addressMapping; |
| | | } else { |
| | | try { |
| | | return objectMapper.writeValueAsString(addressMapping); |
| | | } catch (Exception e) { |
| | | log.warn("åºååextraParams.addressMapping失败, deviceId={}", device.getId(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | throw new IllegalArgumentException("设å¤é
ç½®ä¸æªæ¾å°addressMapping, deviceId=" + device.getId()); |
| | | } |
| | | |
| | | /** |
| | | * ä»DeviceConfig䏿ådbArea |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @return dbArea |
| | | */ |
| | | private String extractDbArea(DeviceConfig device) { |
| | | // ä»extraParams.plcConfigä¸è·åï¼æ°ç»æï¼ |
| | | Map<String, Object> plcConfig = getPlcConfig(device); |
| | | Object dbArea = plcConfig.get("dbArea"); |
| | | if (dbArea != null) { |
| | | return String.valueOf(dbArea); |
| | | } |
| | | |
| | | // å
¼å®¹æ§ç»æï¼extraParamsæ ¹èç¹ |
| | | Map<String, Object> extraParams = parseExtraParams(device); |
| | | Object legacyDbArea = extraParams.get("dbArea"); |
| | | if (legacyDbArea != null) { |
| | | return String.valueOf(legacyDbArea); |
| | | } |
| | | |
| | | // é»è®¤å¼ |
| | | return "DB12"; |
| | | } |
| | | |
| | | /** |
| | | * ä»DeviceConfig䏿åbeginIndex |
| | | * |
| | | * @param device 设å¤é
ç½® |
| | | * @return beginIndex |
| | | */ |
| | | private int extractBeginIndex(DeviceConfig device) { |
| | | // ä»extraParams.plcConfigä¸è·å |
| | | Map<String, Object> plcConfig = getPlcConfig(device); |
| | | Object beginIndex = plcConfig.get("beginIndex"); |
| | | if (beginIndex != null) { |
| | | return parseInteger(beginIndex); |
| | | } |
| | | |
| | | // å
¼å®¹æ§ç»æï¼extraParamsæ ¹èç¹ |
| | | Map<String, Object> extraParams = parseExtraParams(device); |
| | | Object legacyBeginIndex = extraParams.get("beginIndex"); |
| | | if (legacyBeginIndex != null) { |
| | | return parseInteger(legacyBeginIndex); |
| | | } |
| | | |
| | | // é»è®¤å¼ |
| | | return 0; |
| | | } |
| | | |
| | | private Map<String, Object> parseExtraParams(DeviceConfig device) { |
| | | if (device.getExtraParams() == null || device.getExtraParams().trim().isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | try { |
| | | return objectMapper.readValue(device.getExtraParams(), MAP_TYPE); |
| | | } catch (Exception e) { |
| | | log.warn("è§£æè®¾å¤extraParams失败, deviceId={}", device.getId(), e); |
| | | return Collections.emptyMap(); |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | private Map<String, Object> getPlcConfig(DeviceConfig device) { |
| | | Map<String, Object> extraParams = parseExtraParams(device); |
| | | Object plcConfig = extraParams.get("plcConfig"); |
| | | if (plcConfig instanceof Map) { |
| | | return (Map<String, Object>) plcConfig; |
| | | } |
| | | if (plcConfig instanceof String) { |
| | | try { |
| | | return objectMapper.readValue((String) plcConfig, MAP_TYPE); |
| | | } catch (Exception e) { |
| | | log.warn("è§£æextraParams.plcConfig失败, deviceId={}", device.getId(), e); |
| | | } |
| | | } |
| | | return Collections.emptyMap(); |
| | | } |
| | | |
| | | private int parseInteger(Object value) { |
| | | if (value instanceof Number) { |
| | | return ((Number) value).intValue(); |
| | | } |
| | | try { |
| | | return Integer.parseInt(String.valueOf(value)); |
| | | } catch (NumberFormatException ex) { |
| | | log.warn("æ æ³è§£ææ´æ°å¼: {}", value); |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> readPlcData(DeviceConfig device, List<String> fieldNames, EnhancedS7Serializer s7Serializer) { |
| | | if (device == null) { |
| | | throw new IllegalArgumentException("设å¤é
ç½®ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | String addressMapping = extractAddressMapping(device); |
| | | if (addressMapping == null || addressMapping.isEmpty()) { |
| | | throw new IllegalArgumentException("设å¤é
ç½®ä¸addressMappingä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | try { |
| | | // è§£æaddressMapping JSONé
ç½® |
| | | JSONObject addressMappingObj = JSONObject.parseObject(addressMapping); |
| | | |
| | | // æå»ºS7Parameterå表 |
| | | String dbArea = extractDbArea(device); |
| | | List<S7Parameter> parameters = buildS7ParametersForDevice(device, dbArea, addressMappingObj, fieldNames); |
| | | |
| | | // ä»PLCè¯»åæ°æ® |
| | | List<S7Parameter> results = s7Serializer.read(parameters); |
| | | |
| | | // å°ç»æè½¬æ¢ä¸ºMap |
| | | Map<String, Object> resultMap = new HashMap<>(); |
| | | for (int i = 0; i < fieldNames.size() && i < results.size(); i++) { |
| | | String fieldName = fieldNames.get(i); |
| | | Object value = results.get(i).getValue(); |
| | | resultMap.put(fieldName, value); |
| | | } |
| | | |
| | | return resultMap; |
| | | } catch (Exception e) { |
| | | log.error("读åPLCæ°æ®å¤±è´¥ï¼è¯·æ£æ¥ï¼1.PLC IPå°åæ¯å¦æ£ç¡®[{}] 2.PLCè®¾å¤æ¯å¦å¨çº¿ 3.ç½ç»è¿æ¥æ¯å¦æ£å¸¸ï¼deviceId: {}, 详ç»é误: {}", |
| | | device.getPlcIp(), device.getId(), e.getMessage(), e); |
| | | return new HashMap<>(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void writePlcData(DeviceConfig device, Map<String, Object> dataMap, EnhancedS7Serializer s7Serializer) { |
| | | if (device == null) { |
| | | throw new IllegalArgumentException("设å¤é
ç½®ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | String addressMapping = extractAddressMapping(device); |
| | | if (addressMapping == null || addressMapping.isEmpty()) { |
| | | throw new IllegalArgumentException("设å¤é
ç½®ä¸addressMappingä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | try { |
| | | // è§£æaddressMapping JSONé
ç½® |
| | | JSONObject addressMappingObj = JSONObject.parseObject(addressMapping); |
| | | |
| | | // æå»ºS7Parameterå表ï¼å¹¶å¡«å
å¼ |
| | | String dbArea = extractDbArea(device); |
| | | List<S7Parameter> parameters = buildS7ParametersWithValuesForDevice(device, dbArea, addressMappingObj, dataMap); |
| | | |
| | | // åå
¥PLC |
| | | s7Serializer.write(parameters); |
| | | } catch (Exception e) { |
| | | log.error("åå
¥PLCæ°æ®å¤±è´¥ï¼è¯·æ£æ¥ï¼1.PLC IPå°åæ¯å¦æ£ç¡®[{}] 2.PLCè®¾å¤æ¯å¦å¨çº¿ 3.ç½ç»è¿æ¥æ¯å¦æ£å¸¸ï¼deviceId: {}, 详ç»é误: {}", |
| | | device.getPlcIp(), device.getId(), e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> readAllPlcData(DeviceConfig device, EnhancedS7Serializer s7Serializer) { |
| | | if (device == null) { |
| | | throw new IllegalArgumentException("设å¤é
ç½®ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | String addressMapping = extractAddressMapping(device); |
| | | if (addressMapping == null || addressMapping.isEmpty()) { |
| | | throw new IllegalArgumentException("设å¤é
ç½®ä¸addressMappingä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | // è·åææå段å |
| | | JSONObject addressMappingObj = JSONObject.parseObject(addressMapping); |
| | | List<String> allFields = new ArrayList<>(addressMappingObj.keySet()); |
| | | |
| | | // è¯»åææåæ®µ |
| | | return readPlcData(device, allFields, s7Serializer); |
| | | } |
| | | |
| | | @Override |
| | | public Object readPlcField(DeviceConfig device, String fieldName, EnhancedS7Serializer s7Serializer) { |
| | | List<String> fields = new ArrayList<>(); |
| | | fields.add(fieldName); |
| | | |
| | | Map<String, Object> result = readPlcData(device, fields, s7Serializer); |
| | | return result.get(fieldName); |
| | | } |
| | | |
| | | @Override |
| | | public void writePlcField(DeviceConfig device, String fieldName, Object value, EnhancedS7Serializer s7Serializer) { |
| | | Map<String, Object> dataMap = new HashMap<>(); |
| | | dataMap.put(fieldName, value); |
| | | |
| | | writePlcData(device, dataMap, s7Serializer); |
| | | } |
| | | |
| | | /** |
| | | * æå»ºS7Parameterå表ï¼ä¸å
å«å¼ï¼- åºäºDeviceConfig |
| | | */ |
| | | private List<S7Parameter> buildS7ParametersForDevice(DeviceConfig device, String dbArea, JSONObject addressMapping, List<String> fieldNames) { |
| | | List<S7Parameter> parameters = new ArrayList<>(); |
| | | |
| | | for (String fieldName : fieldNames) { |
| | | if (!addressMapping.containsKey(fieldName)) { |
| | | log.warn("åæ®µ {} å¨addressMappingä¸ä¸åå¨ï¼è·³è¿", fieldName); |
| | | continue; |
| | | } |
| | | |
| | | // è·ååæ®µçåç§»å°å |
| | | int offset = addressMapping.getInteger(fieldName); |
| | | |
| | | // æå»ºå®æ´å°åï¼dbArea + offsetï¼å¦ï¼DB12.2ï¼ |
| | | String fullAddress = dbArea + "." + offset; |
| | | |
| | | // å建S7Parameterï¼é»è®¤ä½¿ç¨UINT16ç±»åï¼16使 ç¬¦å·æ´æ°ï¼ |
| | | S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); |
| | | parameters.add(parameter); |
| | | } |
| | | |
| | | return parameters; |
| | | } |
| | | |
| | | /** |
| | | * æå»ºS7Parameterå表ï¼å
å«å¼ï¼- åºäºDeviceConfig |
| | | */ |
| | | private List<S7Parameter> buildS7ParametersWithValuesForDevice(DeviceConfig device, String dbArea, JSONObject addressMapping, Map<String, Object> dataMap) { |
| | | List<S7Parameter> parameters = new ArrayList<>(); |
| | | |
| | | for (Map.Entry<String, Object> entry : dataMap.entrySet()) { |
| | | String fieldName = entry.getKey(); |
| | | Object value = entry.getValue(); |
| | | |
| | | if (!addressMapping.containsKey(fieldName)) { |
| | | log.warn("åæ®µ {} å¨addressMappingä¸ä¸åå¨ï¼è·³è¿", fieldName); |
| | | continue; |
| | | } |
| | | |
| | | // è·ååæ®µçåç§»å°å |
| | | int offset = addressMapping.getInteger(fieldName); |
| | | |
| | | // æå»ºå®æ´å°å |
| | | String fullAddress = dbArea + "." + offset; |
| | | |
| | | // å建S7Parameterï¼è®¾ç½®å¼ |
| | | S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); |
| | | parameter.setValue(value); |
| | | parameters.add(parameter); |
| | | } |
| | | |
| | | return parameters; |
| | | } |
| | | } |
| | |
| | | |
| | | import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType; |
| | | import com.github.xingshuangs.iot.protocol.s7.service.S7PLC; |
| | | import com.mes.entity.PlcBaseData; |
| | | import com.mes.entity.PlcAddress; |
| | | import com.mes.entity.PlcBaseData; |
| | | import com.mes.s7.enhanced.EnhancedS7Serializer; |
| | | import com.mes.service.PlcAddressService; |
| | | import com.mes.service.IPlcTestWriteService; |
| | | import com.mes.service.PlcTestWriteService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class PlcTestWriteServiceImpl implements PlcTestWriteService { |
| | | public class PlcTestWriteServiceImpl extends PlcTestWriteService { |
| | | |
| | | @Resource |
| | | private PlcAddressService plcAddressService; |
| New file |
| | |
| | | package com.mes.task.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.mes.task.dto.MultiDeviceTaskQuery; |
| | | import com.mes.task.dto.MultiDeviceTaskRequest; |
| | | import com.mes.task.entity.MultiDeviceTask; |
| | | import com.mes.task.entity.TaskStepDetail; |
| | | import com.mes.task.service.MultiDeviceTaskService; |
| | | import com.mes.vo.Result; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.validation.Valid; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡æ§å¶å¨ |
| | | */ |
| | | @RestController |
| | | @RequestMapping("device/task") |
| | | @Api(tags = "å¤è®¾å¤ä»»å¡ç®¡ç") |
| | | @Validated |
| | | @RequiredArgsConstructor |
| | | public class MultiDeviceTaskController { |
| | | |
| | | private final MultiDeviceTaskService multiDeviceTaskService; |
| | | |
| | | @PostMapping("/start") |
| | | @ApiOperation("å¯å¨å¤è®¾å¤èåæµè¯ä»»å¡") |
| | | public Result<MultiDeviceTask> startTask(@Valid @RequestBody MultiDeviceTaskRequest request) { |
| | | return Result.success(multiDeviceTaskService.startTask(request)); |
| | | } |
| | | |
| | | @PostMapping("/list") |
| | | @ApiOperation("å页æ¥è¯¢ä»»å¡å表") |
| | | public Result<Page<MultiDeviceTask>> listTasks(@RequestBody(required = false) MultiDeviceTaskQuery query) { |
| | | MultiDeviceTaskQuery finalQuery = query != null ? query : new MultiDeviceTaskQuery(); |
| | | return Result.success(multiDeviceTaskService.queryTasks(finalQuery)); |
| | | } |
| | | |
| | | @GetMapping("/{taskId}") |
| | | @ApiOperation("æ¥è¯¢ä»»å¡è¯¦æ
") |
| | | public Result<MultiDeviceTask> getTask(@PathVariable String taskId) { |
| | | return Result.success(multiDeviceTaskService.getTaskByTaskId(taskId)); |
| | | } |
| | | |
| | | @GetMapping("/{taskId}/steps") |
| | | @ApiOperation("æ¥è¯¢ä»»å¡æ¥éª¤è¯¦æ
") |
| | | public Result<List<TaskStepDetail>> getTaskSteps(@PathVariable String taskId) { |
| | | return Result.success(multiDeviceTaskService.getTaskSteps(taskId)); |
| | | } |
| | | |
| | | @PostMapping("/{taskId}/cancel") |
| | | @ApiOperation("åæ¶æ£å¨è¿è¡çä»»å¡") |
| | | public Result<Boolean> cancelTask(@PathVariable String taskId) { |
| | | return Result.success(multiDeviceTaskService.cancelTask(taskId)); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.dto; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡æ¥è¯¢æ¡ä»¶ |
| | | */ |
| | | @Data |
| | | @ApiModel(value = "MultiDeviceTaskQuery", description = "å¤è®¾å¤ä»»å¡å页æ¥è¯¢æ¡ä»¶") |
| | | public class MultiDeviceTaskQuery { |
| | | |
| | | @ApiModelProperty(value = "设å¤ç»ID", example = "1") |
| | | private Long groupId; |
| | | |
| | | @ApiModelProperty(value = "ä»»å¡ç¶æï¼PENDING/RUNNING/COMPLETED/FAILED/CANCELLEDï¼") |
| | | private String status; |
| | | |
| | | @ApiModelProperty(value = "页ç ï¼ä»1å¼å§", example = "1") |
| | | private Integer page = 1; |
| | | |
| | | @ApiModelProperty(value = "æ¯é¡µæ°é", example = "10") |
| | | private Integer size = 10; |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.dto; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import javax.validation.Valid; |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡å¯å¨è¯·æ± |
| | | */ |
| | | @Data |
| | | @ApiModel(value = "MultiDeviceTaskRequest", description = "å¤è®¾å¤èåæµè¯ä»»å¡å¯å¨è¯·æ±") |
| | | public class MultiDeviceTaskRequest { |
| | | |
| | | @ApiModelProperty(value = "设å¤ç»ID", example = "1", required = true) |
| | | @NotNull(message = "设å¤ç»IDä¸è½ä¸ºç©º") |
| | | private Long groupId; |
| | | |
| | | @ApiModelProperty(value = "任塿¾ç¤ºåç§°") |
| | | private String taskName; |
| | | |
| | | @ApiModelProperty(value = "触å人") |
| | | private String triggeredBy; |
| | | |
| | | @ApiModelProperty(value = "ä»»å¡åæ°", required = true) |
| | | @Valid |
| | | @NotNull(message = "ä»»å¡åæ°ä¸è½ä¸ºç©º") |
| | | private TaskParameters parameters; |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.dto; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import javax.validation.constraints.NotEmpty; |
| | | import java.io.Serializable; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡åæ° |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @ApiModel(value = "TaskParameters", description = "å¤è®¾å¤ä»»å¡æ§è¡åæ°") |
| | | public class TaskParameters implements Serializable { |
| | | |
| | | @ApiModelProperty(value = "ç»çIDå表ï¼ä¿ææ§è¡é¡ºåºï¼", required = true) |
| | | @NotEmpty(message = "ç»çIDå表ä¸è½ä¸ºç©º") |
| | | private List<String> glassIds; |
| | | |
| | | @ApiModelProperty(value = "ä¸å¤§è½¦ä½ç½®ç¼ç ") |
| | | private String positionCode; |
| | | |
| | | @ApiModelProperty(value = "ä¸å¤§è½¦ä½ç½®å¼") |
| | | private Integer positionValue; |
| | | |
| | | @ApiModelProperty(value = "大ççå 工类å") |
| | | private Integer processType; |
| | | |
| | | @ApiModelProperty(value = "ç»çåå¨ä½ç½®") |
| | | private Integer storagePosition; |
| | | |
| | | @ApiModelProperty(value = "æ§è¡é´é(毫ç§)") |
| | | private Integer executionInterval; |
| | | |
| | | @ApiModelProperty(value = "设å¤çº§å«åæ°è¦çï¼keyå¯ä»¥æ¯è®¾å¤ç±»åæè®¾å¤ç¼ç ") |
| | | private Map<String, Map<String, Object>> deviceOverrides; |
| | | |
| | | @ApiModelProperty(value = "é¢å¤éä¼ åæ°") |
| | | private Map<String, Object> extra; |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * å¤è®¾å¤èåæµè¯ä»»å¡å®ä½ |
| | | */ |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("multi_device_task") |
| | | @ApiModel(value = "MultiDeviceTask", description = "å¤è®¾å¤èåæµè¯ä»»å¡è®°å½") |
| | | public class MultiDeviceTask implements Serializable { |
| | | |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | @ApiModelProperty("èªå¢ä¸»é®") |
| | | private Long id; |
| | | |
| | | @TableField("task_id") |
| | | @ApiModelProperty("ä»»å¡å¯ä¸ç¼å·") |
| | | private String taskId; |
| | | |
| | | @TableField("group_id") |
| | | @ApiModelProperty("设å¤ç»IDï¼å符串ï¼") |
| | | private String groupId; |
| | | |
| | | @TableField("project_id") |
| | | @ApiModelProperty("æå±é¡¹ç®IDï¼å符串ï¼") |
| | | private String projectId; |
| | | |
| | | @TableField("status") |
| | | @ApiModelProperty("ä»»å¡ç¶æ") |
| | | private String status; |
| | | |
| | | @TableField("current_step") |
| | | @ApiModelProperty("å½åæ§è¡æ¥éª¤") |
| | | private Integer currentStep; |
| | | |
| | | @TableField("total_steps") |
| | | @ApiModelProperty("æ»æ¥éª¤æ°") |
| | | private Integer totalSteps; |
| | | |
| | | @TableField("start_time") |
| | | @ApiModelProperty("å¼å§æ¶é´") |
| | | private Date startTime; |
| | | |
| | | @TableField("end_time") |
| | | @ApiModelProperty("ç»ææ¶é´") |
| | | private Date endTime; |
| | | |
| | | @TableField("error_message") |
| | | @ApiModelProperty("é误信æ¯") |
| | | private String errorMessage; |
| | | |
| | | @TableField("result_data") |
| | | @ApiModelProperty("æ§è¡ç»ææ°æ®(JSON)") |
| | | private String resultData; |
| | | |
| | | @TableField(value = "created_time", fill = FieldFill.INSERT) |
| | | @ApiModelProperty("å建æ¶é´") |
| | | private Date createdTime; |
| | | |
| | | @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE) |
| | | @ApiModelProperty("æ´æ°æ¶é´") |
| | | private Date updatedTime; |
| | | |
| | | public enum Status { |
| | | PENDING, |
| | | RUNNING, |
| | | COMPLETED, |
| | | FAILED, |
| | | CANCELLED |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡æ¥éª¤å®ä½ |
| | | */ |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("task_step_detail") |
| | | @ApiModel(value = "TaskStepDetail", description = "å¤è®¾å¤ä»»å¡æ¥éª¤è¯¦æ
") |
| | | public class TaskStepDetail implements Serializable { |
| | | |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | @ApiModelProperty("主é®ID") |
| | | private Long id; |
| | | |
| | | @TableField("task_id") |
| | | @ApiModelProperty("å
³èä»»å¡ID") |
| | | private String taskId; |
| | | |
| | | @TableField("step_order") |
| | | @ApiModelProperty("æ¥éª¤é¡ºåº") |
| | | private Integer stepOrder; |
| | | |
| | | @TableField("device_id") |
| | | @ApiModelProperty("设å¤IDï¼å符串ï¼") |
| | | private String deviceId; |
| | | |
| | | @TableField("step_name") |
| | | @ApiModelProperty("æ¥éª¤åç§°") |
| | | private String stepName; |
| | | |
| | | @TableField("status") |
| | | @ApiModelProperty("æ¥éª¤ç¶æ") |
| | | private String status; |
| | | |
| | | @TableField("start_time") |
| | | @ApiModelProperty("å¼å§æ¶é´") |
| | | private Date startTime; |
| | | |
| | | @TableField("end_time") |
| | | @ApiModelProperty("ç»ææ¶é´") |
| | | private Date endTime; |
| | | |
| | | @TableField("duration_ms") |
| | | @ApiModelProperty("æ§è¡èæ¶ï¼æ¯«ç§ï¼") |
| | | private Long durationMs; |
| | | |
| | | @TableField("input_data") |
| | | @ApiModelProperty("è¾å
¥æ°æ®(JSON)") |
| | | private String inputData; |
| | | |
| | | @TableField("output_data") |
| | | @ApiModelProperty("è¾åºæ°æ®(JSON)") |
| | | private String outputData; |
| | | |
| | | @TableField("error_message") |
| | | @ApiModelProperty("é误信æ¯") |
| | | private String errorMessage; |
| | | |
| | | @TableField("retry_count") |
| | | @ApiModelProperty("éè¯æ¬¡æ°") |
| | | private Integer retryCount; |
| | | |
| | | @TableField(value = "created_time", fill = FieldFill.INSERT) |
| | | @ApiModelProperty("è®°å½å建æ¶é´") |
| | | private Date createdTime; |
| | | |
| | | public enum Status { |
| | | PENDING, |
| | | RUNNING, |
| | | COMPLETED, |
| | | FAILED, |
| | | SKIPPED |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.mes.task.entity.MultiDeviceTask; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡ Mapper |
| | | */ |
| | | @Mapper |
| | | public interface MultiDeviceTaskMapper extends BaseMapper<MultiDeviceTask> { |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.mes.task.entity.TaskStepDetail; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * 任塿¥éª¤ Mapper |
| | | */ |
| | | @Mapper |
| | | public interface TaskStepDetailMapper extends BaseMapper<TaskStepDetail> { |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.model; |
| | | |
| | | import com.mes.task.dto.TaskParameters; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡æ§è¡ä¸ä¸æ |
| | | */ |
| | | @Getter |
| | | public class TaskExecutionContext { |
| | | |
| | | private final TaskParameters parameters; |
| | | |
| | | @Setter |
| | | private List<String> loadedGlassIds; |
| | | |
| | | @Setter |
| | | private List<String> processedGlassIds; |
| | | |
| | | private final Map<String, Object> sharedData = new ConcurrentHashMap<>(); |
| | | |
| | | public TaskExecutionContext(TaskParameters parameters) { |
| | | if (parameters == null) { |
| | | this.parameters = new TaskParameters(); |
| | | } else { |
| | | this.parameters = parameters; |
| | | } |
| | | if (CollectionUtils.isEmpty(this.parameters.getGlassIds())) { |
| | | this.parameters.setGlassIds(new ArrayList<>()); |
| | | } |
| | | this.sharedData.put("initialGlassIds", new ArrayList<>(this.parameters.getGlassIds())); |
| | | } |
| | | |
| | | public Map<String, Object> getSharedData() { |
| | | return sharedData; |
| | | } |
| | | |
| | | public List<String> getSafeLoadedGlassIds() { |
| | | return loadedGlassIds == null ? Collections.emptyList() : loadedGlassIds; |
| | | } |
| | | |
| | | public List<String> getSafeProcessedGlassIds() { |
| | | return processedGlassIds == null ? Collections.emptyList() : processedGlassIds; |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.model; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 任塿§è¡ç»æ |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class TaskExecutionResult { |
| | | |
| | | private boolean success; |
| | | |
| | | private String message; |
| | | |
| | | private Map<String, Object> data; |
| | | |
| | | public static TaskExecutionResult success(Map<String, Object> payload) { |
| | | return TaskExecutionResult.builder() |
| | | .success(true) |
| | | .message("æ§è¡å®æ") |
| | | .data(payload != null ? payload : new HashMap<>()) |
| | | .build(); |
| | | } |
| | | |
| | | public static TaskExecutionResult failure(String message, Map<String, Object> payload) { |
| | | return TaskExecutionResult.builder() |
| | | .success(false) |
| | | .message(message) |
| | | .data(payload != null ? payload : new HashMap<>()) |
| | | .build(); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.mes.task.dto.MultiDeviceTaskQuery; |
| | | import com.mes.task.dto.MultiDeviceTaskRequest; |
| | | import com.mes.task.entity.MultiDeviceTask; |
| | | import com.mes.task.entity.TaskStepDetail; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡æå¡ |
| | | */ |
| | | public interface MultiDeviceTaskService extends IService<MultiDeviceTask> { |
| | | |
| | | /** |
| | | * å¯å¨å¤è®¾å¤æµè¯ä»»å¡ |
| | | */ |
| | | MultiDeviceTask startTask(MultiDeviceTaskRequest request); |
| | | |
| | | /** |
| | | * æ ¹æ®ä»»å¡ç¼å·è·åä»»å¡ |
| | | */ |
| | | MultiDeviceTask getTaskByTaskId(String taskId); |
| | | |
| | | /** |
| | | * æ¥è¯¢ä»»å¡æ¥éª¤ |
| | | */ |
| | | List<TaskStepDetail> getTaskSteps(String taskId); |
| | | |
| | | /** |
| | | * åæ¶ä»»å¡ |
| | | */ |
| | | boolean cancelTask(String taskId); |
| | | |
| | | /** |
| | | * å页æ¥è¯¢ä»»å¡ |
| | | */ |
| | | Page<MultiDeviceTask> queryTasks(MultiDeviceTaskQuery query); |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.entity.DeviceGroupConfig; |
| | | import com.mes.interaction.DeviceInteraction; |
| | | import com.mes.interaction.DeviceInteractionRegistry; |
| | | import com.mes.interaction.DeviceLogicHandler; |
| | | import com.mes.interaction.DeviceLogicHandlerFactory; |
| | | import com.mes.interaction.base.InteractionContext; |
| | | import com.mes.interaction.base.InteractionResult; |
| | | import com.mes.device.service.DeviceInteractionService; |
| | | import com.mes.task.dto.TaskParameters; |
| | | import com.mes.task.entity.MultiDeviceTask; |
| | | import com.mes.task.entity.TaskStepDetail; |
| | | import com.mes.task.mapper.MultiDeviceTaskMapper; |
| | | import com.mes.task.mapper.TaskStepDetailMapper; |
| | | import com.mes.task.model.TaskExecutionContext; |
| | | import com.mes.task.model.TaskExecutionResult; |
| | | import com.mes.device.vo.DevicePlcVO; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡æ§è¡å¼æ |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class TaskExecutionEngine { |
| | | |
| | | private static final Map<String, String> DEFAULT_OPERATIONS = new HashMap<>(); |
| | | |
| | | static { |
| | | DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.LOAD_VEHICLE, "feedGlass"); |
| | | DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.LARGE_GLASS, "processGlass"); |
| | | DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.GLASS_STORAGE, "storeGlass"); |
| | | } |
| | | |
| | | private final TaskStepDetailMapper taskStepDetailMapper; |
| | | private final MultiDeviceTaskMapper multiDeviceTaskMapper; |
| | | private final DeviceInteractionService deviceInteractionService; |
| | | private final DeviceInteractionRegistry interactionRegistry; |
| | | private final DeviceLogicHandlerFactory handlerFactory; |
| | | private final ObjectMapper objectMapper; |
| | | |
| | | public TaskExecutionResult execute(MultiDeviceTask task, |
| | | DeviceGroupConfig groupConfig, |
| | | List<DeviceConfig> devices, |
| | | TaskParameters parameters) { |
| | | |
| | | if (CollectionUtils.isEmpty(devices)) { |
| | | return TaskExecutionResult.failure("设å¤ç»æªé
置设å¤ï¼æ æ³æ§è¡ä»»å¡", Collections.emptyMap()); |
| | | } |
| | | |
| | | TaskExecutionContext context = new TaskExecutionContext(parameters); |
| | | task.setTotalSteps(devices.size()); |
| | | task.setStatus(MultiDeviceTask.Status.RUNNING.name()); |
| | | multiDeviceTaskMapper.updateById(task); |
| | | |
| | | List<Map<String, Object>> stepSummaries = new ArrayList<>(); |
| | | boolean success = true; |
| | | String failureMessage = null; |
| | | |
| | | for (int i = 0; i < devices.size(); i++) { |
| | | DeviceConfig device = devices.get(i); |
| | | int order = i + 1; |
| | | TaskStepDetail step = createStepRecord(task, device, order); |
| | | StepResult stepResult = executeStep(task, step, device, context); |
| | | stepSummaries.add(stepResult.toSummary()); |
| | | if (!stepResult.isSuccess()) { |
| | | success = false; |
| | | failureMessage = stepResult.getMessage(); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("steps", stepSummaries); |
| | | payload.put("groupId", groupConfig.getId()); |
| | | payload.put("deviceCount", devices.size()); |
| | | |
| | | if (success) { |
| | | return TaskExecutionResult.success(payload); |
| | | } |
| | | return TaskExecutionResult.failure(failureMessage != null ? failureMessage : "任塿§è¡å¤±è´¥", payload); |
| | | } |
| | | |
| | | private TaskStepDetail createStepRecord(MultiDeviceTask task, DeviceConfig device, int order) { |
| | | TaskStepDetail step = new TaskStepDetail(); |
| | | step.setTaskId(task.getTaskId()); |
| | | step.setStepOrder(order); |
| | | step.setDeviceId(String.valueOf(device.getId())); |
| | | step.setStepName(device.getDeviceName()); |
| | | step.setStatus(TaskStepDetail.Status.PENDING.name()); |
| | | step.setRetryCount(0); |
| | | taskStepDetailMapper.insert(step); |
| | | return step; |
| | | } |
| | | |
| | | private StepResult executeStep(MultiDeviceTask task, |
| | | TaskStepDetail step, |
| | | DeviceConfig device, |
| | | TaskExecutionContext context) { |
| | | Date startTime = new Date(); |
| | | step.setStartTime(startTime); |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | |
| | | DeviceInteraction deviceInteraction = interactionRegistry.getInteraction(device.getDeviceType()); |
| | | if (deviceInteraction != null) { |
| | | return executeInteractionStep(task, step, device, context, deviceInteraction); |
| | | } |
| | | |
| | | Map<String, Object> params = buildOperationParams(device, context); |
| | | step.setInputData(toJson(params)); |
| | | taskStepDetailMapper.updateById(step); |
| | | |
| | | String operation = determineOperation(device, params); |
| | | DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType()); |
| | | DevicePlcVO.OperationResult result; |
| | | |
| | | try { |
| | | if (handler == null) { |
| | | result = deviceInteractionService.executeOperation(device.getId(), operation, params); |
| | | } else { |
| | | result = handler.execute(device, operation, params); |
| | | } |
| | | |
| | | boolean opSuccess = Boolean.TRUE.equals(result.getSuccess()); |
| | | updateStepAfterOperation(step, result, opSuccess); |
| | | updateTaskProgress(task, step.getStepOrder(), opSuccess); |
| | | |
| | | if (opSuccess) { |
| | | updateContextAfterSuccess(device, context, params); |
| | | return StepResult.success(device.getDeviceName(), result.getMessage()); |
| | | } |
| | | return StepResult.failure(device.getDeviceName(), result.getMessage()); |
| | | } catch (Exception e) { |
| | | log.error("è®¾å¤æä½å¼å¸¸, deviceId={}, operation={}", device.getId(), operation, e); |
| | | step.setStatus(TaskStepDetail.Status.FAILED.name()); |
| | | step.setErrorMessage(e.getMessage()); |
| | | step.setEndTime(new Date()); |
| | | step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); |
| | | taskStepDetailMapper.updateById(step); |
| | | updateTaskProgress(task, step.getStepOrder(), false); |
| | | return StepResult.failure(device.getDeviceName(), e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private StepResult executeInteractionStep(MultiDeviceTask task, |
| | | TaskStepDetail step, |
| | | DeviceConfig device, |
| | | TaskExecutionContext context, |
| | | DeviceInteraction deviceInteraction) { |
| | | try { |
| | | InteractionContext interactionContext = new InteractionContext(device, context); |
| | | step.setInputData(toJson(context.getParameters())); |
| | | InteractionResult interactionResult = deviceInteraction.execute(interactionContext); |
| | | boolean success = interactionResult != null && interactionResult.isSuccess(); |
| | | updateStepAfterInteraction(step, interactionResult); |
| | | updateTaskProgress(task, step.getStepOrder(), success); |
| | | |
| | | if (success) { |
| | | return StepResult.success(device.getDeviceName(), interactionResult.getMessage()); |
| | | } |
| | | String message = interactionResult != null ? interactionResult.getMessage() : "äº¤äºæ§è¡å¤±è´¥"; |
| | | return StepResult.failure(device.getDeviceName(), message); |
| | | } catch (Exception e) { |
| | | log.error("äº¤äºæ§è¡å¼å¸¸, deviceId={}", device.getId(), e); |
| | | step.setStatus(TaskStepDetail.Status.FAILED.name()); |
| | | step.setErrorMessage(e.getMessage()); |
| | | step.setEndTime(new Date()); |
| | | step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); |
| | | taskStepDetailMapper.updateById(step); |
| | | updateTaskProgress(task, step.getStepOrder(), false); |
| | | return StepResult.failure(device.getDeviceName(), e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void updateStepAfterOperation(TaskStepDetail step, |
| | | DevicePlcVO.OperationResult result, |
| | | boolean success) { |
| | | step.setEndTime(new Date()); |
| | | if (step.getStartTime() != null) { |
| | | step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); |
| | | } |
| | | step.setStatus(success ? TaskStepDetail.Status.COMPLETED.name() : TaskStepDetail.Status.FAILED.name()); |
| | | step.setErrorMessage(success ? null : result.getMessage()); |
| | | step.setOutputData(toJson(result)); |
| | | taskStepDetailMapper.updateById(step); |
| | | } |
| | | |
| | | private void updateStepAfterInteraction(TaskStepDetail step, |
| | | InteractionResult result) { |
| | | step.setEndTime(new Date()); |
| | | if (step.getStartTime() != null) { |
| | | step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); |
| | | } |
| | | boolean success = result != null && result.isSuccess(); |
| | | step.setStatus(success ? TaskStepDetail.Status.COMPLETED.name() : TaskStepDetail.Status.FAILED.name()); |
| | | step.setErrorMessage(success ? null : (result != null ? result.getMessage() : "äº¤äºæ§è¡å¤±è´¥")); |
| | | step.setOutputData(result != null ? toJson(result.getData()) : "{}"); |
| | | taskStepDetailMapper.updateById(step); |
| | | } |
| | | |
| | | private void updateTaskProgress(MultiDeviceTask task, int currentStep, boolean success) { |
| | | task.setCurrentStep(currentStep); |
| | | if (!success) { |
| | | task.setStatus(MultiDeviceTask.Status.FAILED.name()); |
| | | } |
| | | LambdaUpdateWrapper<MultiDeviceTask> update = Wrappers.<MultiDeviceTask>lambdaUpdate() |
| | | .eq(MultiDeviceTask::getId, task.getId()) |
| | | .set(MultiDeviceTask::getCurrentStep, currentStep); |
| | | if (!success) { |
| | | update.set(MultiDeviceTask::getStatus, MultiDeviceTask.Status.FAILED.name()); |
| | | } |
| | | multiDeviceTaskMapper.update(null, update); |
| | | } |
| | | |
| | | private String determineOperation(DeviceConfig device, Map<String, Object> params) { |
| | | if (params != null && params.containsKey("operation")) { |
| | | Object op = params.get("operation"); |
| | | if (op != null) { |
| | | return String.valueOf(op); |
| | | } |
| | | } |
| | | return DEFAULT_OPERATIONS.getOrDefault(device.getDeviceType(), "feedGlass"); |
| | | } |
| | | |
| | | private Map<String, Object> buildOperationParams(DeviceConfig device, TaskExecutionContext context) { |
| | | Map<String, Object> params = new HashMap<>(); |
| | | TaskParameters taskParams = context.getParameters(); |
| | | |
| | | switch (device.getDeviceType()) { |
| | | case DeviceConfig.DeviceType.LOAD_VEHICLE: |
| | | params.put("glassIds", new ArrayList<>(taskParams.getGlassIds())); |
| | | if (StringUtils.hasText(taskParams.getPositionCode())) { |
| | | params.put("positionCode", taskParams.getPositionCode()); |
| | | } |
| | | if (taskParams.getPositionValue() != null) { |
| | | params.put("positionValue", taskParams.getPositionValue()); |
| | | } |
| | | params.put("triggerRequest", true); |
| | | break; |
| | | case DeviceConfig.DeviceType.LARGE_GLASS: |
| | | List<String> source = context.getSafeLoadedGlassIds(); |
| | | if (CollectionUtils.isEmpty(source)) { |
| | | source = taskParams.getGlassIds(); |
| | | } |
| | | if (!CollectionUtils.isEmpty(source)) { |
| | | params.put("glassId", source.get(0)); |
| | | params.put("glassIds", new ArrayList<>(source)); |
| | | } |
| | | params.put("processType", taskParams.getProcessType() != null ? taskParams.getProcessType() : 1); |
| | | params.put("triggerRequest", true); |
| | | break; |
| | | case DeviceConfig.DeviceType.GLASS_STORAGE: |
| | | List<String> processed = context.getSafeProcessedGlassIds(); |
| | | if (CollectionUtils.isEmpty(processed)) { |
| | | processed = context.getSafeLoadedGlassIds(); |
| | | } |
| | | if (!CollectionUtils.isEmpty(processed)) { |
| | | params.put("glassId", processed.get(0)); |
| | | params.put("glassIds", new ArrayList<>(processed)); |
| | | } |
| | | if (taskParams.getStoragePosition() != null) { |
| | | params.put("storagePosition", taskParams.getStoragePosition()); |
| | | } |
| | | params.put("triggerRequest", true); |
| | | break; |
| | | default: |
| | | if (!CollectionUtils.isEmpty(taskParams.getExtra())) { |
| | | params.putAll(taskParams.getExtra()); |
| | | } |
| | | } |
| | | |
| | | mergeOverrides(device, taskParams, params); |
| | | return params; |
| | | } |
| | | |
| | | private void mergeOverrides(DeviceConfig device, TaskParameters taskParameters, Map<String, Object> params) { |
| | | if (CollectionUtils.isEmpty(taskParameters.getDeviceOverrides())) { |
| | | return; |
| | | } |
| | | Map<String, Object> override = taskParameters.getDeviceOverrides().get(device.getDeviceType()); |
| | | if (override == null && StringUtils.hasText(device.getDeviceCode())) { |
| | | override = taskParameters.getDeviceOverrides().get(device.getDeviceCode()); |
| | | } |
| | | if (override != null) { |
| | | params.putAll(override); |
| | | } |
| | | } |
| | | |
| | | private void updateContextAfterSuccess(DeviceConfig device, |
| | | TaskExecutionContext context, |
| | | Map<String, Object> params) { |
| | | switch (device.getDeviceType()) { |
| | | case DeviceConfig.DeviceType.LOAD_VEHICLE: |
| | | context.setLoadedGlassIds(extractGlassIds(params)); |
| | | break; |
| | | case DeviceConfig.DeviceType.LARGE_GLASS: |
| | | context.setProcessedGlassIds(extractGlassIds(params)); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | private List<String> extractGlassIds(Map<String, Object> params) { |
| | | if (params == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Object glassIds = params.get("glassIds"); |
| | | if (glassIds instanceof List) { |
| | | @SuppressWarnings("unchecked") |
| | | List<String> cast = (List<String>) glassIds; |
| | | return new ArrayList<>(cast); |
| | | } |
| | | Object glassId = params.get("glassId"); |
| | | if (glassId != null) { |
| | | return Collections.singletonList(String.valueOf(glassId)); |
| | | } |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | private String toJson(Object value) { |
| | | try { |
| | | return objectMapper.writeValueAsString(value); |
| | | } catch (JsonProcessingException e) { |
| | | return "{}"; |
| | | } |
| | | } |
| | | |
| | | private static class StepResult { |
| | | private final boolean success; |
| | | private final String message; |
| | | private final String deviceName; |
| | | |
| | | private StepResult(boolean success, String message, String deviceName) { |
| | | this.success = success; |
| | | this.message = message; |
| | | this.deviceName = deviceName; |
| | | } |
| | | |
| | | public static StepResult success(String deviceName, String message) { |
| | | return new StepResult(true, message, deviceName); |
| | | } |
| | | |
| | | public static StepResult failure(String deviceName, String message) { |
| | | return new StepResult(false, message, deviceName); |
| | | } |
| | | |
| | | public boolean isSuccess() { |
| | | return success; |
| | | } |
| | | |
| | | public String getMessage() { |
| | | return message; |
| | | } |
| | | |
| | | public Map<String, Object> toSummary() { |
| | | Map<String, Object> summary = new HashMap<>(); |
| | | summary.put("deviceName", deviceName); |
| | | summary.put("success", success); |
| | | summary.put("message", message); |
| | | return summary; |
| | | } |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.mes.task.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.entity.DeviceGroupConfig; |
| | | import com.mes.device.mapper.DeviceGroupRelationMapper; |
| | | import com.mes.device.service.DeviceGroupConfigService; |
| | | import com.mes.task.dto.MultiDeviceTaskQuery; |
| | | import com.mes.task.dto.MultiDeviceTaskRequest; |
| | | import com.mes.task.dto.TaskParameters; |
| | | import com.mes.task.entity.MultiDeviceTask; |
| | | import com.mes.task.entity.TaskStepDetail; |
| | | import com.mes.task.mapper.MultiDeviceTaskMapper; |
| | | import com.mes.task.mapper.TaskStepDetailMapper; |
| | | import com.mes.task.model.TaskExecutionResult; |
| | | import com.mes.task.service.MultiDeviceTaskService; |
| | | import com.mes.task.service.TaskExecutionEngine; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | |
| | | /** |
| | | * å¤è®¾å¤ä»»å¡æå¡å®ç° |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class MultiDeviceTaskServiceImpl extends ServiceImpl<MultiDeviceTaskMapper, MultiDeviceTask> |
| | | implements MultiDeviceTaskService { |
| | | |
| | | private final DeviceGroupConfigService deviceGroupConfigService; |
| | | private final DeviceGroupRelationMapper deviceGroupRelationMapper; |
| | | private final TaskStepDetailMapper taskStepDetailMapper; |
| | | private final TaskExecutionEngine taskExecutionEngine; |
| | | private final ObjectMapper objectMapper; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MultiDeviceTask startTask(MultiDeviceTaskRequest request) { |
| | | DeviceGroupConfig groupConfig = deviceGroupConfigService.getDeviceGroupById(request.getGroupId()); |
| | | if (groupConfig == null) { |
| | | throw new IllegalArgumentException("设å¤ç»ä¸åå¨: " + request.getGroupId()); |
| | | } |
| | | if (groupConfig.getStatus() != DeviceGroupConfig.Status.ENABLED) { |
| | | throw new IllegalStateException("设å¤ç»æªå¯ç¨ï¼æ æ³æ§è¡ä»»å¡"); |
| | | } |
| | | |
| | | List<DeviceConfig> devices = deviceGroupRelationMapper.getOrderedDeviceConfigs(groupConfig.getId()); |
| | | if (CollectionUtils.isEmpty(devices)) { |
| | | throw new IllegalStateException("设å¤ç»æªé
置任ä½è®¾å¤ï¼æ æ³æ§è¡ä»»å¡"); |
| | | } |
| | | |
| | | TaskParameters parameters = request.getParameters(); |
| | | if (parameters == null || CollectionUtils.isEmpty(parameters.getGlassIds())) { |
| | | throw new IllegalArgumentException("è³å°éè¦é
ç½®ä¸æ¡ç»çID"); |
| | | } |
| | | |
| | | MultiDeviceTask task = new MultiDeviceTask(); |
| | | task.setTaskId(generateTaskId(groupConfig)); |
| | | task.setGroupId(String.valueOf(groupConfig.getId())); |
| | | task.setProjectId(String.valueOf(groupConfig.getProjectId())); |
| | | task.setStatus(MultiDeviceTask.Status.PENDING.name()); |
| | | task.setCurrentStep(0); |
| | | task.setTotalSteps(devices.size()); |
| | | task.setStartTime(new Date()); |
| | | save(task); |
| | | |
| | | try { |
| | | TaskExecutionResult result = taskExecutionEngine.execute(task, groupConfig, devices, parameters); |
| | | task.setStatus(result.isSuccess() ? MultiDeviceTask.Status.COMPLETED.name() : MultiDeviceTask.Status.FAILED.name()); |
| | | task.setErrorMessage(result.isSuccess() ? null : result.getMessage()); |
| | | task.setEndTime(new Date()); |
| | | task.setResultData(writeJson(result.getData())); |
| | | updateById(task); |
| | | return task; |
| | | } catch (Exception ex) { |
| | | log.error("å¤è®¾å¤ä»»å¡æ§è¡å¼å¸¸, taskId={}", task.getTaskId(), ex); |
| | | task.setStatus(MultiDeviceTask.Status.FAILED.name()); |
| | | task.setErrorMessage(ex.getMessage()); |
| | | task.setEndTime(new Date()); |
| | | updateById(task); |
| | | throw new RuntimeException("å¤è®¾å¤ä»»å¡æ§è¡å¤±è´¥: " + ex.getMessage(), ex); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public MultiDeviceTask getTaskByTaskId(String taskId) { |
| | | LambdaQueryWrapper<MultiDeviceTask> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(MultiDeviceTask::getTaskId, taskId); |
| | | return getOne(wrapper); |
| | | } |
| | | |
| | | @Override |
| | | public List<TaskStepDetail> getTaskSteps(String taskId) { |
| | | LambdaQueryWrapper<TaskStepDetail> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(TaskStepDetail::getTaskId, taskId); |
| | | wrapper.orderByAsc(TaskStepDetail::getStepOrder); |
| | | return taskStepDetailMapper.selectList(wrapper); |
| | | } |
| | | |
| | | @Override |
| | | public boolean cancelTask(String taskId) { |
| | | MultiDeviceTask task = getTaskByTaskId(taskId); |
| | | if (task == null) { |
| | | return false; |
| | | } |
| | | if (!MultiDeviceTask.Status.RUNNING.name().equals(task.getStatus())) { |
| | | return false; |
| | | } |
| | | task.setStatus(MultiDeviceTask.Status.CANCELLED.name()); |
| | | task.setEndTime(new Date()); |
| | | return updateById(task); |
| | | } |
| | | |
| | | @Override |
| | | public Page<MultiDeviceTask> queryTasks(MultiDeviceTaskQuery query) { |
| | | int page = query.getPage() != null && query.getPage() > 0 ? query.getPage() : 1; |
| | | int size = query.getSize() != null && query.getSize() > 0 ? query.getSize() : 10; |
| | | Page<MultiDeviceTask> pageParam = new Page<>(page, size); |
| | | |
| | | LambdaQueryWrapper<MultiDeviceTask> wrapper = new LambdaQueryWrapper<>(); |
| | | if (query.getGroupId() != null) { |
| | | wrapper.eq(MultiDeviceTask::getGroupId, String.valueOf(query.getGroupId())); |
| | | } |
| | | if (StringUtils.hasText(query.getStatus())) { |
| | | wrapper.eq(MultiDeviceTask::getStatus, query.getStatus().toUpperCase(Locale.ROOT)); |
| | | } |
| | | wrapper.orderByDesc(MultiDeviceTask::getCreatedTime); |
| | | return page(pageParam, wrapper); |
| | | } |
| | | |
| | | private String generateTaskId(DeviceGroupConfig groupConfig) { |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA); |
| | | return "TASK_" + groupConfig.getId() + "_" + sdf.format(new Date()); |
| | | } |
| | | |
| | | private String writeJson(Object data) { |
| | | if (data == null) { |
| | | return "{}"; |
| | | } |
| | | try { |
| | | return objectMapper.writeValueAsString(data); |
| | | } catch (JsonProcessingException e) { |
| | | return "{}"; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | strict: false #è®¾ç½®ä¸¥æ ¼æ¨¡å¼,é»è®¤falseä¸å¯å¨. å¯å¨å卿ªå¹é
尿宿°æ®æºæ¶ååæåºå¼å¸¸,ä¸å¯å¨ä¼ä½¿ç¨é»è®¤æ°æ®æº. |
| | | datasource: |
| | | northGlassMes: |
| | | url: jdbc:mysql://${ip}:${port}/mes_modular?serverTimezone=GMT%2b8 |
| | | url: jdbc:mysql://${ip}:${port}/mes_test?serverTimezone=GMT%2b8 |
| | | username: root |
| | | password: beibo.123/ |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | |
| | | /** |
| | | * å建设å¤ç»é
ç½® |
| | | */ |
| | | create(data) { |
| | | create(config) { |
| | | return request({ |
| | | url: '/api/plcSend/device/group/create', |
| | | method: 'post', |
| | | data |
| | | data: { |
| | | groupConfig: config |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * æ´æ°è®¾å¤ç»é
ç½® |
| | | */ |
| | | update(data) { |
| | | update(id, config) { |
| | | return request({ |
| | | url: '/api/plcSend/device/group/update', |
| | | method: 'post', |
| | | data |
| | | data: { |
| | | groupId: id, |
| | | groupConfig: config |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * å é¤è®¾å¤ç»é
ç½® |
| | | */ |
| | | delete(data) { |
| | | delete(id) { |
| | | return request({ |
| | | url: '/api/plcSend/device/group/delete', |
| | | method: 'post', |
| | | data |
| | | data: { groupId: id } |
| | | }) |
| | | }, |
| | | |
| | |
| | | /** |
| | | * å¯ç¨è®¾å¤ç» |
| | | */ |
| | | enable(data) { |
| | | enable(id) { |
| | | return request({ |
| | | url: '/api/plcSend/device/group/enable', |
| | | method: 'post', |
| | | data |
| | | data: { groupId: id } |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * ç¦ç¨è®¾å¤ç» |
| | | */ |
| | | disable(data) { |
| | | disable(id) { |
| | | return request({ |
| | | url: '/api/plcSend/device/group/disable', |
| | | method: 'post', |
| | | data |
| | | data: { groupId: id } |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * æ¹éå¯ç¨è®¾å¤ç» |
| | | */ |
| | | batchEnable(data) { |
| | | batchEnable(groupIds) { |
| | | return request({ |
| | | url: '/api/plcSend/device/group/batch-enable', |
| | | method: 'post', |
| | | data |
| | | data: { groupIds } |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * æ¹éç¦ç¨è®¾å¤ç» |
| | | */ |
| | | batchDisable(data) { |
| | | batchDisable(groupIds) { |
| | | return request({ |
| | | url: '/api/plcSend/device/group/batch-disable', |
| | | method: 'post', |
| | | data |
| | | data: { groupIds } |
| | | }) |
| | | }, |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | // 设å¤äº¤äºæä½API |
| | | export const deviceInteractionApi = { |
| | | /** |
| | | * æ§è¡è®¾å¤é»è¾æä½ |
| | | * @param {Object} data - { deviceId, operation, params } |
| | | */ |
| | | executeOperation(data) { |
| | | return request({ |
| | | url: '/api/plcSend/device/interaction/execute', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * ç»ç䏿åå
¥ |
| | | */ |
| | | feedGlass(data) { |
| | | return request({ |
| | | url: '/api/plcSend/device/interaction/glass-feed', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // ç»è®¡API |
| | | export const getDeviceStatistics = (data) => { |
| | | return request({ |
| | |
| | | deviceConfigApi, |
| | | deviceGroupApi, |
| | | devicePlcApi, |
| | | deviceInteractionApi, |
| | | getDeviceStatistics, |
| | | getDeviceGroupStatistics |
| | | } |
| New file |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | const BASE_URL = '/api/plcSend/device/task' |
| | | |
| | | export const multiDeviceTaskApi = { |
| | | /** |
| | | * å¯å¨å¤è®¾å¤ä»»å¡ |
| | | */ |
| | | startTask(data) { |
| | | return request({ |
| | | url: `${BASE_URL}/start`, |
| | | method: 'post', |
| | | data |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * æ¥è¯¢ä»»å¡å表 |
| | | */ |
| | | getTaskList(params) { |
| | | return request({ |
| | | url: `${BASE_URL}/list`, |
| | | method: 'post', |
| | | data: params |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * æ¥è¯¢ä»»å¡è¯¦æ
|
| | | */ |
| | | getTaskById(taskId) { |
| | | return request({ |
| | | url: `${BASE_URL}/${taskId}`, |
| | | method: 'get' |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * æ¥è¯¢ä»»å¡æ¥éª¤ |
| | | */ |
| | | getTaskSteps(taskId) { |
| | | return request({ |
| | | url: `${BASE_URL}/${taskId}/steps`, |
| | | method: 'get' |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * åæ¶ä»»å¡ |
| | | */ |
| | | cancelTask(taskId) { |
| | | return request({ |
| | | url: `${BASE_URL}/${taskId}/cancel`, |
| | | method: 'post' |
| | | }) |
| | | } |
| | | } |
| | | |
| | |
| | | name: 'plcTest', |
| | | component: () => import('../views/plcTest/Test.vue') |
| | | }, |
| | | { |
| | | path: '/plcTest/MultiDeviceWorkbench', |
| | | name: 'MultiDeviceWorkbench', |
| | | component: () => import('../views/plcTest/MultiDeviceWorkbench.vue') |
| | | }, |
| | | { |
| | | path: '/device/DeviceManagement', |
| | | name: 'DeviceManagement', |
| | | component: () => import('../views/device/DeviceManagement.vue') |
| | | } |
| | | ] |
| | | }, |
| | | |
| | |
| | | // export const WebSocketHost = "10.153.19.150"; |
| | | // export const WebSocketHost = "172.17.2.7"; |
| | | export const WebSocketHost = "10.153.19.213";//hxl |
| | | export const WebSocketHost = "10.153.19.49";//hxl |
| | | // export const WebSocketHost = "10.153.19.2";//zt |
| | | //export const WebSocketHost = "10.153.19.20";//wsx |
| | | // export const WebSocketHost = "127.0.0.1"; |
| | |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="é讯åè®®" prop="protocolType"> |
| | | <el-select v-model="deviceForm.protocolType" placeholder="éæ©é讯åè®®" style="width: 100%;"> |
| | | <el-select |
| | | v-model="deviceForm.protocolType" |
| | | placeholder="éæ©é讯åè®®" |
| | | style="width: 100%;" |
| | | @change="handleProtocolTypeChange" |
| | | > |
| | | <el-option label="S7 Communication" value="S7 Communication" /> |
| | | <el-option label="Modbus TCP" value="Modbus TCP" /> |
| | | <el-option label="OPC UA" value="OPC UA" /> |
| | | <el-option label="EtherNet/IP" value="EtherNet/IP" /> |
| | | <el-option label="Profinet" value="Profinet" /> |
| | | <el-option label="å
¶ä»" value="å
¶ä»" /> |
| | | </el-select> |
| | | <span class="form-tip">S7ç³»åPLCé常使ç¨S7 Communicationåè®®</span> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="è¶
æ¶æ¶é´(ç§)" prop="timeout"> |
| | |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 设å¤é»è¾é
ç½® --> |
| | | <el-card class="form-section" shadow="never" style="margin-top: 20px;" v-if="deviceForm.deviceType"> |
| | | <template #header> |
| | | <span class="section-title">设å¤é»è¾é
ç½®</span> |
| | | <span class="form-tip">æ ¹æ®è®¾å¤ç±»åé
ç½®ç¹å®çä¸å¡é»è¾åæ°</span> |
| | | </template> |
| | | |
| | | <!-- ä¸å¤§è½¦è®¾å¤é»è¾é
ç½® --> |
| | | <div v-if="deviceForm.deviceType === 'ä¸å¤§è½¦'"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="车è¾å®¹é"> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.vehicleCapacity" |
| | | :min="1" |
| | | :max="10000" |
| | | :step="100" |
| | | style="width: 100%;" |
| | | /> |
| | | <span class="form-tip">è½¦è¾æå¤§å®¹é</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»çé´é(ms)"> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.glassIntervalMs" |
| | | :min="100" |
| | | :max="10000" |
| | | :step="100" |
| | | style="width: 100%;" |
| | | /> |
| | | <span class="form-tip">ç»ç䏿é´éæ¶é´ï¼æ¯«ç§ï¼</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èªå¨ä¸æ"> |
| | | <el-switch v-model="deviceLogicParams.autoFeed" /> |
| | | <span class="form-tip">æ¯å¦èªå¨è§¦åä¸æè¯·æ±</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æå¤§éè¯æ¬¡æ°"> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.maxRetryCount" |
| | | :min="0" |
| | | :max="10" |
| | | :step="1" |
| | | style="width: 100%;" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="ä½ç½®æ å°"> |
| | | <div class="position-mapping"> |
| | | <div |
| | | v-for="(value, key, index) in deviceLogicParams.positionMapping" |
| | | :key="index" |
| | | class="mapping-item" |
| | | > |
| | | <el-input |
| | | v-model="mappingKeys[index]" |
| | | placeholder="ä½ç½®ä»£ç " |
| | | size="small" |
| | | style="width: 150px; margin-right: 10px;" |
| | | @input="updatePositionMapping(index, $event, value)" |
| | | /> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.positionMapping[mappingKeys[index] || key]" |
| | | :min="0" |
| | | :max="100" |
| | | size="small" |
| | | style="width: 120px; margin-right: 10px;" |
| | | /> |
| | | <el-button |
| | | type="danger" |
| | | size="small" |
| | | @click="removePositionMapping(key)" |
| | | > |
| | | å é¤ |
| | | </el-button> |
| | | </div> |
| | | <el-button type="primary" size="small" @click="addPositionMapping"> |
| | | æ·»å ä½ç½®æ å° |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 大çç设å¤é»è¾é
ç½® --> |
| | | <div v-if="deviceForm.deviceType === '大çç'"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»ç尺寸"> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.glassSize" |
| | | :min="100" |
| | | :max="5000" |
| | | :step="100" |
| | | style="width: 100%;" |
| | | /> |
| | | <span class="form-tip">ç»ç尺寸ï¼mmï¼</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¤çæ¶é´(ms)"> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.processingTime" |
| | | :min="1000" |
| | | :max="60000" |
| | | :step="1000" |
| | | style="width: 100%;" |
| | | /> |
| | | <span class="form-tip">ç»çå¤çæ¶é´ï¼æ¯«ç§ï¼</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èªå¨å¤ç"> |
| | | <el-switch v-model="deviceLogicParams.autoProcess" /> |
| | | <span class="form-tip">æ¯å¦èªå¨è§¦åå¤ç请æ±</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æå¤§éè¯æ¬¡æ°"> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.maxRetryCount" |
| | | :min="0" |
| | | :max="10" |
| | | :step="1" |
| | | style="width: 100%;" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- ç»çåå¨è®¾å¤é»è¾é
ç½® --> |
| | | <div v-if="deviceForm.deviceType === 'ç»çåå¨'"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åå¨å®¹é"> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.storageCapacity" |
| | | :min="1" |
| | | :max="1000" |
| | | :step="1" |
| | | style="width: 100%;" |
| | | /> |
| | | <span class="form-tip">æå¤§å卿°é</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å货模å¼"> |
| | | <el-select v-model="deviceLogicParams.retrievalMode" style="width: 100%;"> |
| | | <el-option label="å
è¿å
åº (FIFO)" value="FIFO" /> |
| | | <el-option label="åè¿å
åº (LIFO)" value="LIFO" /> |
| | | <el-option label="éæº (RANDOM)" value="RANDOM" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èªå¨åå¨"> |
| | | <el-switch v-model="deviceLogicParams.autoStore" /> |
| | | <span class="form-tip">æ¯å¦èªå¨è§¦ååå¨è¯·æ±</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èªå¨åè´§"> |
| | | <el-switch v-model="deviceLogicParams.autoRetrieve" /> |
| | | <span class="form-tip">æ¯å¦èªå¨è§¦åå货请æ±</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æå¤§éè¯æ¬¡æ°"> |
| | | <el-input-number |
| | | v-model="deviceLogicParams.maxRetryCount" |
| | | :min="0" |
| | | :max="10" |
| | | :step="1" |
| | | style="width: 100%;" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- æè¿°ä¿¡æ¯ --> |
| | | <el-card class="form-section" shadow="never" style="margin-top: 20px;"> |
| | | <template #header> |
| | |
| | | const saving = ref(false) |
| | | const testing = ref(false) |
| | | const testResult = ref(null) |
| | | |
| | | // 设å¤é»è¾åæ°ï¼æ ¹æ®è®¾å¤ç±»å卿æ¾ç¤ºï¼ |
| | | const deviceLogicParams = reactive({ |
| | | // ä¸å¤§è½¦åæ° |
| | | vehicleCapacity: 6000, |
| | | glassIntervalMs: 1000, |
| | | autoFeed: true, |
| | | maxRetryCount: 5, |
| | | positionMapping: {}, |
| | | // 大ççåæ° |
| | | glassSize: 2000, |
| | | processingTime: 5000, |
| | | autoProcess: true, |
| | | // ç»çåå¨åæ° |
| | | storageCapacity: 100, |
| | | retrievalMode: 'FIFO', |
| | | autoStore: true, |
| | | autoRetrieve: true |
| | | }) |
| | | |
| | | // ä½ç½®æ å°ç鮿°ç»ï¼ç¨äºv-forï¼ |
| | | const mappingKeys = ref([]) |
| | | |
| | | // 设å¤è¡¨åæ°æ® |
| | | const getDefaultForm = () => ({ |
| | |
| | | emit('update:modelValue', newVal) |
| | | }) |
| | | |
| | | // çå¬PLCç±»åååï¼èªå¨è®¾ç½®é讯åè®® |
| | | watch(() => deviceForm.plcType, (newPlcType) => { |
| | | // å¦æéæ©çæ¯S7ç³»åPLCï¼èªå¨è®¾ç½®é讯å议为S7 Communication |
| | | if (newPlcType && (newPlcType.startsWith('S') || newPlcType.includes('S7'))) { |
| | | if (!deviceForm.protocolType || deviceForm.protocolType === 'å
¶ä»') { |
| | | deviceForm.protocolType = 'S7 Communication' |
| | | } |
| | | } |
| | | }) |
| | | |
| | | // å¤çé讯åè®®åå |
| | | const handleProtocolTypeChange = (value) => { |
| | | // 妿鿩äºéS7åè®®ï¼ä½PLCç±»åæ¯S7ç³»åï¼ç»åºæç¤º |
| | | if (value && value !== 'S7 Communication' && deviceForm.plcType) { |
| | | const s7Types = ['S1200', 'S1500', 'S400', 'S300', 'S200', 'S200_SMART'] |
| | | if (s7Types.includes(deviceForm.plcType)) { |
| | | ElMessage.warning('S7ç³»åPLCé常使ç¨S7 Communicationåè®®ï¼è¯·ç¡®è®¤åè®®éæ©æ¯å¦æ£ç¡®') |
| | | } |
| | | } |
| | | } |
| | | |
| | | // æ¹æ³å®ä¹ |
| | | const parseJsonSafe = (str, defaultValue = null) => { |
| | | if (!str) return defaultValue |
| | |
| | | deviceForm.dbArea = plcConfig.dbArea || 'DB1' |
| | | deviceForm.beginIndex = plcConfig.beginIndex ?? 0 |
| | | deviceForm.autoModeInterval = plcConfig.autoModeInterval ?? 5000 |
| | | |
| | | // å è½½é
ç½®åæ°ï¼ä» configJsonï¼ |
| | | // å
¼å®¹ä¸¤ç§æ ¼å¼ï¼ |
| | | // 1. æ°ç»æ ¼å¼ï¼[{ paramKey, paramValue, description }] |
| | | // 2. å¯¹è±¡æ ¼å¼ï¼æ§æ ¼å¼ï¼ï¼{ fieldName: offset } - èªå¨è½¬æ¢ä¸ºæ°ç»æ ¼å¼ |
| | | loadConfigParams(data?.configJson) |
| | | |
| | | // å 载设å¤é»è¾åæ° |
| | | const deviceLogic = extraObj.deviceLogic || {} |
| | | loadDeviceLogicParams(deviceLogic, data?.deviceType) |
| | | } |
| | | |
| | | // å è½½é
ç½®åæ°ï¼å
¼å®¹æ§çå¯¹è±¡æ ¼å¼ï¼ |
| | | const loadConfigParams = (configJson) => { |
| | | if (!configJson) { |
| | | deviceForm.configParams = [] |
| | | return |
| | | } |
| | | |
| | | try { |
| | | const parsed = typeof configJson === 'string' ? JSON.parse(configJson) : configJson |
| | | |
| | | // å¦ææ¯æ°ç»æ ¼å¼ï¼ç´æ¥ä½¿ç¨ |
| | | if (Array.isArray(parsed)) { |
| | | deviceForm.configParams = parsed |
| | | } |
| | | // 妿æ¯å¯¹è±¡æ ¼å¼ï¼å段å â åç§»éï¼ï¼è½¬æ¢ä¸ºæ°ç»æ ¼å¼ |
| | | else if (typeof parsed === 'object' && parsed !== null) { |
| | | // åæ®µåå°ä¸ææè¿°çæ å° |
| | | const fieldDescriptionMap = { |
| | | 'plcRequest': 'PLC请æ±å', |
| | | 'inPosition': 'è¿çä½ç½®', |
| | | 'plcGlassId1': 'ç»çid1', |
| | | 'plcGlassId2': 'ç»çid2', |
| | | 'plcGlassId3': 'ç»çid3', |
| | | 'plcGlassId4': 'ç»çid4', |
| | | 'plcGlassId5': 'ç»çid5', |
| | | 'plcGlassId6': 'ç»çid6', |
| | | 'plcGlassCount': 'ç»çæ°é', |
| | | 'onlineState': 'èæºç¶æ', |
| | | 'plcReport': 'PLCæ±æ¥', |
| | | 'state1': 'ç¶æ1', |
| | | 'state2': 'ç¶æ2', |
| | | 'state3': 'ç¶æ3', |
| | | 'state4': 'ç¶æ4', |
| | | 'state5': 'ç¶æ5', |
| | | 'state6': 'ç¶æ6', |
| | | 'mesSend': 'MESåé', |
| | | 'mesConfirm': 'MES确认', |
| | | 'trainInfo': 'å车信æ¯', |
| | | 'start1': 'èµ·å§1', |
| | | 'start2': 'èµ·å§2', |
| | | 'start3': 'èµ·å§3', |
| | | 'start4': 'èµ·å§4', |
| | | 'start5': 'èµ·å§5', |
| | | 'start6': 'èµ·å§6', |
| | | 'target1': 'ç®æ 1', |
| | | 'target2': 'ç®æ 2', |
| | | 'target3': 'ç®æ 3', |
| | | 'target4': 'ç®æ 4', |
| | | 'target5': 'ç®æ 5', |
| | | 'target6': 'ç®æ 6', |
| | | 'mesWidth1': 'MES宽度1', |
| | | 'mesWidth2': 'MES宽度2', |
| | | 'mesWidth3': 'MES宽度3', |
| | | 'mesWidth4': 'MES宽度4', |
| | | 'mesWidth5': 'MES宽度5', |
| | | 'mesWidth6': 'MES宽度6', |
| | | 'mesHeight1': 'MESé«åº¦1', |
| | | 'mesHeight2': 'MESé«åº¦2', |
| | | 'mesHeight3': 'MESé«åº¦3', |
| | | 'mesHeight4': 'MESé«åº¦4', |
| | | 'mesHeight5': 'MESé«åº¦5', |
| | | 'mesHeight6': 'MESé«åº¦6', |
| | | 'mesThickness1': 'MESå度1', |
| | | 'mesThickness2': 'MESå度2', |
| | | 'mesThickness3': 'MESå度3', |
| | | 'mesThickness4': 'MESå度4', |
| | | 'mesThickness5': 'MESå度5', |
| | | 'mesThickness6': 'MESå度6', |
| | | 'edgeDistance1': 'è¾¹ç¼è·ç¦»1', |
| | | 'edgeDistance2': 'è¾¹ç¼è·ç¦»2', |
| | | 'edgeDistance3': 'è¾¹ç¼è·ç¦»3', |
| | | 'edgeDistance4': 'è¾¹ç¼è·ç¦»4', |
| | | 'edgeDistance5': 'è¾¹ç¼è·ç¦»5', |
| | | 'edgeDistance6': 'è¾¹ç¼è·ç¦»6', |
| | | 'targetEdgeDistance1': 'ç®æ è¾¹ç¼è·ç¦»1', |
| | | 'targetEdgeDistance2': 'ç®æ è¾¹ç¼è·ç¦»2', |
| | | 'targetEdgeDistance3': 'ç®æ è¾¹ç¼è·ç¦»3', |
| | | 'targetEdgeDistance4': 'ç®æ è¾¹ç¼è·ç¦»4', |
| | | 'targetEdgeDistance5': 'ç®æ è¾¹ç¼è·ç¦»5', |
| | | 'targetEdgeDistance6': 'ç®æ è¾¹ç¼è·ç¦»6', |
| | | 'alarmInfo': 'æ¥è¦ä¿¡æ¯' |
| | | } |
| | | |
| | | // 转æ¢ä¸ºæ°ç»æ ¼å¼ |
| | | deviceForm.configParams = Object.keys(parsed).map(fieldName => ({ |
| | | paramKey: fieldName, |
| | | paramValue: String(parsed[fieldName]), |
| | | description: fieldDescriptionMap[fieldName] || fieldName |
| | | })) |
| | | } else { |
| | | deviceForm.configParams = [] |
| | | } |
| | | } catch (error) { |
| | | console.warn('è§£æconfigJson失败', error) |
| | | deviceForm.configParams = [] |
| | | } |
| | | } |
| | | |
| | | // å 载设å¤é»è¾åæ° |
| | | const loadDeviceLogicParams = (deviceLogic, deviceType) => { |
| | | if (deviceType === 'ä¸å¤§è½¦') { |
| | | deviceLogicParams.vehicleCapacity = deviceLogic.vehicleCapacity ?? 6000 |
| | | deviceLogicParams.glassIntervalMs = deviceLogic.glassIntervalMs ?? 1000 |
| | | deviceLogicParams.autoFeed = deviceLogic.autoFeed ?? true |
| | | deviceLogicParams.maxRetryCount = deviceLogic.maxRetryCount ?? 5 |
| | | deviceLogicParams.positionMapping = deviceLogic.positionMapping || {} |
| | | mappingKeys.value = Object.keys(deviceLogicParams.positionMapping) |
| | | } else if (deviceType === '大çç') { |
| | | deviceLogicParams.glassSize = deviceLogic.glassSize ?? 2000 |
| | | deviceLogicParams.processingTime = deviceLogic.processingTime ?? 5000 |
| | | deviceLogicParams.autoProcess = deviceLogic.autoProcess ?? true |
| | | deviceLogicParams.maxRetryCount = deviceLogic.maxRetryCount ?? 3 |
| | | } else if (deviceType === 'ç»çåå¨') { |
| | | deviceLogicParams.storageCapacity = deviceLogic.storageCapacity ?? 100 |
| | | deviceLogicParams.retrievalMode = deviceLogic.retrievalMode || 'FIFO' |
| | | deviceLogicParams.autoStore = deviceLogic.autoStore ?? true |
| | | deviceLogicParams.autoRetrieve = deviceLogic.autoRetrieve ?? true |
| | | deviceLogicParams.maxRetryCount = deviceLogic.maxRetryCount ?? 3 |
| | | } |
| | | } |
| | | |
| | | // ä½ç½®æ å°ç¸å
³æ¹æ³ |
| | | const addPositionMapping = () => { |
| | | const newKey = `POS${Object.keys(deviceLogicParams.positionMapping).length + 1}` |
| | | deviceLogicParams.positionMapping[newKey] = 1 |
| | | mappingKeys.value.push(newKey) |
| | | } |
| | | |
| | | const removePositionMapping = (key) => { |
| | | delete deviceLogicParams.positionMapping[key] |
| | | mappingKeys.value = mappingKeys.value.filter(k => k !== key) |
| | | } |
| | | |
| | | const updatePositionMapping = (index, newKey, oldValue) => { |
| | | const oldKey = mappingKeys.value[index] |
| | | if (oldKey && oldKey !== newKey) { |
| | | delete deviceLogicParams.positionMapping[oldKey] |
| | | } |
| | | mappingKeys.value[index] = newKey |
| | | if (newKey) { |
| | | deviceLogicParams.positionMapping[newKey] = oldValue || 1 |
| | | } |
| | | } |
| | | |
| | | const resetForm = () => { |
| | | Object.assign(deviceForm, getDefaultForm()) |
| | | deviceFormRef.value?.clearValidate() |
| | | |
| | | // é置设å¤é»è¾åæ° |
| | | deviceLogicParams.vehicleCapacity = 6000 |
| | | deviceLogicParams.glassIntervalMs = 1000 |
| | | deviceLogicParams.autoFeed = true |
| | | deviceLogicParams.maxRetryCount = 5 |
| | | deviceLogicParams.positionMapping = {} |
| | | mappingKeys.value = [] |
| | | |
| | | deviceLogicParams.glassSize = 2000 |
| | | deviceLogicParams.processingTime = 5000 |
| | | deviceLogicParams.autoProcess = true |
| | | |
| | | deviceLogicParams.storageCapacity = 100 |
| | | deviceLogicParams.retrievalMode = 'FIFO' |
| | | deviceLogicParams.autoStore = true |
| | | deviceLogicParams.autoRetrieve = true |
| | | } |
| | | |
| | | const addConfigParam = () => { |
| | |
| | | plcType: deviceForm.plcType |
| | | } |
| | | |
| | | // ä¿å设å¤é»è¾åæ° |
| | | const deviceLogic = {} |
| | | if (deviceForm.deviceType === 'ä¸å¤§è½¦') { |
| | | deviceLogic.vehicleCapacity = deviceLogicParams.vehicleCapacity |
| | | deviceLogic.glassIntervalMs = deviceLogicParams.glassIntervalMs |
| | | deviceLogic.autoFeed = deviceLogicParams.autoFeed |
| | | deviceLogic.maxRetryCount = deviceLogicParams.maxRetryCount |
| | | deviceLogic.positionMapping = deviceLogicParams.positionMapping |
| | | } else if (deviceForm.deviceType === '大çç') { |
| | | deviceLogic.glassSize = deviceLogicParams.glassSize |
| | | deviceLogic.processingTime = deviceLogicParams.processingTime |
| | | deviceLogic.autoProcess = deviceLogicParams.autoProcess |
| | | deviceLogic.maxRetryCount = deviceLogicParams.maxRetryCount |
| | | } else if (deviceForm.deviceType === 'ç»çåå¨') { |
| | | deviceLogic.storageCapacity = deviceLogicParams.storageCapacity |
| | | deviceLogic.retrievalMode = deviceLogicParams.retrievalMode |
| | | deviceLogic.autoStore = deviceLogicParams.autoStore |
| | | deviceLogic.autoRetrieve = deviceLogicParams.autoRetrieve |
| | | deviceLogic.maxRetryCount = deviceLogicParams.maxRetryCount |
| | | } |
| | | |
| | | if (Object.keys(deviceLogic).length > 0) { |
| | | extraObj.deviceLogic = deviceLogic |
| | | } |
| | | |
| | | // æå»º configJsonï¼å° configParams æ°ç»è½¬æ¢ä¸º JSON å符串 |
| | | // configParams ç»æ: [{ paramKey: '', paramValue: '', description: '' }] |
| | | let configJsonValue = null |
| | | if (deviceForm.configParams && deviceForm.configParams.length > 0) { |
| | | // è¿æ»¤æç©ºåæ° |
| | | const validParams = deviceForm.configParams.filter( |
| | | param => param.paramKey && param.paramKey.trim() !== '' |
| | | ) |
| | | if (validParams.length > 0) { |
| | | configJsonValue = JSON.stringify(validParams) |
| | | } |
| | | } |
| | | |
| | | const saveData = { |
| | | deviceName: deviceForm.deviceName, |
| | | deviceCode: deviceForm.deviceCode, |
| | |
| | | isPrimary: deviceForm.isPrimary, |
| | | enabled: deviceForm.enabled, |
| | | description: deviceForm.description, |
| | | configJson: deviceForm.configParams.length > 0 |
| | | ? JSON.stringify(deviceForm.configParams) |
| | | : null, |
| | | configJson: configJsonValue, // ä¿åé
ç½®åæ°JSON |
| | | extraParams: JSON.stringify(extraObj) |
| | | } |
| | | |
| | |
| | | handleClose() |
| | | } catch (error) { |
| | | console.error('ä¿å设å¤é
置失败:', error) |
| | | // 妿æ¯è¡¨åéªè¯éè¯¯ï¼æ¾ç¤ºæ´è¯¦ç»çéè¯¯ä¿¡æ¯ |
| | | if (error && typeof error === 'object' && !error.response) { |
| | | const errorFields = Object.keys(error) |
| | | if (errorFields.length > 0) { |
| | | const firstError = error[errorFields[0]] |
| | | const errorMessage = Array.isArray(firstError) |
| | | ? firstError[0]?.message || firstError[0] |
| | | : firstError?.message || firstError |
| | | ElMessage.error(`表åéªè¯å¤±è´¥: ${errorMessage}`) |
| | | return |
| | | } |
| | | } |
| | | ElMessage.error(isEdit.value ? 'æ´æ°è®¾å¤é
置失败' : 'å建设å¤é
置失败') |
| | | } finally { |
| | | saving.value = false |
| | |
| | | :deep(.el-card__body) { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .position-mapping { |
| | | width: 100%; |
| | | } |
| | | |
| | | .mapping-item { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | padding: 12px; |
| | | border: 1px solid #ebeef5; |
| | | border-radius: 6px; |
| | | background-color: #fafafa; |
| | | } |
| | | </style> |
| | |
| | | customParams: form.customParams |
| | | } |
| | | |
| | | let response |
| | | if (isEdit.value) { |
| | | response = await deviceGroupApi.update(props.data.id, config) |
| | | } else { |
| | | response = await deviceGroupApi.create(config) |
| | | } |
| | | |
| | | if (response.success) { |
| | | const response = isEdit.value |
| | | ? await deviceGroupApi.update(props.data.id, config) |
| | | : await deviceGroupApi.create(config) |
| | | |
| | | const ok = response && (response.success || response.code === 200 || response.isSuccess) |
| | | if (ok) { |
| | | ElMessage.success(isEdit.value ? '设å¤ç»æ´æ°æå' : '设å¤ç»å建æå') |
| | | emit('success', isEdit.value ? 'update' : 'create') |
| | | handleClose() |
| | | } else { |
| | | ElMessage.error(response.message || (isEdit.value ? 'æ´æ°å¤±è´¥' : 'å建失败')) |
| | | ElMessage.error(response?.message || (isEdit.value ? 'æ´æ°å¤±è´¥' : 'å建失败')) |
| | | } |
| | | } catch (error) { |
| | | console.error('ä¿åé
置失败:', error) |
| | |
| | | try { |
| | | const groupId = row.id || row.groupId |
| | | if (row.enabled) { |
| | | await deviceGroupApi.enable({ groupId }) |
| | | await deviceGroupApi.enable(groupId) |
| | | ElMessage.success('设å¤ç»å¯ç¨æå') |
| | | } else { |
| | | await deviceGroupApi.disable({ groupId }) |
| | | await deviceGroupApi.disable(groupId) |
| | | ElMessage.success('设å¤ç»ç¦ç¨æå') |
| | | } |
| | | emit('refresh-statistics') |
| | |
| | | const batchEnable = async () => { |
| | | try { |
| | | const groupIds = selectedGroups.value.map(item => item.id || item.groupId) |
| | | await deviceGroupApi.batchEnable({ groupIds }) |
| | | await deviceGroupApi.batchEnable(groupIds) |
| | | ElMessage.success(`æåå¯ç¨ ${groupIds.length} 个设å¤ç»`) |
| | | clearSelection() |
| | | loadGroupList() |
| | |
| | | const batchDisable = async () => { |
| | | try { |
| | | const groupIds = selectedGroups.value.map(item => item.id || item.groupId) |
| | | await deviceGroupApi.batchDisable({ groupIds }) |
| | | await deviceGroupApi.batchDisable(groupIds) |
| | | ElMessage.success(`æåç¦ç¨ ${groupIds.length} 个设å¤ç»`) |
| | | clearSelection() |
| | | loadGroupList() |
| New file |
| | |
| | | <template> |
| | | <div class="multi-device-workbench"> |
| | | <div class="main-grid"> |
| | | <div class="left-panel"> |
| | | <GroupList @select="handleGroupSelect" /> |
| | | </div> |
| | | <div class="right-panel"> |
| | | <TaskOrchestration :group="selectedGroup" @task-started="refreshMonitor" /> |
| | | <ExecutionMonitor ref="monitorRef" :group-id="selectedGroupId" class="monitor-panel" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, ref } from 'vue' |
| | | import GroupList from './components/DeviceGroup/GroupList.vue' |
| | | import TaskOrchestration from './components/MultiDeviceTest/TaskOrchestration.vue' |
| | | import ExecutionMonitor from './components/MultiDeviceTest/ExecutionMonitor.vue' |
| | | |
| | | const selectedGroup = ref(null) |
| | | const monitorRef = ref(null) |
| | | |
| | | const selectedGroupId = computed(() => { |
| | | if (!selectedGroup.value) return null |
| | | return selectedGroup.value.id || selectedGroup.value.groupId |
| | | }) |
| | | |
| | | const handleGroupSelect = (group) => { |
| | | selectedGroup.value = group |
| | | } |
| | | |
| | | const refreshMonitor = () => { |
| | | monitorRef.value?.fetchTasks?.() |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .multi-device-workbench { |
| | | padding: 24px; |
| | | min-height: 100%; |
| | | background: linear-gradient(180deg, #f6f9ff 0%, #f4f6fb 100%); |
| | | } |
| | | |
| | | .main-grid { |
| | | display: grid; |
| | | grid-template-columns: 360px 1fr; |
| | | gap: 24px; |
| | | } |
| | | |
| | | .right-panel { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24px; |
| | | } |
| | | |
| | | .monitor-panel { |
| | | flex: 1; |
| | | } |
| | | |
| | | @media (max-width: 1200px) { |
| | | .main-grid { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | } |
| | | </style> |
| | | |
| New file |
| | |
| | | <template> |
| | | <div class="group-list-panel"> |
| | | <div class="panel-header"> |
| | | <div> |
| | | <h3>设å¤ç»å表</h3> |
| | | <p>éæ©ä¸ä¸ªè®¾å¤ç»è¿è¡ç¼ææµè¯</p> |
| | | </div> |
| | | <div class="actions"> |
| | | <el-input |
| | | v-model="filters.keyword" |
| | | placeholder="æç´¢è®¾å¤ç»åç§°/ç¼ç " |
| | | clearable |
| | | @clear="fetchGroups" |
| | | @keyup.enter="fetchGroups" |
| | | class="search-input" |
| | | > |
| | | <template #prefix> |
| | | <el-icon><Search /></el-icon> |
| | | </template> |
| | | </el-input> |
| | | <el-button :loading="loading" @click="fetchGroups"> |
| | | <el-icon><Refresh /></el-icon> |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="groups" |
| | | height="320" |
| | | stripe |
| | | class="group-table" |
| | | @row-click="handleRowClick" |
| | | > |
| | | <el-table-column prop="groupName" label="设å¤ç»" min-width="160" /> |
| | | <el-table-column prop="groupCode" label="ç¼ç " min-width="120" /> |
| | | <el-table-column prop="status" label="ç¶æ" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="formatStatus(row.status).type">{{ formatStatus(row.status).label }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="deviceCount" label="è®¾å¤æ°é" width="100" /> |
| | | <el-table-column label="æåæ´æ°æ¶é´" min-width="160"> |
| | | <template #default="{ row }"> |
| | | {{ row.updatedTime || row.createTime || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, reactive, ref } from 'vue' |
| | | import { Refresh, Search } from '@element-plus/icons-vue' |
| | | import { deviceGroupApi } from '@/api/device/deviceManagement' |
| | | |
| | | const emit = defineEmits(['select']) |
| | | |
| | | const loading = ref(false) |
| | | const groups = ref([]) |
| | | const filters = reactive({ |
| | | keyword: '' |
| | | }) |
| | | |
| | | const fetchGroups = async () => { |
| | | try { |
| | | loading.value = true |
| | | const payload = { |
| | | page: 1, |
| | | size: 20, |
| | | keyword: filters.keyword |
| | | } |
| | | const { data } = await deviceGroupApi.getList(payload) |
| | | const records = data?.records || data?.data || data || [] |
| | | groups.value = Array.isArray(records) ? records : [] |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | const handleRowClick = (row) => { |
| | | emit('select', row) |
| | | } |
| | | |
| | | const formatStatus = (status) => { |
| | | const value = typeof status === 'number' ? status : String(status || '').toUpperCase() |
| | | if (value === 1 || value === 'å¯ç¨' || value === 'ENABLED') { |
| | | return { label: 'å¯ç¨', type: 'success' } |
| | | } |
| | | if (value === 0 || value === 'åç¨' || value === 'DISABLED') { |
| | | return { label: 'åç¨', type: 'info' } |
| | | } |
| | | if (value === 2 || value === 'ç»´æ¤ä¸' || value === 'MAINTENANCE') { |
| | | return { label: 'ç»´æ¤', type: 'warning' } |
| | | } |
| | | return { label: status || 'æªç¥', type: 'default' } |
| | | } |
| | | |
| | | onMounted(fetchGroups) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .group-list-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | box-shadow: 0 8px 32px rgba(15, 18, 63, 0.08); |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .panel-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .panel-header h3 { |
| | | margin: 0; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .panel-header p { |
| | | margin: 2px 0 0; |
| | | color: #909399; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .search-input { |
| | | width: 240px; |
| | | } |
| | | |
| | | .group-table { |
| | | flex: 1; |
| | | } |
| | | </style> |
| | | |
| New file |
| | |
| | | <template> |
| | | <div class="execution-monitor"> |
| | | <div class="panel-header"> |
| | | <div> |
| | | <h3>任塿§è¡çæ§</h3> |
| | | <p>宿¶æ¥çææ°çå¤è®¾å¤ä»»å¡</p> |
| | | </div> |
| | | <el-button :loading="loading" @click="fetchTasks"> |
| | | <el-icon><Refresh /></el-icon> |
| | | å·æ° |
| | | </el-button> |
| | | </div> |
| | | |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="tasks" |
| | | height="300" |
| | | stripe |
| | | @row-click="handleRowClick" |
| | | > |
| | | <el-table-column prop="taskId" label="ä»»å¡ç¼å·" min-width="160" /> |
| | | <el-table-column prop="groupId" label="设å¤ç»ID" width="120" /> |
| | | <el-table-column prop="status" label="ç¶æ" width="120"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="statusType(row.status)">{{ row.status }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="currentStep" label="è¿åº¦" width="120"> |
| | | <template #default="{ row }"> |
| | | {{ row.currentStep || 0 }} / {{ row.totalSteps || 0 }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¼å§æ¶é´" min-width="160" prop="startTime" /> |
| | | <el-table-column label="ç»ææ¶é´" min-width="160" prop="endTime" /> |
| | | </el-table> |
| | | |
| | | <el-drawer v-model="drawerVisible" size="40%" title="任塿¥éª¤è¯¦æ
"> |
| | | <el-timeline v-loading="stepsLoading" :reverse="false"> |
| | | <el-timeline-item |
| | | v-for="step in steps" |
| | | :key="step.id" |
| | | :timestamp="step.startTime || '-'" |
| | | :type="step.status === 'COMPLETED' ? 'success' : step.status === 'FAILED' ? 'danger' : 'primary'" |
| | | > |
| | | <div class="step-title">{{ step.stepName }}</div> |
| | | <div class="step-desc">ç¶æï¼{{ step.status }}</div> |
| | | <div class="step-desc">èæ¶ï¼{{ formatDuration(step.durationMs) }}</div> |
| | | <div class="step-desc" v-if="step.errorMessage"> |
| | | é误ï¼{{ step.errorMessage }} |
| | | </div> |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | </el-drawer> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, watch } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { Refresh } from '@element-plus/icons-vue' |
| | | import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask' |
| | | |
| | | const props = defineProps({ |
| | | groupId: { |
| | | type: [String, Number], |
| | | default: null |
| | | } |
| | | }) |
| | | |
| | | const loading = ref(false) |
| | | const tasks = ref([]) |
| | | const drawerVisible = ref(false) |
| | | const stepsLoading = ref(false) |
| | | const steps = ref([]) |
| | | const currentTaskId = ref(null) |
| | | |
| | | const fetchTasks = async () => { |
| | | try { |
| | | loading.value = true |
| | | const { data } = await multiDeviceTaskApi.getTaskList({ |
| | | groupId: props.groupId, |
| | | page: 1, |
| | | size: 10 |
| | | }) |
| | | tasks.value = data?.records || data?.data || data || [] |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || 'å 载任å¡å表失败') |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | const handleRowClick = async (row) => { |
| | | currentTaskId.value = row.taskId |
| | | drawerVisible.value = true |
| | | stepsLoading.value = true |
| | | try { |
| | | const { data } = await multiDeviceTaskApi.getTaskSteps(row.taskId) |
| | | steps.value = Array.isArray(data) ? data : (data?.data || []) |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || 'å è½½ä»»å¡æ¥éª¤å¤±è´¥') |
| | | } finally { |
| | | stepsLoading.value = false |
| | | } |
| | | } |
| | | |
| | | const statusType = (status) => { |
| | | switch ((status || '').toUpperCase()) { |
| | | case 'COMPLETED': |
| | | return 'success' |
| | | case 'FAILED': |
| | | return 'danger' |
| | | case 'RUNNING': |
| | | return 'warning' |
| | | default: |
| | | return 'info' |
| | | } |
| | | } |
| | | |
| | | const formatDuration = (ms) => { |
| | | if (!ms) return '-' |
| | | if (ms < 1000) return `${ms} ms` |
| | | return `${(ms / 1000).toFixed(1)} s` |
| | | } |
| | | |
| | | watch( |
| | | () => props.groupId, |
| | | () => { |
| | | fetchTasks() |
| | | }, |
| | | { immediate: true } |
| | | ) |
| | | |
| | | onMounted(fetchTasks) |
| | | |
| | | defineExpose({ |
| | | fetchTasks |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .execution-monitor { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | box-shadow: 0 8px 32px rgba(15, 18, 63, 0.08); |
| | | } |
| | | |
| | | .panel-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .panel-header h3 { |
| | | margin: 0; |
| | | } |
| | | |
| | | .panel-header p { |
| | | margin: 4px 0 0; |
| | | color: #909399; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .step-title { |
| | | font-weight: 600; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .step-desc { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | } |
| | | </style> |
| | | |
| New file |
| | |
| | | <template> |
| | | <div class="task-orchestration"> |
| | | <div class="panel-header"> |
| | | <div> |
| | | <h3>å¤è®¾å¤æµè¯ç¼æ</h3> |
| | | <p v-if="group">å½å设å¤ç»ï¼{{ group.groupName }}ï¼{{ group.deviceCount || '-' }} å°è®¾å¤ï¼</p> |
| | | <p v-else class="warning">请å
å¨å·¦ä¾§éæ©ä¸ä¸ªè®¾å¤ç»</p> |
| | | </div> |
| | | <el-button type="primary" :disabled="!group" :loading="loading" @click="handleSubmit"> |
| | | <el-icon><Promotion /></el-icon> |
| | | å¯å¨æµè¯ |
| | | </el-button> |
| | | </div> |
| | | |
| | | <el-form :model="form" label-width="120px"> |
| | | <el-form-item label="ç»çIDå表"> |
| | | <el-input |
| | | v-model="glassIdsInput" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥ç»çæ¡ç ï¼æ¯æå¤è¡æéå·åé" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ä½ç½®ç¼ç "> |
| | | <el-input v-model="form.positionCode" placeholder="ä¾å¦ï¼POS1" /> |
| | | </el-form-item> |
| | | <el-form-item label="åå¨ä½ç½®"> |
| | | <el-input-number v-model="form.storagePosition" :min="1" :max="200" /> |
| | | </el-form-item> |
| | | <el-form-item label="æ§è¡é´é (ms)"> |
| | | <el-input-number v-model="form.executionInterval" :min="100" :max="10000" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, reactive, ref, watch } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { Promotion } from '@element-plus/icons-vue' |
| | | import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask' |
| | | |
| | | const props = defineProps({ |
| | | group: { |
| | | type: Object, |
| | | default: null |
| | | } |
| | | }) |
| | | |
| | | const emit = defineEmits(['task-started']) |
| | | |
| | | const form = reactive({ |
| | | positionCode: '', |
| | | storagePosition: null, |
| | | executionInterval: 1000 |
| | | }) |
| | | |
| | | const glassIdsInput = ref('') |
| | | const loading = ref(false) |
| | | |
| | | watch( |
| | | () => props.group, |
| | | () => { |
| | | glassIdsInput.value = '' |
| | | } |
| | | ) |
| | | |
| | | const glassIds = computed(() => { |
| | | if (!glassIdsInput.value) return [] |
| | | return glassIdsInput.value |
| | | .split(/[\n,ï¼]/) |
| | | .map((item) => item.trim()) |
| | | .filter((item) => item.length > 0) |
| | | }) |
| | | |
| | | const handleSubmit = async () => { |
| | | if (!props.group) { |
| | | ElMessage.warning('请å
éæ©è®¾å¤ç»') |
| | | return |
| | | } |
| | | if (glassIds.value.length === 0) { |
| | | ElMessage.warning('请è³å°è¾å
¥ä¸ä¸ªç»çID') |
| | | return |
| | | } |
| | | try { |
| | | loading.value = true |
| | | await multiDeviceTaskApi.startTask({ |
| | | groupId: props.group.id || props.group.groupId, |
| | | parameters: { |
| | | glassIds: glassIds.value, |
| | | positionCode: form.positionCode || null, |
| | | storagePosition: form.storagePosition, |
| | | executionInterval: form.executionInterval |
| | | } |
| | | }) |
| | | ElMessage.success('ä»»å¡å·²å¯å¨') |
| | | emit('task-started') |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || 'ä»»å¡å¯å¨å¤±è´¥') |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .task-orchestration { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | box-shadow: 0 8px 32px rgba(15, 18, 63, 0.08); |
| | | } |
| | | |
| | | .panel-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .panel-header h3 { |
| | | margin: 0; |
| | | } |
| | | |
| | | .panel-header p { |
| | | margin: 4px 0 0; |
| | | color: #909399; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .panel-header .warning { |
| | | color: #f56c6c; |
| | | } |
| | | </style> |
| | | |