pom.xml
@@ -97,6 +97,11 @@ <version>1.16.22</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.10.0</version> </dependency> </dependencies> <build> src/main/java/com/zy/asrs/controller/WorkController.java
@@ -29,6 +29,12 @@ @Autowired private BasDevpService basDevpService; @RequestMapping("/agv/put/site") @ManagerAuth(memo = "获取AGV工作区") public R availableAgvSite(){ return R.ok().add(basDevpService.getAgvAvailableInSite()); } @RequestMapping("/available/put/site") @ManagerAuth(memo = "获取入库站点") public R availablePutSite(){ src/main/java/com/zy/asrs/entity/param/CombParam.java
@@ -23,6 +23,9 @@ private String billNo; // 小车取货点 private Integer agvSite; public String getWarehouse() { return warehouse; } @@ -117,4 +120,12 @@ public void setBillNo(String billNo) { this.billNo = billNo; } public Integer getAgvSite() { return agvSite; } public void setAgvSite(Integer agvSite) { this.agvSite = agvSite; } } src/main/java/com/zy/asrs/mapper/BasDevpMapper.java
@@ -4,6 +4,7 @@ import com.zy.asrs.entity.BasDevp; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; import java.util.List; @@ -12,6 +13,9 @@ @Repository public interface BasDevpMapper extends BaseMapper<BasDevp> { @Select("select dev_no from asr_bas_devp where 1=1 and fronting = 'Y' order by dev_no") List<Integer> getAgvInArea(); List<Integer> getAvailableInSite(@Param("typeNo") Integer typeNo); List<Integer> getAvailableOutSite(@Param("typeNo") Integer typeNo); src/main/java/com/zy/asrs/service/BasAgvService.java
@@ -5,4 +5,6 @@ public interface BasAgvService extends IService<BasAgv> { BasAgv selectIdleAgv(); } src/main/java/com/zy/asrs/service/BasDevpService.java
@@ -8,6 +8,12 @@ public interface BasDevpService extends IService<BasDevp> { /** * Agv工作区 * @return */ List<Integer> getAgvAvailableInSite(); /** * 入库站 * @return */ src/main/java/com/zy/asrs/service/impl/BasAgvServiceImpl.java
@@ -1,12 +1,32 @@ package com.zy.asrs.service.impl; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.core.common.Cools; import com.core.exception.CoolException; import com.zy.asrs.mapper.BasAgvMapper; import com.zy.asrs.entity.BasAgv; import com.zy.asrs.service.BasAgvService; import com.baomidou.mybatisplus.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; @Slf4j @Service("basAgvService") public class BasAgvServiceImpl extends ServiceImpl<BasAgvMapper, BasAgv> implements BasAgvService { @Override public BasAgv selectIdleAgv() { List<BasAgv> idleAgvs = this.selectList(new EntityWrapper<BasAgv>() .eq("in_enable", "Y") .eq("is_user", 1) .eq("status", 6) ); if (Cools.isEmpty(idleAgvs)) { log.warn("没有空闲的Agv小车"); throw new CoolException("没有空闲的Agv小车"); } return idleAgvs.get(0); } } src/main/java/com/zy/asrs/service/impl/BasDevpServiceImpl.java
@@ -18,6 +18,11 @@ private WrkMastService wrkMastService; @Override public List<Integer> getAgvAvailableInSite() { return this.baseMapper.getAgvInArea(); } @Override public List<Integer> getAvailableInSite() { return this.baseMapper.getAvailableInSite(1); } src/main/java/com/zy/asrs/service/impl/MobileServiceImpl.java
@@ -1,34 +1,48 @@ package com.zy.asrs.service.impl; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.mapper.Wrapper; import com.core.common.BaseRes; import com.core.common.Cools; import com.core.exception.CoolException; import com.zy.asrs.entity.BasAgv; import com.zy.asrs.entity.MatCode; import com.zy.asrs.entity.WaitPakin; import com.zy.asrs.entity.param.CombParam; import com.zy.asrs.service.BasAgvService; import com.zy.asrs.service.MatCodeService; import com.zy.asrs.service.MobileService; import com.zy.asrs.service.WaitPakinService; import com.zy.asrs.utils.VersionUtils; import com.zy.common.model.agv.AgvCommand; import com.zy.common.model.agv.AgvResult; import com.zy.common.utils.HttpHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.util.Date; /** * 移动端服务核心类 * Created by vincent on 2020/6/28 */ @Slf4j @Service public class MobileServiceImpl implements MobileService { @Value("${agv.url}") private String agvUrl; @Autowired private MatCodeService matCodeService; @Autowired private WaitPakinService waitPakinService; @Autowired private BasAgvService basAgvService; @Override @Transactional @@ -66,6 +80,34 @@ throw new CoolException("保存数据失败"); } } // 小车入库搬运命令 ---------------------------------------------------- if (!Cools.isEmpty(param.getAgvSite())) { BasAgv idleAgv = basAgvService.selectIdleAgv(); AgvCommand command = new AgvCommand(); command.setAgvId(idleAgv.getAgvId()); command.setInterCode("8888"); command.setBeginLoc(String.valueOf(param.getAgvSite())); command.setEndLoc("10"); log.info(JSON.toJSONString(command)); String result; try { result = new HttpHandler.Builder() .setUri(agvUrl + "/api/interfaceTask/SendTaskByThirdParty") .setJson(JSON.toJSONString(command)) .build() .doPost(); } catch (IOException e) { e.printStackTrace(); throw new CoolException("访问AGV接口失败"); } AgvResult agvResult = JSON.parseObject(result, AgvResult.class); log.info(JSON.toJSONString(agvResult)); if (!agvResult.getResult()) { log.error("agv命令发送失败[agvId={}],错误信息={}", command.getAgvId(), agvResult.getExplain()); throw new CoolException("agv命令发送失败[agvId=" + command.getAgvId() + "],错误信息=" + agvResult.getExplain()); } } } else { WaitPakin waitPakin = new WaitPakin(); waitPakin.setZpallet(param.getBarcode()); // 托盘码 src/main/java/com/zy/common/model/agv/AgvCommand.java
New file @@ -0,0 +1,41 @@ package com.zy.common.model.agv; import lombok.Data; /** * Created by vincent on 2021/5/19 */ @Data public class AgvCommand { // 小车号 private Integer AgvId; // 任务号 private String InterCode; // 任务优先级 固定 - 5 private String Sort = "5"; // 开始货位 private String BeginLoc; // 开始货位层级 固定 - 1 private String BeginLevel = "1"; // 目的货位 private String EndLoc; // 目的货位层级 private String EndLevel = "1"; // 任务类型(3:取放货;5:取货+载货行走;6: 载货行走+放货) 固定 3 private String TaskType = "3"; // 托盘码 private String TrayCode; // 托盘类型 固定 1 private String SalverType = "1"; } src/main/java/com/zy/common/model/agv/AgvInfo.java
New file @@ -0,0 +1,76 @@ package com.zy.common.model.agv; import com.alibaba.fastjson.annotation.JSONField; import com.core.common.Cools; import com.zy.asrs.entity.BasAgv; import com.zy.common.model.enums.AgvStatusType; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * Created by vincent on 2021/5/19 */ @Data public class AgvInfo implements Cloneable, Serializable { private static final long serialVersionUID = 1L; // 小车编号 private Integer Id; // 是否启用(true:启用,false:禁用) private Boolean IsUse; // 当前位置点位 private String CurrentNode; // 当前电量 private String Power; // 一般状态(01:前往取货点,02:取货点取货,03:前往送货点,04:送货点放货,05:任务完成,06:空闲,07:故障,08:充电中,09:手动,10:失联,11:离线,12:行走) private String Status; @JSONField(serialize = false) private transient AgvStatusType agvStatus; // 故障码 private String Fault; // 任务号 private String taskcode; // 任务点 private String Point_Number; // 任务类型(1:行走,2:取货,3:放货,4:充电) private String ActionType; @Override public AgvInfo clone() { try { return (AgvInfo) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } public BasAgv toSqlModel(){ BasAgv basAgv = new BasAgv(); basAgv.setAgvId(this.getId()); basAgv.setIsUser(this.getIsUse()?1:0); basAgv.setCurrentNode(this.getCurrentNode()); basAgv.setPower(this.getPower()); basAgv.setStatus(Cools.isEmpty(this.getAgvStatus())?0:Integer.parseInt(this.getAgvStatus().id)); basAgv.setFault(this.getFault()); basAgv.setTaskCode(this.getTaskcode()); basAgv.setEndLoc(this.getPoint_Number()); basAgv.setAction(Cools.isEmpty(this.getActionType())?0:Integer.parseInt(this.getActionType())); basAgv.setModiTime(new Date()); return basAgv; } } src/main/java/com/zy/common/model/agv/AgvResult.java
New file @@ -0,0 +1,25 @@ package com.zy.common.model.agv; import lombok.Data; /** * Created by vincent on 2021/5/19 */ @Data public class AgvResult { // 反馈结果 private Boolean Result; // 结果说明 private String Explain; public AgvResult() { } public AgvResult(Boolean result, String explain) { Result = result; Explain = explain; } } src/main/java/com/zy/common/model/enums/AgvStatusType.java
New file @@ -0,0 +1,51 @@ package com.zy.common.model.enums; public enum AgvStatusType { GOTO_TAKE("01", "前往取货点"), TAKING("02", "取货点取货"), GOTO_PUT("03", "前往送货点"), PUTTING("04", "送货点放货"), COMPLETE("05", "任务完成"), IDLE("06", "空闲"), ERROR("07", "故障"), CHARGING("08", "充电中"), HANDLE("09", "手动"), DISPLAY("010", "失联"), SOS("011" , "离线"), WORKING("012", "行走"), ; public String id; public String desc; AgvStatusType(String id, String desc) { this.id = id; this.desc = desc; } public static AgvStatusType get(String id) { if (null == id) { return null; } for (AgvStatusType type : AgvStatusType.values()) { if (type.id.equals(id)) { return type; } } return null; } public static AgvStatusType get(AgvStatusType type) { if (null == type) { return null; } for (AgvStatusType agvStatusType : AgvStatusType.values()) { if (agvStatusType == type) { return agvStatusType; } } return null; } } src/main/java/com/zy/common/utils/HttpHandler.java
New file @@ -0,0 +1,213 @@ package com.zy.common.utils; import okhttp3.*; import java.io.IOException; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; /** * Http协议客户端 * @author luxiaotao * @date 2018-9-27 */ public class HttpHandler { private static final Integer DEFAULT_TIMEOUT_SECONDS = 5; private static final MediaType MEDIA_TYPE = MediaType.parse("application/json;charset=utf-8"); private String uri; private String path; private String json; private Map<String, Object> params; private Map<String, Object> headers; private boolean https; private Integer timeout; private TimeUnit timeUnit; public HttpHandler(Builder builder){ this.uri = builder.uri; this.path = builder.path; this.json = builder.json; this.params = builder.params; this.headers = builder.headers; this.https = builder.https; this.timeout = builder.timeout; this.timeUnit = builder.timeUnit; } /** * GET请求执行 * @return the HttpHandler response */ public String doGet() throws IOException { String url = paramsToUrl(uri, path, params, https); Request.Builder headerBuilder = new Request.Builder(); if (headers != null && headers.size()>0){ for (Map.Entry<String, Object> entry : headers.entrySet()){ headerBuilder.addHeader(entry.getKey(), String.valueOf(entry.getValue())); } } Request request = headerBuilder.url(url).build(); Response response = getClient(timeout, timeUnit).newCall(request).execute(); return response.isSuccessful() ? response.body().string() : null; } /** * POST请求执行 * @return the HttpHandler response */ public String doPost() throws IOException { Request request; Request.Builder headerBuilder = new Request.Builder(); if (headers != null && headers.size()>0){ for (Map.Entry<String, Object> entry : headers.entrySet()){ headerBuilder.addHeader(entry.getKey(), String.valueOf(entry.getValue())); } } if (json == null || "".equals(json)){ FormBody.Builder builder = new FormBody.Builder(); for (Map.Entry<String, Object> entry : params.entrySet()){ builder.add(entry.getKey(), String.valueOf(entry.getValue())); } FormBody body = builder.build(); request = headerBuilder .url((https?"https://":"http://")+uri+path) .post(body) .build(); } else { RequestBody body = RequestBody.create(MEDIA_TYPE, json); Request.Builder builder = headerBuilder.url((https?"https://":"http://")+uri+path); builder.header("Content-Type", "application/json;charset=UTF-8"); request = builder.post(body).build(); } Call call = getClient(timeout, timeUnit).newCall(request); Response response = call.execute(); return response.body().string(); } /** * get请求参数拼接方法 * @return 请求行 */ private String paramsToUrl(String uri, String path, Map<String, Object> params, boolean isHttps) { StringBuilder res = new StringBuilder(); res.append(isHttps ? "https://" : "http://"); res.append(uri); if (path.length() > 0 && !(path.charAt(0) == '/')){ res.append("/"); } res.append(path); Optional.ofNullable(params).ifPresent( args -> { res.append("?"); args.forEach((key, value) -> { res.append(key); res.append("="); res.append(value); res.append("&"); }); } ); String url = res.toString(); if ("&".equals(url.substring(url.length()-1, url.length()))){ url = url.substring(0, url.length()-1); } return url; } /** * 获取 okHttpClient * @return the HttpHandler instance */ private OkHttpClient getClient(Integer timeout, TimeUnit timeUnit){ return new OkHttpClient .Builder() .connectTimeout(timeout, timeUnit) .readTimeout(timeout, timeUnit) .build(); } /** * Http协议报文建造者 */ public static class Builder { private String uri; private String path; private String json; private Map<String, Object> params; private Map<String, Object> headers; private boolean https; private Integer timeout; private TimeUnit timeUnit; { // 默认5s超时 timeout = DEFAULT_TIMEOUT_SECONDS; timeUnit = TimeUnit.SECONDS; path = ""; } /** * 建造器 * @return the HttpHandler instance */ public HttpHandler build(){ if (null == this.uri || "".equals(this.uri)){ throw new RuntimeException("uri is null"); } if (this.uri.startsWith("http://")){ this.uri = this.uri.substring(6,uri.length()); } else if (this.uri.startsWith("https://")){ this.uri = this.uri.substring(7,uri.length()); } return new HttpHandler(this); } public Builder setUri(String uri) { this.uri = uri; return this; } public Builder setPath(String path) { if (!path.startsWith("/")){ path = "/" + path; } this.path = path; return this; } public Builder setTimeout(Integer timeout, TimeUnit timeUnit) { this.timeout = timeout; this.timeUnit = timeUnit; return this; } public Builder setParams(Map<String, Object> params) { this.params = params; return this; } public Builder setHeaders(Map<String, Object> headers) { this.headers = headers; return this; } public Builder setHttps(boolean https) { this.https = https; return this; } public Builder setJson(String json) { this.json = json; return this; } } } src/main/resources/application.yml
@@ -8,7 +8,7 @@ name: @pom.build.finalName@ datasource: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://192.168.0.252:1433;databasename=xtyasrs url: jdbc:sqlserver://localhost:1433;databasename=xtyasrs username: sa password: sa@123 mvc: @@ -56,3 +56,6 @@ doubleLocs: 1,4,5,8,9,12 # 一个堆垛机负责的货架排数 groupCount: 4 agv: url: http://192.168.0.251:8088 src/main/webapp/views/pda/combAgv.html
@@ -30,7 +30,6 @@ <!-- 工作区 --> <div class="layui-inline" style="width: 100px; margin-left: 10px"> <select id="devpSelect"> <option value="">工作区</option> </select> </div> </div> @@ -81,12 +80,13 @@ // 获取工作区 function getDevp(){ $.ajax({ url: baseUrl+"/available/put/site", url: baseUrl+"/agv/put/site", headers: {'token': localStorage.getItem('token')}, method: 'POST', async: false, success: function (res) { if (res.code === 200){ $('#devpSelect').html(''); var tpl = $("#devpSelectTemplate").html(); var template = Handlebars.compile(tpl); var html = template(res); @@ -177,28 +177,28 @@ tips("请选择工作区", true); return; } alert("小车组托入库完成(功能还未实现)"); // $.ajax({ // url: baseUrl+"/mobile/comb/auth", // headers: {'token': localStorage.getItem('token')}, // data: JSON.stringify({ // barcode: barcode, // combMats: matData // }), // contentType:'application/json;charset=UTF-8', // method: 'POST', // async: false, // success: function (res) { // if (res.code === 200) { // reset(); // tips("组托成功") // } else if (res.code === 403) { // top.location.href = baseUrl + "/pda"; // } else { // tips(res.msg, true) // } // } // }) $.ajax({ url: baseUrl+"/mobile/comb/auth", headers: {'token': localStorage.getItem('token')}, data: JSON.stringify({ barcode: barcode, combMats: matData, agvSite: devp }), contentType:'application/json;charset=UTF-8', method: 'POST', async: false, success: function (res) { if (res.code === 200) { reset(); tips("组托成功") } else if (res.code === 403) { top.location.href = baseUrl + "/pda"; } else { tips(res.msg, true) } } }) } /** @@ -232,6 +232,7 @@ </script> <script type="text/template" id="devpSelectTemplate"> <option value="">工作区</option> {{#each data}} <option value="{{this}}">{{this}}</option> {{/each}}