| New file |
| | |
| | | package com.zy.asrs.controller; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.core.annotations.ManagerAuth; |
| | | import com.core.common.BaseRes; |
| | | import com.core.common.Cools; |
| | | import com.core.common.R; |
| | | import com.zy.asrs.entity.BasCrnp; |
| | | import com.zy.asrs.entity.dto.BasCrnpMonitorDto; |
| | | import com.zy.asrs.entity.dto.WcsCrnDto; |
| | | import com.zy.asrs.entity.dto.WcsCrnSyncResult; |
| | | import com.zy.asrs.service.BasCrnpService; |
| | | import com.zy.asrs.service.WcsCrnSyncService; |
| | | import com.zy.asrs.utils.CrnUtils; |
| | | import com.zy.common.web.BaseController; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestMethod; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @RestController |
| | | public class BasCrnpController extends BaseController { |
| | | |
| | | @Autowired |
| | | private BasCrnpService basCrnpService; |
| | | |
| | | @Autowired |
| | | private WcsCrnSyncService wcsCrnSyncService; |
| | | |
| | | @Autowired |
| | | private CrnUtils crnUtils; |
| | | |
| | | @RequestMapping(value = "/basCrnp/{id}/auth") |
| | | @ManagerAuth |
| | | public R get(@PathVariable("id") Integer id) { |
| | | return R.ok(basCrnpService.getById(id)); |
| | | } |
| | | |
| | | @RequestMapping(value = "/basCrnp/list/auth") |
| | | @ManagerAuth |
| | | public R list(@RequestParam(defaultValue = "1") Integer curr, |
| | | @RequestParam(defaultValue = "10") Integer limit, |
| | | @RequestParam(required = false) Integer crnNo, |
| | | @RequestParam(required = false) String condition) { |
| | | QueryWrapper<BasCrnp> wrapper = new QueryWrapper<>(); |
| | | if (crnNo != null) { |
| | | wrapper.eq("crn_no", crnNo); |
| | | } |
| | | if (!Cools.isEmpty(condition)) { |
| | | wrapper.and(inner -> inner.like("crn_no", condition).or().like("memo", condition)); |
| | | } |
| | | wrapper.orderBy(true, true, "crn_no"); |
| | | |
| | | Page<BasCrnp> page = basCrnpService.page(new Page<>(curr, limit), wrapper); |
| | | List<BasCrnpMonitorDto> records = new ArrayList<>(); |
| | | for (BasCrnp basCrnp : page.getRecords()) { |
| | | records.add(toMonitorDto(basCrnp)); |
| | | } |
| | | |
| | | Map<String, Object> data = new HashMap<>(); |
| | | data.put("total", page.getTotal()); |
| | | data.put("records", records); |
| | | data.put("lastSyncTime", crnUtils.getLastSyncTime()); |
| | | data.put("syncError", crnUtils.getLastSyncError()); |
| | | return R.ok(data); |
| | | } |
| | | |
| | | @RequestMapping(value = "/basCrnp/add/auth", method = RequestMethod.POST) |
| | | @ManagerAuth |
| | | public R add(BasCrnp basCrnp) { |
| | | if (basCrnp == null || basCrnp.getCrnNo() == null) { |
| | | return R.error("堆垛机编号不能为空"); |
| | | } |
| | | if (basCrnpService.count(new QueryWrapper<BasCrnp>().eq("crn_no", basCrnp.getCrnNo())) > 0) { |
| | | return R.parse(BaseRes.REPEAT).add("堆垛机编号"); |
| | | } |
| | | Date now = new Date(); |
| | | basCrnp.setStatus(basCrnp.getStatus() == null ? 1 : basCrnp.getStatus()); |
| | | basCrnp.setCreateBy(getUserId()); |
| | | basCrnp.setCreateTime(now); |
| | | basCrnp.setUpdateBy(getUserId()); |
| | | basCrnp.setUpdateTime(now); |
| | | basCrnpService.save(basCrnp); |
| | | return R.ok(); |
| | | } |
| | | |
| | | @RequestMapping(value = "/basCrnp/update/auth", method = RequestMethod.POST) |
| | | @ManagerAuth |
| | | public R update(BasCrnp basCrnp) { |
| | | if (basCrnp == null || basCrnp.getCrnNo() == null) { |
| | | return R.error("堆垛机编号不能为空"); |
| | | } |
| | | BasCrnp old = basCrnpService.getById(basCrnp.getCrnNo()); |
| | | if (old == null) { |
| | | return R.error("堆垛机配置不存在"); |
| | | } |
| | | old.setStatus(basCrnp.getStatus() == null ? old.getStatus() : basCrnp.getStatus()); |
| | | old.setMemo(basCrnp.getMemo()); |
| | | old.setUpdateBy(getUserId()); |
| | | old.setUpdateTime(new Date()); |
| | | basCrnpService.updateById(old); |
| | | return R.ok(); |
| | | } |
| | | |
| | | @RequestMapping(value = "/basCrnp/delete/auth", method = RequestMethod.POST) |
| | | @ManagerAuth |
| | | public R delete(@RequestParam(value = "ids[]") Integer[] ids) { |
| | | if (ids == null || ids.length == 0) { |
| | | return R.error("请选择要删除的数据"); |
| | | } |
| | | basCrnpService.removeByIds(Arrays.asList(ids)); |
| | | for (Integer id : ids) { |
| | | crnUtils.crnMap.remove(id); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | @RequestMapping(value = "/basCrnp/check/column/auth", method = RequestMethod.POST) |
| | | @ManagerAuth |
| | | public R checkColumn(@RequestBody JSONObject param) { |
| | | String key = param == null ? null : param.getString("key"); |
| | | Object val = param == null ? null : param.get("val"); |
| | | if (!"crnNo".equals(key) || Cools.isEmpty(val)) { |
| | | return R.ok(); |
| | | } |
| | | BasCrnp basCrnp = basCrnpService.getOne(new QueryWrapper<BasCrnp>().eq("crn_no", val)); |
| | | if (basCrnp != null) { |
| | | return R.parse(BaseRes.REPEAT).add(getComment(BasCrnp.class, key)); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | @RequestMapping(value = "/basCrnp/refresh/auth", method = RequestMethod.POST) |
| | | @ManagerAuth |
| | | public R refresh() { |
| | | WcsCrnSyncResult result = wcsCrnSyncService.sync(); |
| | | if (!result.isSuccess()) { |
| | | return R.error(result.getMessage()); |
| | | } |
| | | return R.ok(result); |
| | | } |
| | | |
| | | private BasCrnpMonitorDto toMonitorDto(BasCrnp basCrnp) { |
| | | BasCrnpMonitorDto dto = new BasCrnpMonitorDto(); |
| | | dto.setCrnNo(basCrnp.getCrnNo()); |
| | | dto.setLocalStatus(basCrnp.getStatus()); |
| | | dto.setLocalStatusDesc(basCrnp.getStatus$()); |
| | | dto.setMemo(basCrnp.getMemo()); |
| | | |
| | | WcsCrnDto runtime = crnUtils.crnMap.get(basCrnp.getCrnNo()); |
| | | if (runtime == null) { |
| | | dto.setOnline(0); |
| | | dto.setOnlineDesc("离线"); |
| | | dto.setMode(null); |
| | | dto.setModeDesc("离线"); |
| | | dto.setDeviceStatus(null); |
| | | dto.setDeviceStatusDesc("离线"); |
| | | dto.setTaskNo(null); |
| | | dto.setAlarm(null); |
| | | dto.setLastSyncTime(crnUtils.getLastSyncTime()); |
| | | dto.setSyncError(crnUtils.getLastSyncError()); |
| | | return dto; |
| | | } |
| | | |
| | | dto.setOnline(runtime.getOnline()); |
| | | dto.setOnlineDesc(runtime.getOnline() != null && runtime.getOnline() == 1 ? "在线" : "离线"); |
| | | dto.setMode(runtime.getMode()); |
| | | dto.setModeDesc(runtime.getModeDesc()); |
| | | dto.setDeviceStatus(runtime.getStatus()); |
| | | dto.setDeviceStatusDesc(runtime.getStatusDesc()); |
| | | dto.setTaskNo(runtime.getTaskNo()); |
| | | dto.setAlarm(runtime.getAlarm()); |
| | | dto.setLastSyncTime(runtime.getLastSyncTime()); |
| | | dto.setSyncError(runtime.getSyncError()); |
| | | return dto; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.entity; |
| | | |
| | | 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 com.core.common.Cools; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | @Data |
| | | @TableName("asr_bas_crnp") |
| | | public class BasCrnp implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Schema(description = "堆垛机编号") |
| | | @TableId(value = "crn_no", type = IdType.INPUT) |
| | | @TableField("crn_no") |
| | | private Integer crnNo; |
| | | |
| | | @Schema(description = "状态 1: 正常 0: 禁用") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "创建人员") |
| | | @TableField("create_by") |
| | | private Long createBy; |
| | | |
| | | @Schema(description = "创建时间") |
| | | @TableField("create_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date createTime; |
| | | |
| | | @Schema(description = "修改人员") |
| | | @TableField("update_by") |
| | | private Long updateBy; |
| | | |
| | | @Schema(description = "修改时间") |
| | | @TableField("update_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date updateTime; |
| | | |
| | | @Schema(description = "备注") |
| | | private String memo; |
| | | |
| | | public String getStatus$() { |
| | | if (this.status == null) { |
| | | return null; |
| | | } |
| | | switch (this.status) { |
| | | case 1: |
| | | return "正常"; |
| | | case 0: |
| | | return "禁用"; |
| | | default: |
| | | return String.valueOf(this.status); |
| | | } |
| | | } |
| | | |
| | | public String getCreateTime$() { |
| | | if (Cools.isEmpty(this.createTime)) { |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime); |
| | | } |
| | | |
| | | public String getUpdateTime$() { |
| | | if (Cools.isEmpty(this.updateTime)) { |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.entity.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class BasCrnpMonitorDto { |
| | | |
| | | private Integer crnNo; |
| | | |
| | | private Integer localStatus; |
| | | |
| | | private String localStatusDesc; |
| | | |
| | | private Integer online; |
| | | |
| | | private String onlineDesc; |
| | | |
| | | private Integer mode; |
| | | |
| | | private String modeDesc; |
| | | |
| | | private Integer deviceStatus; |
| | | |
| | | private String deviceStatusDesc; |
| | | |
| | | private Integer taskNo; |
| | | |
| | | private Integer alarm; |
| | | |
| | | private String lastSyncTime; |
| | | |
| | | private String syncError; |
| | | |
| | | private String memo; |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.entity.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class WcsCrnDto { |
| | | |
| | | private Integer crnNo; |
| | | |
| | | private Integer online; |
| | | |
| | | private Integer mode; |
| | | |
| | | private String modeDesc; |
| | | |
| | | private Integer status; |
| | | |
| | | private String statusDesc; |
| | | |
| | | private Integer taskNo; |
| | | |
| | | private Integer alarm; |
| | | |
| | | private String lastSyncTime; |
| | | |
| | | private String syncError; |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.entity.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class WcsCrnSyncResult { |
| | | |
| | | private boolean success; |
| | | |
| | | private String message; |
| | | |
| | | private int requestCount; |
| | | |
| | | private int updateCount; |
| | | |
| | | private String lastSyncTime; |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.enums; |
| | | |
| | | public enum CrnModeType { |
| | | |
| | | NONE(-1, "离线"), |
| | | STOP(0, "维修"), |
| | | HAND(1, "手动"), |
| | | HALF_AUTO(2, "半自动"), |
| | | AUTO(3, "自动"); |
| | | |
| | | public final Integer id; |
| | | public final String desc; |
| | | |
| | | CrnModeType(Integer id, String desc) { |
| | | this.id = id; |
| | | this.desc = desc; |
| | | } |
| | | |
| | | public static CrnModeType get(Integer id) { |
| | | if (id == null) { |
| | | return NONE; |
| | | } |
| | | for (CrnModeType type : CrnModeType.values()) { |
| | | if (type.id.equals(id)) { |
| | | return type; |
| | | } |
| | | } |
| | | return NONE; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.enums; |
| | | |
| | | public enum CrnStatusType { |
| | | |
| | | NONE(-1, "离线"), |
| | | IDLE(0, "空闲"), |
| | | FETCH_MOVING(1, "取货行走"), |
| | | FETCH_WAITING(2, "取货等待"), |
| | | FETCHING(3, "取货中"), |
| | | PUT_MOVING(4, "放货走行"), |
| | | PUT_WAITING(5, "放货等待"), |
| | | PUTTING(6, "放货中"), |
| | | ORIGIN_GO(7, "回原点"), |
| | | ORIGIN_BACK(8, "回反原点"), |
| | | MOVING(9, "走行中"), |
| | | WAITING(10, "任务完成等待WCS确认"), |
| | | PAUSE(11, "任务暂停"), |
| | | SOS(99, "报警"), |
| | | UNKNOW(100, "其他"); |
| | | |
| | | public final Integer id; |
| | | public final String desc; |
| | | |
| | | CrnStatusType(Integer id, String desc) { |
| | | this.id = id; |
| | | this.desc = desc; |
| | | } |
| | | |
| | | public static CrnStatusType get(Integer id) { |
| | | if (id == null) { |
| | | return NONE; |
| | | } |
| | | for (CrnStatusType type : CrnStatusType.values()) { |
| | | if (type.id.equals(id)) { |
| | | return type; |
| | | } |
| | | } |
| | | return UNKNOW; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.zy.asrs.entity.BasCrnp; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | @Mapper |
| | | @Repository |
| | | public interface BasCrnpMapper extends BaseMapper<BasCrnp> { |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.zy.asrs.entity.BasCrnp; |
| | | |
| | | public interface BasCrnpService extends IService<BasCrnp> { |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.service; |
| | | |
| | | import com.zy.asrs.entity.dto.WcsCrnSyncResult; |
| | | |
| | | public interface WcsCrnSyncService { |
| | | |
| | | WcsCrnSyncResult sync(); |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.zy.asrs.entity.BasCrnp; |
| | | import com.zy.asrs.mapper.BasCrnpMapper; |
| | | import com.zy.asrs.service.BasCrnpService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Service("basCrnpService") |
| | | public class BasCrnpServiceImpl extends ServiceImpl<BasCrnpMapper, BasCrnp> implements BasCrnpService { |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.service.impl; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.core.common.Cools; |
| | | import com.zy.asrs.entity.BasCrnp; |
| | | import com.zy.asrs.entity.dto.WcsCrnDto; |
| | | import com.zy.asrs.entity.dto.WcsCrnSyncResult; |
| | | import com.zy.asrs.enums.CrnModeType; |
| | | import com.zy.asrs.enums.CrnStatusType; |
| | | import com.zy.asrs.service.BasCrnpService; |
| | | import com.zy.asrs.service.WcsCrnSyncService; |
| | | import com.zy.asrs.utils.CrnUtils; |
| | | import com.zy.common.utils.HttpHandler; |
| | | import com.zy.system.entity.Config; |
| | | import com.zy.system.service.ConfigService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | @Slf4j |
| | | @Service("wcsCrnSyncService") |
| | | public class WcsCrnSyncServiceImpl implements WcsCrnSyncService { |
| | | |
| | | private static final String WCS_DEVICE_STATUS_URL_CODE = "wcsDeviceStatusUrl"; |
| | | |
| | | @Autowired |
| | | private ConfigService configService; |
| | | |
| | | @Autowired |
| | | private BasCrnpService basCrnpService; |
| | | |
| | | @Autowired |
| | | private CrnUtils crnUtils; |
| | | |
| | | @Override |
| | | public synchronized WcsCrnSyncResult sync() { |
| | | WcsCrnSyncResult result = new WcsCrnSyncResult(); |
| | | String now = nowText(); |
| | | |
| | | List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<BasCrnp>() |
| | | .eq("status", 1) |
| | | .orderBy(true, true, "crn_no")); |
| | | Set<Integer> enabledCrnNos = new HashSet<>(); |
| | | for (BasCrnp basCrnp : basCrnps) { |
| | | if (basCrnp != null && basCrnp.getCrnNo() != null) { |
| | | enabledCrnNos.add(basCrnp.getCrnNo()); |
| | | } |
| | | } |
| | | crnUtils.crnMap.keySet().removeIf(crnNo -> !enabledCrnNos.contains(crnNo)); |
| | | |
| | | result.setRequestCount(enabledCrnNos.size()); |
| | | |
| | | if (enabledCrnNos.isEmpty()) { |
| | | crnUtils.setLastSyncTime(now); |
| | | crnUtils.setLastSyncError(null); |
| | | result.setSuccess(true); |
| | | result.setMessage("暂无启用的堆垛机配置"); |
| | | result.setLastSyncTime(now); |
| | | return result; |
| | | } |
| | | |
| | | Config config = configService.getOne(new QueryWrapper<Config>().eq("code", WCS_DEVICE_STATUS_URL_CODE)); |
| | | String deviceStatusUrl = config == null ? null : config.getValue(); |
| | | if (Cools.isEmpty(deviceStatusUrl)) { |
| | | return markSyncError(enabledCrnNos, now, result, "未配置WCS堆垛机状态接口地址"); |
| | | } |
| | | |
| | | HashMap<String, Object> requestParam = new HashMap<>(); |
| | | requestParam.put("crnNos", new ArrayList<>(enabledCrnNos)); |
| | | |
| | | String response; |
| | | try { |
| | | response = new HttpHandler.Builder() |
| | | .setUri(deviceStatusUrl) |
| | | .setHttps(deviceStatusUrl.startsWith("https://")) |
| | | .setJson(JSON.toJSONString(requestParam)) |
| | | .setTimeout(30, TimeUnit.SECONDS) |
| | | .build() |
| | | .doPost(); |
| | | } catch (Exception e) { |
| | | log.error("同步堆垛机状态失败", e); |
| | | return markSyncError(enabledCrnNos, now, result, "调用WCS接口失败: " + e.getMessage()); |
| | | } |
| | | |
| | | if (Cools.isEmpty(response)) { |
| | | return markSyncError(enabledCrnNos, now, result, "WCS接口无响应"); |
| | | } |
| | | |
| | | JSONObject responseObj; |
| | | try { |
| | | responseObj = JSON.parseObject(response); |
| | | } catch (Exception e) { |
| | | log.error("解析堆垛机状态响应失败: {}", response, e); |
| | | return markSyncError(enabledCrnNos, now, result, "WCS接口响应解析失败"); |
| | | } |
| | | |
| | | if (responseObj == null) { |
| | | return markSyncError(enabledCrnNos, now, result, "WCS接口响应为空"); |
| | | } |
| | | if (responseObj.getInteger("code") == null || responseObj.getInteger("code") != 200) { |
| | | return markSyncError(enabledCrnNos, now, result, |
| | | Cools.isEmpty(responseObj.getString("msg")) ? "WCS接口返回失败" : responseObj.getString("msg")); |
| | | } |
| | | |
| | | JSONObject dataObj = responseObj.getJSONObject("data"); |
| | | JSONArray crnList = dataObj == null ? null : dataObj.getJSONArray("crnList"); |
| | | Set<Integer> returnedCrnNos = new HashSet<>(); |
| | | int updateCount = 0; |
| | | if (crnList != null) { |
| | | for (Object item : crnList) { |
| | | if (!(item instanceof JSONObject)) { |
| | | continue; |
| | | } |
| | | JSONObject crnObj = (JSONObject) item; |
| | | Integer crnNo = crnObj.getInteger("crnNo"); |
| | | if (crnNo == null || !enabledCrnNos.contains(crnNo)) { |
| | | continue; |
| | | } |
| | | returnedCrnNos.add(crnNo); |
| | | crnUtils.crnMap.put(crnNo, buildOnlineDto(crnObj, now)); |
| | | updateCount++; |
| | | } |
| | | } |
| | | |
| | | for (Integer crnNo : enabledCrnNos) { |
| | | if (returnedCrnNos.contains(crnNo)) { |
| | | continue; |
| | | } |
| | | crnUtils.crnMap.put(crnNo, buildOfflineDto(crnNo, now, null)); |
| | | } |
| | | |
| | | crnUtils.setLastSyncTime(now); |
| | | crnUtils.setLastSyncError(null); |
| | | result.setSuccess(true); |
| | | result.setMessage("同步成功"); |
| | | result.setUpdateCount(updateCount); |
| | | result.setLastSyncTime(now); |
| | | return result; |
| | | } |
| | | |
| | | private WcsCrnSyncResult markSyncError(Set<Integer> configuredCrnNos, String now, WcsCrnSyncResult result, String error) { |
| | | crnUtils.setLastSyncTime(now); |
| | | crnUtils.setLastSyncError(error); |
| | | for (Integer crnNo : configuredCrnNos) { |
| | | WcsCrnDto current = crnUtils.crnMap.get(crnNo); |
| | | if (current == null) { |
| | | current = buildOfflineDto(crnNo, now, error); |
| | | } else { |
| | | current.setLastSyncTime(now); |
| | | current.setSyncError(error); |
| | | } |
| | | crnUtils.crnMap.put(crnNo, current); |
| | | } |
| | | result.setSuccess(false); |
| | | result.setMessage(error); |
| | | result.setLastSyncTime(now); |
| | | return result; |
| | | } |
| | | |
| | | private WcsCrnDto buildOnlineDto(JSONObject crnObj, String now) { |
| | | WcsCrnDto dto = new WcsCrnDto(); |
| | | Integer mode = crnObj.getInteger("mode"); |
| | | Integer status = crnObj.getInteger("status"); |
| | | dto.setCrnNo(crnObj.getInteger("crnNo")); |
| | | dto.setOnline(1); |
| | | dto.setMode(mode); |
| | | dto.setModeDesc(CrnModeType.get(mode).desc); |
| | | dto.setStatus(status); |
| | | dto.setStatusDesc(CrnStatusType.get(status).desc); |
| | | dto.setTaskNo(crnObj.getInteger("taskNo")); |
| | | dto.setAlarm(crnObj.getInteger("alarm")); |
| | | dto.setLastSyncTime(now); |
| | | dto.setSyncError(null); |
| | | return dto; |
| | | } |
| | | |
| | | private WcsCrnDto buildOfflineDto(Integer crnNo, String now, String syncError) { |
| | | WcsCrnDto dto = new WcsCrnDto(); |
| | | dto.setCrnNo(crnNo); |
| | | dto.setOnline(0); |
| | | dto.setMode(null); |
| | | dto.setModeDesc("离线"); |
| | | dto.setStatus(null); |
| | | dto.setStatusDesc("离线"); |
| | | dto.setTaskNo(null); |
| | | dto.setAlarm(null); |
| | | dto.setLastSyncTime(now); |
| | | dto.setSyncError(syncError); |
| | | return dto; |
| | | } |
| | | |
| | | private String nowText() { |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.timer; |
| | | |
| | | import com.zy.asrs.service.WcsCrnSyncService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | @Component |
| | | public class WcsCrnTimer { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(WcsCrnTimer.class); |
| | | |
| | | @Autowired |
| | | private WcsCrnSyncService wcsCrnSyncService; |
| | | |
| | | @Scheduled(cron = "0/3 * * * * ? ") |
| | | public void execute() { |
| | | try { |
| | | wcsCrnSyncService.sync(); |
| | | } catch (Exception e) { |
| | | log.error("堆垛机状态定时同步失败", e); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.utils; |
| | | |
| | | import com.zy.asrs.entity.dto.WcsCrnDto; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | @Component |
| | | public class CrnUtils { |
| | | |
| | | public Map<Integer, WcsCrnDto> crnMap = new ConcurrentHashMap<>(); |
| | | |
| | | private volatile String lastSyncTime; |
| | | |
| | | private volatile String lastSyncError; |
| | | |
| | | public String getLastSyncTime() { |
| | | return lastSyncTime; |
| | | } |
| | | |
| | | public void setLastSyncTime(String lastSyncTime) { |
| | | this.lastSyncTime = lastSyncTime; |
| | | } |
| | | |
| | | public String getLastSyncError() { |
| | | return lastSyncError; |
| | | } |
| | | |
| | | public void setLastSyncError(String lastSyncError) { |
| | | this.lastSyncError = lastSyncError; |
| | | } |
| | | } |
| New file |
| | |
| | | CREATE TABLE IF NOT EXISTS `asr_bas_crnp` ( |
| | | `crn_no` int NOT NULL COMMENT '堆垛机编号', |
| | | `status` int DEFAULT 1 COMMENT '状态 1:正常 0:禁用', |
| | | `create_by` bigint DEFAULT NULL COMMENT '创建人', |
| | | `create_time` datetime DEFAULT NULL COMMENT '创建时间', |
| | | `update_by` bigint DEFAULT NULL COMMENT '修改人', |
| | | `update_time` datetime DEFAULT NULL COMMENT '修改时间', |
| | | `memo` varchar(255) DEFAULT NULL COMMENT '备注', |
| | | PRIMARY KEY (`crn_no`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='堆垛机配置'; |
| | | |
| | | INSERT INTO `sys_config` (`name`, `code`, `value`, `type`, `status`, `select_type`) |
| | | SELECT 'WCS堆垛机状态接口', 'wcsDeviceStatusUrl', '', 1, 1, NULL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM `sys_config` WHERE `code` = 'wcsDeviceStatusUrl' |
| | | ); |
| | | |
| | | INSERT INTO `sys_resource` (`code`, `name`, `resource_id`, `level`, `sort`, `status`) |
| | | SELECT 'views/basCrnp/basCrnp.html', '堆垛机状态监控', `resource_id`, `level`, `sort` + 1, `status` |
| | | FROM `sys_resource` |
| | | WHERE `code` = 'views/basStation/basStation.html' |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM `sys_resource` WHERE `code` = 'views/basCrnp/basCrnp.html' |
| | | ) |
| | | LIMIT 1; |
| | | |
| | | INSERT INTO `sys_role_resource` (`role_id`, `resource_id`) |
| | | SELECT rr.`role_id`, new_res.`id` |
| | | FROM `sys_role_resource` rr |
| | | JOIN `sys_resource` old_res ON old_res.`id` = rr.`resource_id` |
| | | JOIN `sys_resource` new_res ON new_res.`code` = 'views/basCrnp/basCrnp.html' |
| | | WHERE old_res.`code` = 'views/basStation/basStation.html' |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM `sys_role_resource` srr |
| | | WHERE srr.`role_id` = rr.`role_id` |
| | | AND srr.`resource_id` = new_res.`id` |
| | | ); |
| New file |
| | |
| | | var pageCurr = 1; |
| | | var tableIns; |
| | | var autoRefreshTimer = null; |
| | | var editDialogIndex = null; |
| | | var reloading = false; |
| | | |
| | | layui.config({ |
| | | base: baseUrl + "/static/layui/lay/modules/" |
| | | }).use(['table', 'form', 'admin'], function () { |
| | | var table = layui.table; |
| | | var $ = layui.jquery; |
| | | var layer = layui.layer; |
| | | var form = layui.form; |
| | | var admin = layui.admin; |
| | | |
| | | tableIns = table.render({ |
| | | elem: '#basCrnp', |
| | | headers: {token: localStorage.getItem('token')}, |
| | | url: baseUrl + '/basCrnp/list/auth', |
| | | page: true, |
| | | limit: 15, |
| | | limits: [15, 30, 50, 100, 200, 500], |
| | | toolbar: '#toolbar', |
| | | cellMinWidth: 80, |
| | | height: 'full-160', |
| | | cols: [[ |
| | | {type: 'checkbox'}, |
| | | {field: 'crnNo', align: 'center', title: '堆垛机号'}, |
| | | {field: 'localStatusDesc', align: 'center', title: '本地状态'}, |
| | | {field: 'onlineDesc', align: 'center', title: '在线状态'}, |
| | | {field: 'modeDesc', align: 'center', title: '模式'}, |
| | | {field: 'deviceStatusDesc', align: 'center', title: '设备状态'}, |
| | | { |
| | | field: 'taskNo', |
| | | align: 'center', |
| | | title: '任务号', |
| | | templet: function (d) { |
| | | return d.taskNo == null ? '-' : d.taskNo; |
| | | } |
| | | }, |
| | | { |
| | | field: 'alarm', |
| | | align: 'center', |
| | | title: '报警码', |
| | | templet: function (d) { |
| | | return d.alarm == null ? '-' : d.alarm; |
| | | } |
| | | }, |
| | | { |
| | | field: 'lastSyncTime', |
| | | align: 'center', |
| | | title: '最后同步时间', |
| | | templet: function (d) { |
| | | return isEmpty(d.lastSyncTime) ? '-' : d.lastSyncTime; |
| | | } |
| | | }, |
| | | {field: 'memo', align: 'center', title: '备注'}, |
| | | {fixed: 'right', title: '操作', align: 'center', toolbar: '#operate', width: 120} |
| | | ]], |
| | | request: { |
| | | pageName: 'curr', |
| | | pageSize: 'limit' |
| | | }, |
| | | parseData: function (res) { |
| | | return { |
| | | code: res.code, |
| | | msg: res.msg, |
| | | count: res.data.total, |
| | | data: res.data.records, |
| | | syncError: res.data.syncError, |
| | | lastSyncTime: res.data.lastSyncTime |
| | | }; |
| | | }, |
| | | response: { |
| | | statusCode: 200 |
| | | }, |
| | | done: function (res, curr) { |
| | | reloading = false; |
| | | if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | return; |
| | | } |
| | | pageCurr = curr; |
| | | renderSyncStatus(res.lastSyncTime, res.syncError); |
| | | limit(); |
| | | } |
| | | }); |
| | | |
| | | table.on('toolbar(basCrnp)', function (obj) { |
| | | var checkStatus = table.checkStatus(obj.config.id).data; |
| | | switch (obj.event) { |
| | | case 'addData': |
| | | showEditModel(); |
| | | break; |
| | | case 'deleteData': |
| | | if (checkStatus.length === 0) { |
| | | layer.msg('请选择要删除的数据', {icon: 2}); |
| | | return; |
| | | } |
| | | del(checkStatus.map(function (d) { |
| | | return d.crnNo; |
| | | })); |
| | | break; |
| | | case 'refreshData': |
| | | refreshNow(); |
| | | break; |
| | | } |
| | | }); |
| | | |
| | | table.on('tool(basCrnp)', function (obj) { |
| | | var data = obj.data; |
| | | switch (obj.event) { |
| | | case 'edit': |
| | | showEditModel(data); |
| | | break; |
| | | case 'del': |
| | | del([data.crnNo]); |
| | | break; |
| | | } |
| | | }); |
| | | |
| | | form.on('submit(search)', function () { |
| | | pageCurr = 1; |
| | | tableReload(); |
| | | return false; |
| | | }); |
| | | |
| | | form.on('submit(reset)', function () { |
| | | pageCurr = 1; |
| | | clearFormVal($('#search-box')); |
| | | tableReload(); |
| | | return false; |
| | | }); |
| | | |
| | | function showEditModel(mData) { |
| | | editDialogIndex = admin.open({ |
| | | type: 1, |
| | | area: '600px', |
| | | title: (mData ? '修改' : '添加') + '堆垛机配置', |
| | | content: $('#editDialog').html(), |
| | | success: function (layero, dIndex) { |
| | | form.val('detail', { |
| | | crnNo: mData ? mData.crnNo : '', |
| | | status: mData ? mData.localStatus : 1, |
| | | memo: mData ? mData.memo : '' |
| | | }); |
| | | if (mData) { |
| | | $(layero).find('#crnNo').attr('readonly', true).addClass('layui-disabled'); |
| | | } |
| | | form.on('submit(editSubmit)', function (data) { |
| | | var loadIndex = layer.load(2); |
| | | $.ajax({ |
| | | url: baseUrl + "/basCrnp/" + (mData ? 'update' : 'add') + "/auth", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | data: data.field, |
| | | method: 'POST', |
| | | success: function (res) { |
| | | layer.close(loadIndex); |
| | | if (res.code === 200) { |
| | | layer.close(dIndex); |
| | | editDialogIndex = null; |
| | | layer.msg('保存成功', {icon: 1}); |
| | | tableReload(); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } else { |
| | | layer.msg(res.msg, {icon: 2}); |
| | | } |
| | | } |
| | | }); |
| | | return false; |
| | | }); |
| | | $(layero).children('.layui-layer-content').css('overflow', 'visible'); |
| | | layui.form.render('select'); |
| | | }, |
| | | end: function () { |
| | | editDialogIndex = null; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | function del(ids) { |
| | | layer.confirm('确定要删除选中数据吗?', { |
| | | skin: 'layui-layer-admin', |
| | | shade: .1 |
| | | }, function (i) { |
| | | layer.close(i); |
| | | var loadIndex = layer.load(2); |
| | | $.ajax({ |
| | | url: baseUrl + "/basCrnp/delete/auth", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | data: {ids: ids}, |
| | | method: 'POST', |
| | | success: function (res) { |
| | | layer.close(loadIndex); |
| | | if (res.code === 200) { |
| | | layer.msg('删除成功', {icon: 1}); |
| | | pageCurr = 1; |
| | | tableReload(); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } else { |
| | | layer.msg(res.msg, {icon: 2}); |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | function refreshNow() { |
| | | var loadIndex = layer.load(2); |
| | | $.ajax({ |
| | | url: baseUrl + '/basCrnp/refresh/auth', |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'POST', |
| | | success: function (res) { |
| | | layer.close(loadIndex); |
| | | if (res.code === 200) { |
| | | layer.msg('同步成功', {icon: 1}); |
| | | tableReload(); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } else { |
| | | layer.msg(res.msg, {icon: 2}); |
| | | } |
| | | }, |
| | | error: function () { |
| | | layer.close(loadIndex); |
| | | layer.msg('同步请求异常', {icon: 2}); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | function renderSyncStatus(lastSyncTime, syncError) { |
| | | var box = $('#syncStatusBox'); |
| | | if (isEmpty(lastSyncTime) && isEmpty(syncError)) { |
| | | box.css('color', '#999').text('尚未同步'); |
| | | return; |
| | | } |
| | | if (!isEmpty(syncError)) { |
| | | box.css('color', '#FF5722').text('数据非最新,最近同步时间:' + (lastSyncTime || '-') + ',原因:' + syncError); |
| | | return; |
| | | } |
| | | box.css('color', '#16b777').text('最近同步成功时间:' + (lastSyncTime || '-')); |
| | | } |
| | | |
| | | autoRefreshTimer = setInterval(function () { |
| | | if (editDialogIndex != null || reloading) { |
| | | return; |
| | | } |
| | | tableReload(); |
| | | }, 3000); |
| | | |
| | | $(window).on('beforeunload', function () { |
| | | if (autoRefreshTimer != null) { |
| | | clearInterval(autoRefreshTimer); |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | function tableReload() { |
| | | if (reloading) { |
| | | return; |
| | | } |
| | | reloading = true; |
| | | var searchData = {}; |
| | | $.each($('#search-box [name]').serializeArray(), function () { |
| | | searchData[this.name] = this.value; |
| | | }); |
| | | tableIns.reload({ |
| | | where: searchData, |
| | | page: {curr: pageCurr} |
| | | }); |
| | | } |
| | | |
| New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="en"> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <title>堆垛机状态监控</title> |
| | | <meta name="renderer" content="webkit"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> |
| | | <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all"> |
| | | <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all"> |
| | | <link rel="stylesheet" href="../../static/css/cool.css" media="all"> |
| | | </head> |
| | | <body> |
| | | |
| | | <div class="layui-fluid"> |
| | | <div class="layui-card"> |
| | | <div class="layui-card-body"> |
| | | <div class="layui-form toolbar" id="search-box"> |
| | | <div class="layui-form-item"> |
| | | <div class="layui-inline"> |
| | | <div class="layui-input-inline"> |
| | | <input class="layui-input" type="text" name="crnNo" placeholder="堆垛机号" autocomplete="off"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-inline"> |
| | | <div class="layui-input-inline"> |
| | | <input class="layui-input" type="text" name="condition" placeholder="关键字" autocomplete="off"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-inline">  |
| | | <button class="layui-btn icon-btn" lay-filter="search" lay-submit> |
| | | <i class="layui-icon"></i>搜索 |
| | | </button> |
| | | <button class="layui-btn icon-btn" lay-filter="reset" lay-submit> |
| | | <i class="layui-icon"></i>重置 |
| | | </button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div id="syncStatusBox" class="layui-word-aux" style="margin-bottom: 10px;">尚未同步</div> |
| | | |
| | | <table class="layui-hide" id="basCrnp" lay-filter="basCrnp"></table> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <script type="text/html" id="toolbar"> |
| | | <div class="layui-btn-container"> |
| | | <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">新增</button> |
| | | <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">删除</button> |
| | | <button class="layui-btn layui-btn-sm layui-btn-normal" id="btn-refresh" lay-event="refreshData">立即同步</button> |
| | | </div> |
| | | </script> |
| | | |
| | | <script type="text/html" id="operate"> |
| | | <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">修改</a> |
| | | <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">删除</a> |
| | | </script> |
| | | |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/basCrnp/basCrnp.js" charset="utf-8"></script> |
| | | </body> |
| | | |
| | | <script type="text/html" id="editDialog"> |
| | | <form id="detail" lay-filter="detail" class="layui-form admin-form model-form"> |
| | | <div class="layui-row"> |
| | | <div class="layui-col-md12"> |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label">堆垛机号: </label> |
| | | <div class="layui-input-block"> |
| | | <input id="crnNo" class="layui-input" name="crnNo" placeholder="请输入堆垛机号" onkeyup="check(this.id, 'basCrnp')"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label">本地状态: </label> |
| | | <div class="layui-input-block"> |
| | | <select name="status"> |
| | | <option value="1">正常</option> |
| | | <option value="0">禁用</option> |
| | | </select> |
| | | </div> |
| | | </div> |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label">备注: </label> |
| | | <div class="layui-input-block"> |
| | | <input class="layui-input" name="memo" placeholder="请输入备注"> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <hr class="layui-bg-gray"> |
| | | <div class="layui-form-item text-right"> |
| | | <button class="layui-btn" lay-filter="editSubmit" lay-submit>保存</button> |
| | | <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button> |
| | | </div> |
| | | </form> |
| | | </script> |
| | | </html> |