Commit 4e22f397 zshaohui

1.增加云料仓看板功能

2.mimo增加呼叫agv
3.设备互联增加AGV,FUJINEOLINK,PANACIMNEOLINK类型
4.nexim功能提交
1 个父辈 10186043
正在显示 31 个修改的文件 包含 1149 行增加40 行删除
......@@ -116,6 +116,9 @@ public class MenuInit {
//Mimo临时看板
addDefaultFunctionMenu(0,null,"SMD BOX MIMO","SMDBOXMIMO", "smdBoxMimo/index","smdMimo");
//新的电子看板
addDefaultFunctionMenu(0,null,"云料仓看板","boxmimokanban", "boxmimokanban/index","smdMimo");
//电子看板
addDefaultFunctionMenu(1,null,"电子看板","eleckanban", "eleckanban/index","kanban");
......
package com.neotel.smfcore.core.agv;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.core.agv.bean.AgvInfo;
import com.neotel.smfcore.core.agv.util.AgvCache;
import com.neotel.smfcore.security.annotation.AnonymousAccess;
import io.swagger.annotations.Api;
......@@ -15,12 +14,32 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class AgvController {
@ApiOperation("呼叫agv")
@RequestMapping("/call")
@AnonymousAccess
public ResultBean call() {
boolean callAgv = AgvCache.getCallAgv();
if (callAgv){
return ResultBean.newErrorResult(-1,"smfcore.agv.already.call","已经呼叫");
}
AgvCache.updateCallAgv(true);
return ResultBean.newOkResult("");
}
@ApiOperation("定时请求agv是否呼叫")
@RequestMapping("/agvIsAlready")
@AnonymousAccess
public ResultBean agvIsAlready(){
return ResultBean.newOkResult(AgvCache.getCallAgv());
}
@ApiOperation("agv信息上传")
@RequestMapping("/agvInfoUpload")
@ApiOperation("完成呼叫")
@RequestMapping("/finishCall")
@AnonymousAccess
public ResultBean agvInfoUpload(@RequestBody AgvInfo agvInfo) {
AgvCache.updateAgvInfo(agvInfo);
public ResultBean finishCall(){
AgvCache.updateCallAgv(false);
return ResultBean.newOkResult("");
}
}
......@@ -23,5 +23,5 @@ public class AgvInfo {
/**
* 状态
*/
private String status;
private int status;
}
package com.neotel.smfcore.core.agv.util;
import com.google.common.collect.Maps;
import com.neotel.smfcore.core.agv.bean.AgvInfo;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class AgvCache {
public static Map<String, AgvInfo> agvInfoMap = Maps.newConcurrentMap();
public static boolean callAgv = false;
public static void updateAgvInfo(AgvInfo agvInfo){
String name = agvInfo.getName();
agvInfoMap.put(name,agvInfo);
public static void updateCallAgv(boolean isCall) {
callAgv = isCall;
}
public static Collection<AgvInfo> getAllAgvInfo(){
return agvInfoMap.values();
public static boolean getCallAgv() {
return callAgv;
}
}
......@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.core.agv.bean.AgvInfo;
import com.neotel.smfcore.core.agv.util.AgvCache;
import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.dashboard.bean.dto.seconddashboard.MachineStatusDto;
......@@ -12,10 +13,13 @@ import com.neotel.smfcore.core.dashboard.bean.dto.seconddashboard.UpcomingExpira
import com.neotel.smfcore.core.device.bean.BoxStatusBean;
import com.neotel.smfcore.core.device.bean.StatusBean;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.equipment.bean.EquipStatusBean;
import com.neotel.smfcore.core.equipment.enums.EquipmentType;
import com.neotel.smfcore.core.storage.service.manager.IStoragePosManager;
import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.util.DevicesStatusUtil;
import com.neotel.smfcore.core.system.util.EquipStatusUtil;
import com.neotel.smfcore.security.annotation.AnonymousAccess;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
......@@ -26,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
......@@ -134,14 +139,24 @@ public class SecondDashboardController {
@RequestMapping("agv")
@AnonymousAccess
public ResultBean agv() {
return ResultBean.newOkResult(AgvCache.getAllAgvInfo());
}
public static void main(String[] args) {
String dateStr = "2023-11-23 00:00:00";
DateTime dateTime = DateUtil.parse(dateStr, "yyyy-MM-dd HH:mm:ss");
long between = DateUtil.between(new Date(), dateTime, DateUnit.DAY,false);
System.out.println(between);
List<AgvInfo> agvCacheList = new ArrayList<>();
Collection<EquipStatusBean> allStatusBean = EquipStatusUtil.getAllStatusBean();
if (allStatusBean != null && !allStatusBean.isEmpty()) {
for (EquipStatusBean statusBean : allStatusBean) {
if (statusBean.getType().equals(EquipmentType.AGV.name())){
AgvInfo agvInfo = new AgvInfo();
agvInfo.setName(statusBean.getName());
if (statusBean.timeOut()){
agvInfo.setStatus(0);
} else {
agvInfo.setStatus(statusBean.getStatus());
}
agvInfo.setElec(statusBean.getData().get("elec").toString());
agvCacheList.add(agvInfo);
}
}
}
return ResultBean.newOkResult(agvCacheList);
}
}
......@@ -2,6 +2,7 @@ package com.neotel.smfcore.core.device.util;
import com.google.common.base.Strings;
import com.neotel.smfcore.common.exception.ValidateException;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.core.equipment.service.manager.IEquipmentManager;
import com.neotel.smfcore.core.equipment.service.po.Equipment;
import lombok.extern.slf4j.Slf4j;
......@@ -86,7 +87,7 @@ public class EquipmentCache {
}
}
public Equipment autoCreateEquip(String cid, String type) {
public Equipment autoCreateEquip(String name,String cid, String type) {
//判断cid存在
Equipment equipment = null;
equipment = getEquipment(cid);
......@@ -101,7 +102,11 @@ public class EquipmentCache {
equipment = new Equipment();
equipment.setCid(cid);
equipment.setType(type);
equipment.setName(cid);
if (StringUtils.isNotBlank(name)){
equipment.setName(name);
} else {
equipment.setName(cid);
}
equipment = equipmentManager.save(equipment);
reloadEquipment(equipment, "");
......
......@@ -17,6 +17,11 @@ public class EquipStatusBean implements Serializable {
private String cid;
/**
* 设备名称
*/
private String name;
/**
* 设备类型 ,NEOSCAN=扫码贴标,COUNTING=点料机
*/
private String type = EquipmentType.AUTO.name();
......@@ -87,7 +92,10 @@ public class EquipStatusBean implements Serializable {
}
public void addData(String key, String value) {
String oldValue = data.get(key).toString();
String oldValue = "";
if (data.get(key) != null){
oldValue = data.get(key).toString();
}
String valueStr = value;
if (!Strings.isNullOrEmpty(oldValue)) {
valueStr = valueStr + "|" + oldValue;
......@@ -95,10 +103,12 @@ public class EquipStatusBean implements Serializable {
data.put(key, valueStr);
}
public EquipMsg getMsgByType(String type){
for (EquipMsg msg : getMsgList()){
if(msg.getType().equals(type)){
return msg;
public EquipMsg getMsgByType(String type) {
if (msgList != null && !msgList.isEmpty()) {
for (EquipMsg msg : msgList) {
if (msg.getType().equals(type)) {
return msg;
}
}
}
return null;
......
......@@ -25,4 +25,20 @@ public enum EquipmentType {
* 3 插件机
*/
NEOSTATION(),
/**
* 4 FUJINEOLINK
*/
FUJINEOLINK(),
/**
* 5 PANACIMNEOLINK
*/
PANACIMNEOLINK(),
/**
* 6 AGV
*/
AGV()
}
package com.neotel.smfcore.core.equipment.handler.impl;
import cn.hutool.core.util.ObjectUtil;
import com.neotel.smfcore.core.agv.util.AgvCache;
import com.neotel.smfcore.core.equipment.bean.EquipMsg;
import com.neotel.smfcore.core.equipment.bean.EquipStatusBean;
import com.neotel.smfcore.core.equipment.enums.EquipmentType;
......@@ -19,6 +20,15 @@ public class BaseEquipHandler implements IEquipmentHandler {
statusBean.setClientIp(request.getRemoteHost());
handleMsg(statusBean);
//处理AGV调度
if (statusBean.getType().equals(EquipmentType.AGV.name())){
boolean callAgv = AgvCache.getCallAgv();
if (callAgv){
statusBean.addData("call","1");
} else {
statusBean.addData("call","0");
}
}
return statusBean;
}
/**
......
package com.neotel.smfcore.core.equipment.rest;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.core.device.util.EquipmentCache;
import com.neotel.smfcore.core.equipment.bean.EquipStatusBean;
import com.neotel.smfcore.core.equipment.enums.EquipmentType;
......@@ -41,15 +42,23 @@ public class EquipCommunicationController {
public EquipStatusBean communication(@RequestBody final EquipStatusBean statusBean, HttpServletRequest request) {
try {
String cid = statusBean.getCid();
String name = statusBean.getName();
Equipment equipment = equipmentCache.getEquipment(cid);
if (equipment == null) {
equipment = equipmentCache.autoCreateEquip(cid, statusBean.getType());
equipment = equipmentCache.autoCreateEquip(name,cid, statusBean.getType());
if (equipment != null) {
log.error("设备cid: [" + cid + "]不存在,自动创建设备完成");
} else {
log.error("设备cid: [" + cid + "]不存在,自动创建设备失败");
return null;
}
} else {
if (StringUtils.isNotBlank(name)){
if (!name.equals(equipment.getName())){
equipment.setName(name);
equipment = equipmentCache.reloadEquipment(equipment,equipment.getCid());
}
}
}
synchronized (equipment) {
String deviceType = equipment.getType();
......
package com.neotel.smfcore.core.equipment.rest;
import com.neotel.smfcore.core.device.bean.StatusBean;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.device.util.EquipmentCache;
import com.neotel.smfcore.core.equipment.bean.EquipMsg;
import com.neotel.smfcore.core.equipment.bean.EquipStatusBean;
......@@ -7,6 +9,9 @@ import com.neotel.smfcore.core.equipment.bean.EquipStatusDto;
import com.neotel.smfcore.core.equipment.service.po.Equipment; ;
import com.neotel.smfcore.core.message.enums.MessageType;
import com.neotel.smfcore.core.message.service.po.Message;
import com.neotel.smfcore.core.storage.enums.DeviceType;
import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.system.util.DevicesStatusUtil;
import com.neotel.smfcore.core.system.util.EquipStatusUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
......@@ -19,10 +24,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.*;
@Slf4j
......@@ -35,6 +37,9 @@ public class EquipViewController {
@Autowired
private EquipmentCache equipmentCache;
@Autowired
private DataCache dataCache;
@ApiOperation("获取看板数据")
@GetMapping
@PreAuthorize("@el.check('equipmentView:info')")
......@@ -72,8 +77,24 @@ public class EquipViewController {
// }
}
}
resultList.add(dto);
}
//再把NLL加进去
Collection<Storage> storages = dataCache.getAllStorage().values();
for (Storage storage : storages) {
if (storage.getType().equals(DeviceType.NLL.name())){
EquipStatusDto dto=new EquipStatusDto(storage.getId(),storage.getName(),storage.getCid(),false,0,"",storage.getType());
StatusBean bean = DevicesStatusUtil.getStatusBean(storage.getCid());
if (bean == null || bean.timeOut()){
dto.setOnLine(false);
} else {
dto.setOnLine(true);
}
resultList.add(dto);
}
}
return resultList;
}
}
......@@ -52,14 +52,24 @@ public class HumitureController {
Float maxHumidity = msdSettiings.getMaxHumidity();
Float minTemperature = msdSettiings.getMinTemperature();
Float minHumidity = msdSettiings.getMinHumidity();
List<String> cids = criteria.getCids();
String storageId = criteria.getStorageId();
if (StringUtils.isNotBlank(storageId)){
Storage storage = dataCache.getStorageById(storageId);
if (cids == null || cids.isEmpty()){
cids = new ArrayList<>();
}
cids.add(storage.getCid());
criteria.setCids(cids);
}
if (cids == null || cids.isEmpty()) {
criteria.setCids(SecurityUtils.getUserGroupCid());
}
Query query = QueryHelp.getQuery(criteria);
query.with(Sort.by(Sort.Direction.ASC, "createDate"));
query.addCriteria(Criteria.where("temperature").ne("0"));
query.addCriteria(Criteria.where("temperature").ne("0").exists(true));
PageData<Humiture> humitureList = humitureManager.findByPage(query, pageable);
HumitureDto restultDto = new HumitureDto();
restultDto.setMaxHumidity(maxHumidity);
......@@ -90,6 +100,16 @@ public class HumitureController {
Float maxTemperature = msdSettiings.getMaxTemperature();
Float maxHumidity = msdSettiings.getMaxHumidity();
List<String> cids = criteria.getCids();
String storageId = criteria.getStorageId();
if (StringUtils.isNotBlank(storageId)){
Storage storage = dataCache.getStorageById(storageId);
if (cids == null || cids.isEmpty()){
cids = new ArrayList<>();
}
cids.add(storage.getCid());
}
if (cids == null || cids.isEmpty()) {
criteria.setCids(SecurityUtils.getUserGroupCid());
}
......
......@@ -25,4 +25,7 @@ public class HumitureQueryCriteria {
@QueryCondition(type = QueryCondition.Type.BETWEEN, propName = "createDate")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private BetweenData<Date> createDate;
private String storageId;
}
......@@ -6,6 +6,7 @@ import com.neotel.smfcore.core.message.util.DeviceMessageUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
......@@ -37,6 +38,13 @@ public class EquipStatusUtil {
statusMap.put(statusBean.getCid(), statusBean);
}
/**
* 获取所有的设备信息
* @return
*/
public static Collection<EquipStatusBean> getAllStatusBean(){
return statusMap.values();
}
public static void runTimer() {
try {
......
package com.neotel.smfcore.custom.nexim;
import com.neotel.smfcore.common.utils.FileUtil;
import com.neotel.smfcore.common.utils.SmbUtil;
import com.neotel.smfcore.core.api.listener.BaseSmfApiListener;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.custom.nexim.bean.Notify;
import com.neotel.smfcore.custom.nexim.enums.Action;
import com.neotel.smfcore.custom.nexim.enums.NexObject;
import com.neotel.smfcore.custom.nexim.util.NotifyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.File;
@Service
@Slf4j
public class NeximApi extends BaseSmfApiListener {
private final String remoteFilePath = "smb://192.168.1.243/Test/";
@Override
public boolean isForThisApi(String apiName) {
return apiName != null && apiName.equalsIgnoreCase("nexim");
}
@Override
public void inTaskStatusChange(String inNotifyUrl, DataLog task) {
if (task.isFinished()) {
Notify notify = NotifyUtil.getNotifyByTask(Action.LOAD.name(), NexObject.CARRIER.name(), task);
//创建本地文件
String localFilePath = NotifyUtil.getLocalFilePathByNotify(notify);
boolean put = SmbUtil.smbPut(remoteFilePath, localFilePath);
log.info(localFilePath + "入库通知结果为:" + put);
FileUtil.del(new File(localFilePath));
}
}
@Override
public void outTaskStatusChange(String outNotifyUrl, DataLog task) {
if (task.isFinished()) {
Notify notify = NotifyUtil.getNotifyByTask(Action.PROVIDE.name(), NexObject.CARRIER.name(), task);
//创建本地文件
String localFilePath = NotifyUtil.getLocalFilePathByNotify(notify);
boolean put = SmbUtil.smbPut(remoteFilePath, localFilePath);
log.info(localFilePath + "出库通知结果为:" + put);
FileUtil.del(new File(localFilePath));
}
}
}
package com.neotel.smfcore.custom.nexim.bean;
import lombok.Data;
import java.io.Serializable;
@Data
public class Notify implements Serializable {
/**
* action NEW = Object (item or carrier) creation
* DELETE = Object (item or carrier) deletion
* LOAD = Carrier check in
* PROVIDE = Carrier check out
*/
private String action;
/**
* object ARTICLE or CARRIER
*/
private String object;
/**
* objectname Item or carrier name(DID)
*/
private String name;
/**
* Date of Action、yyyymmdd format
*/
private String date;
/**
* Action Time
*/
private String time;
/**
* Job name
*/
private String jobName;
/**
* storage location (Only if object=CARRIER)
*/
private String depot;
/**
* Item designation (Only if object=CARRIER)
*/
private String demand;
/**
* Current carrier's parts inventory or total inventory of the item
*/
private int stock;
}
\ No newline at end of file
package com.neotel.smfcore.custom.nexim.bean;
import lombok.Data;
@Data
public class OrderReq {
private String action;
private String object;
private String name;
private String date;
private String time;
private String depot;
private String stock;
private String expiry;
private String mSL;
private String mSLWATCH;
private String stockMin;
private String stockMax;
private String reelsMin;
private String reelsMax;
private String height;
private String diameter;
private String supply;
private String stockNew;
private String article;
private String units;
private String target;
private String forceTower;
private String demand;
private String los;
}
package com.neotel.smfcore.custom.nexim.bean;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class OrderResp extends OrderReq {
private String answer = "TRUE";
private int error;
private String carrier;
private String values;
private List<String> carrierNameList;
public void setCarrierNameList(String carrierName) {
if (carrierNameList == null || carrierNameList.isEmpty()){
carrierNameList = new ArrayList<>();
}
this.carrierNameList.add(carrierName);
}
}
package com.neotel.smfcore.custom.nexim.enums;
/**
* Action 类型
*/
public enum Action {
/**
* Object (item or carrier) creation
*/
NEW,
/**
* Object (item or carrier) deletion
*/
DELETE,
/**
* Carrier check in
*/
LOAD,
/**
* Carrier check out
*/
PROVIDE,
/**
* 修改
*/
UPDATE,
/**
* 读取
*/
READ
}
package com.neotel.smfcore.custom.nexim.enums;
public class ErrorCode {
public static final int ok = 0;
public static final int lockingError = 1;
public static final int unknownAction = 9;
}
package com.neotel.smfcore.custom.nexim.enums;
public enum NexObject {
/**
* 料号
*/
ARTICLE,
/**
* 唯一码
*/
CARRIER,
/**
* 工单
*/
JOBLIST
}
package com.neotel.smfcore.custom.nexim.enums;
public class OrderType {
public static final String order = "[Order]";
public static final String result = "[Result]";
public static final String data = "[Data]";
}
package com.neotel.smfcore.custom.nexim.order;
import com.google.common.base.Strings;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.common.exception.ValidateException;
import com.neotel.smfcore.common.utils.SecurityUtils;
import com.neotel.smfcore.core.api.SmfApi;
import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.device.enums.OP;
import com.neotel.smfcore.core.device.enums.OP_STATUS;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.order.LiteOrderCache;
import com.neotel.smfcore.core.order.enums.LITEORDER_STATUS;
import com.neotel.smfcore.core.order.enums.ORDER_COLOR;
import com.neotel.smfcore.core.order.service.manager.ILiteOrderItemManager;
import com.neotel.smfcore.core.order.service.manager.ILiteOrderManager;
import com.neotel.smfcore.core.order.service.po.LiteOrder;
import com.neotel.smfcore.core.order.service.po.LiteOrderItem;
import com.neotel.smfcore.core.storage.enums.CHECKOUT_TYPE;
import com.neotel.smfcore.core.storage.service.manager.IStoragePosManager;
import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.core.system.util.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Slf4j
@Service
public class OrderCheckOutService {
@Autowired
private LiteOrderCache liteOrderCache;
@Autowired
private ILiteOrderManager liteOrderManager;
@Autowired
private TaskService taskService;
@Autowired
private IStoragePosManager storagePosManager;
@Autowired
private SmfApi smfApi;
@Autowired
private DataCache dataCache;
@Autowired
private ILiteOrderItemManager liteOrderItemManager;
/**
* 执行工单出库
*/
public synchronized String checkOutLiteOrder(String orderNo, boolean outBom,boolean singleOut) {
LiteOrder cacheOrder = liteOrderCache.getLiteOrder(orderNo);;
if (cacheOrder == null) {
cacheOrder = liteOrderManager.findByOrderNo(orderNo);
}
if (cacheOrder == null) {
return "smfcore.order.out.notFound";
}
if ( !cacheOrder.isTaskFinished() && !cacheOrder.isNew()) {
log.info("工单[" + orderNo + "]正在执行");
return "smfcore.order.out.executing";
}
if(cacheOrder.isClosed()) {
log.info("工单[" + orderNo + "]已关闭,无法出库");
return "smfcore.order.hasClose";
}
ORDER_COLOR nextColor = getNextColor();
if (nextColor == null) {
log.info("执行工单[" + orderNo + "] outBom=" + outBom + "时,已达最大可执行工单数");
return "smfcore.order.out.maxOrder";
}
//先查找是否已经锁定过库位,如果已经锁定过,出锁定的库位
List<StoragePos> lockPosList = storagePosManager.findLockPos(cacheOrder.getOrderNo());
if(lockPosList!=null&& lockPosList.size()>0){
return checkOutOrder(cacheOrder).getMsgKey();
}
log.info("开始执行工单[" + orderNo + "] outBom=" + outBom);
cacheOrder.setTaskReelCount(0);
cacheOrder.setTaskFinishedTime(-1);
cacheOrder.setFinishedReelCount(0);
if (outBom) {
cacheOrder.setStatus(LITEORDER_STATUS.BOM);
} else {
cacheOrder.setStatus(LITEORDER_STATUS.TAILS);
}
//liteOrderMap.put(cacheOrder.getOrderNo(), cacheOrder);
int taskReelCount = 0;
CHECKOUT_TYPE checkoutType = dataCache.getCheckOutType();
List<String> availableStorageIds = dataCache.getAvailableStorageIds();
//其他出库模式一次性全部生成任务
for (LiteOrderItem orderItem : cacheOrder.getOrderItems()) {
orderItem.setOutNum(0);
orderItem.setOutReelCount(0);
liteOrderItemManager.save(orderItem);
//剩余未出数量
Float totalNum = orderItem.getNeedNum() * cacheOrder.getOrderTimes();
int remainNum = totalNum.intValue() - orderItem.getTotalOutNum();
//剩余未出盘数
int remainReelCount = orderItem.getNeedReelCount() - orderItem.getTotalOutReelCount();
//此PN未完成
if (remainNum > 0) {
if (outBom) {
//套料出库,设置剩余数量为1,这样就只会出一盘
remainNum = 1;
remainReelCount = 0;
}
int assignNum = 0;
int assignReelCount = 0;
while (assignNum < remainNum || assignReelCount < remainReelCount) {
Collection<String> excludePosIds = excludeOutPosIds();
String partNumber = orderItem.getPn();
String reelId = orderItem.getRi();
String mpn = orderItem.getMpn();
StoragePos pos = null;
if(!Strings.isNullOrEmpty(reelId)){
//RI
pos=storagePosManager.getByBarcode(reelId);
if(pos != null){
if(excludePosIds.contains(pos.getId())) {
log.info("工单[" + orderNo + "]RI出库,任务数[" + taskReelCount + "]出库位置仓位【" + pos.getPosName() + "】RI=[" + pos.getBarcode().getBarcode() + "]已在操作队列中,跳过不处理");
break;
}
}else{
log.info("工单[" + orderNo + "]RI出库时,库存中未找到料盘["+reelId+"]");
}
}else if (Strings.isNullOrEmpty(reelId) && !Strings.isNullOrEmpty(partNumber)){
//PN
pos=storagePosManager.findPartNumberInStorages(availableStorageIds,"", partNumber, excludePosIds, checkoutType,orderItem.getAppendData());
} else if (Strings.isNullOrEmpty(reelId) && Strings.isNullOrEmpty(partNumber) && !Strings.isNullOrEmpty(mpn)){
pos=storagePosManager.findMpnInStorages(availableStorageIds, mpn, excludePosIds, checkoutType,orderItem.getAppendData());
}
//如果库位没有料,随便找一盘进行出库
if (pos == null){
log.info("没有找到要出库的物料,随便出库一个物料");
Criteria c = Criteria.where("id").nin(excludePosIds).and("enabled").is(true)
.and("barcode.lockId").is(null)
.and("barcode").exists(true);
if (availableStorageIds != null && !availableStorageIds.isEmpty()){
c = c.and("storageId").in(availableStorageIds);
}
Query q = new Query(c);
pos=storagePosManager.findOne(q);
}
if (pos == null) {
// log.error("未找到可以出库的物料[" + partNumber + "]");
break;
} else {
assignNum = assignNum + pos.getBarcode().getAmount();
assignReelCount = assignReelCount + 1;
taskReelCount = taskReelCount + 1;
log.info("工单[" + orderNo + "],任务数[" + taskReelCount + "]出库位置仓位【" + pos.getPosName() + "】RI=[" + pos.getBarcode().getBarcode() + "] PN=[" + partNumber + "] num:" + pos.getBarcode().getAmount());
DataLog task = newTask(pos) ;
task.setSourceId(cacheOrder.getId());
task.setSourceName(cacheOrder.getOrderNo());
task.setSubSourceId(orderItem.getId());
task.setSubSourceInfo(orderItem.getFeederInfo());
task.setType(OP.CHECKOUT);
task.setLightColor(nextColor.getRgb());
task.setStatus(OP_STATUS.WAIT.name());
task.setSingleOut(singleOut);
// task = dataLogDao.save(task);
taskService.addTaskToExecute(task);
}
//如果是RI出库,只有一盘,出完就结束
if(!Strings.isNullOrEmpty(reelId)){
break;
}
}
}
}
cacheOrder.setTaskReelCount(taskReelCount);
cacheOrder.setTotalTaskReelCount(cacheOrder.getTotalTaskReelCount()+taskReelCount);
log.info("工单[" + orderNo + "]任务分配结束,任务数[" + taskReelCount + "]");
smfApi.onOrderStatusChange(cacheOrder);
//有需要出库的
if (taskReelCount <= 0) {
finishedOrderTasks(cacheOrder);
}
liteOrderManager.save(cacheOrder);
liteOrderCache.addOrderToMap(cacheOrder);
if (taskReelCount <= 0) {
//return "工单无可执行的任务";
return "smfcore.order.out.noTask";
}
return "";
}
public ResultBean checkOutOrder(LiteOrder liteOrder) throws ValidateException {
ORDER_COLOR nextColor = getNextColor();
if (nextColor == null) {
log.info("执行工单[" + liteOrder.getOrderNo() + "] 时,已达最大可执行工单数");
throw new ValidateException("order.out.maxOrder","已达最大可执行工单数");
}
//其他出库模式一次性全部生成任务
List<StoragePos> lockPosList = storagePosManager.findLockPos(liteOrder.getOrderNo());
if(lockPosList==null){
throw new ValidateException("smfcore.notFindPos","未找到锁定库位");
}
int taskReelCount = 0;
for (StoragePos lockPos : lockPosList) {
Storage storage = dataCache.getStorageById(lockPos.getStorageId());
Barcode barcode = lockPos.getBarcode();
DataLog task = new DataLog(storage,barcode,lockPos);
task.setSourceId(liteOrder.getId());
task.setSourceName(liteOrder.getOrderNo());
task.setSubSourceId(barcode.getLockName());
task.setSubSourceInfo(barcode.getLockName());
task.setType(OP.CHECKOUT);
task.setPutInDate(barcode.getPutInDate());
task.setLightColor(nextColor.getRgb());
task.setStatus(OP_STATUS.WAIT.name());
taskService.addTaskToExecute(task);
taskReelCount = taskReelCount + 1;
log.info("工单[" + liteOrder.getOrderNo() + "]出库位置仓位【" + task.getPosName() + "】RI=[" + task.getBarcode() + "] PN=[" + task.getPartNumber() + "] ");
}
liteOrder.setTaskReelCount(taskReelCount);
liteOrder.setTotalTaskReelCount(liteOrder.getTotalTaskReelCount()+taskReelCount);
liteOrder.setStatus(LITEORDER_STATUS.TAILS);
log.info("工单[" + liteOrder.getOrderNo() + "]任务分配结束,任务数[" + taskReelCount + "]");
smfApi.onOrderStatusChange(liteOrder);
if (taskReelCount <= 0) {
//没有任务,直接结束
finishedOrderTasks(liteOrder);
}else{
//有需要出库的 ,更新状态
liteOrder.setStatus(LITEORDER_STATUS.TAILS);
}
liteOrder = liteOrderManager.save(liteOrder);
liteOrderCache.addOrderToMap(liteOrder);
if (taskReelCount <= 0) {
return ResultBean.newErrorResult(-1,"smfcore.notask","No task in this order");
}
return ResultBean.newOkResult("smfcore.taskCount", "total task is :{0}",new String[]{ taskReelCount+""},"");
}
public ORDER_COLOR getNextColor() {
//设置颜色
Set<String> currentColors = new HashSet<>();
for (DataLog dataLog :taskService. getQueueTasks()) {
currentColors.add(dataLog.getLightColor());
}
ORDER_COLOR nextColor = ORDER_COLOR.nextColor(currentColors);
return nextColor;
}
/**
* 防止仓位任务重复,需要排除掉已经分配掉的出库仓位
*/
public Collection<String> excludeOutPosIds() {
//排除掉正在执行的仓位
List<DataLog> allTasks = taskService.getAllTasks();
Collection<String> operatingPosIds = new HashSet<>();
for (DataLog task : allTasks) {
if(task.isCheckOutTask()){
String posId = task.getPosId();
if (!Strings.isNullOrEmpty(posId)) {
operatingPosIds.add(task.getPosId());
}
}
}
return operatingPosIds;
}
/**
* 结束当前的任务
*/
public void finishedOrderTasks(LiteOrder liteOrder){
if(liteOrder.isOutOne()){
// setStatus(LITEORDER_STATUS.ONE_FINISHED);
liteOrder.setClosed(true);
}else if(liteOrder.isOutBom()){
liteOrder.setStatus(LITEORDER_STATUS.BOM_FINISHED);
}else if(liteOrder.isOutTails()){
// setStatus(LITEORDER_STATUS.TAILS_FINISHED);
liteOrder.setClosed(true);
}
liteOrder.setTaskFinishedTime(System.currentTimeMillis());
smfApi.onOrderStatusChange(liteOrder);
}
private DataLog newTask(StoragePos pos) {
Storage storage = dataCache.getStorageById(pos.getStorageId());
DataLog task = new DataLog(storage,pos.getBarcode(),pos);
String operator = SecurityUtils.getLoginUsername();
task.setOperator(operator);
return task;
}
}
package com.neotel.smfcore.custom.nexim.order;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.neotel.smfcore.common.utils.*;
import com.neotel.smfcore.core.barcode.service.manager.IBarcodeManager;
import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.device.enums.OP;
import com.neotel.smfcore.core.device.enums.OP_STATUS;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.order.LiteOrderCache;
import com.neotel.smfcore.core.order.service.manager.ILiteOrderManager;
import com.neotel.smfcore.core.order.service.po.LiteOrder;
import com.neotel.smfcore.core.order.service.po.LiteOrderItem;
import com.neotel.smfcore.core.storage.enums.CHECKOUT_TYPE;
import com.neotel.smfcore.core.storage.service.manager.IStoragePosManager;
import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.core.system.util.TaskService;
import com.neotel.smfcore.custom.nexim.bean.OrderReq;
import com.neotel.smfcore.custom.nexim.bean.OrderResp;
import com.neotel.smfcore.custom.nexim.enums.Action;
import com.neotel.smfcore.custom.nexim.enums.ErrorCode;
import com.neotel.smfcore.custom.nexim.enums.NexObject;
import com.neotel.smfcore.custom.nexim.enums.OrderType;
import com.neotel.smfcore.custom.nexim.util.OrderUtil;
import com.neotel.smfcore.security.annotation.AnonymousAccess;
import jcifs.smb.SmbFile;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Slf4j
@Component
public class OrderHandler {
private static IStoragePosManager storagePosManager;
@Autowired
public void setStoragePosManager(IStoragePosManager manager) {
storagePosManager = manager;
}
private static IBarcodeManager barcodeManager;
@Autowired
public void setBarcodeManager(IBarcodeManager manager) {
barcodeManager = manager;
}
private static DataCache dataCache;
@Autowired
public void setDataCache(DataCache cache) {
dataCache = cache;
}
private static TaskService taskService;
@Autowired
public void setTaskService(TaskService service) {
taskService = service;
}
private static ILiteOrderManager liteOrderManager;
@Autowired
public void setLiteOrderManager(ILiteOrderManager manager) {
liteOrderManager = manager;
}
private static LiteOrderCache liteOrderCache;
@Autowired
public void setLiteOrderCache(LiteOrderCache cache) {
liteOrderCache = cache;
}
private static OrderCheckOutService checkOutService;
@Autowired
public void setOrderCheckOutService(OrderCheckOutService service) {
checkOutService = service;
}
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
/*@PostConstruct
public void init() {
scheduledThreadPool.scheduleAtFixedRate(() -> {
try {
handler();
} catch (IOException e) {
e.printStackTrace();
}
}, 10, 5, TimeUnit.SECONDS);
}*/
public static void handler() throws IOException {
String path = dataCache.getOrderSetting().getOrderDir();
File file = new File(path);
if (file.exists() && file.isDirectory()) {
File[] files = file.listFiles();
List<File> fileList = Arrays.stream(files).sorted(Comparator.comparing(File::lastModified).reversed()).collect(Collectors.toList());
File currentFile = fileList.get(0);
List<String> lineList = OrderUtil.getLines(currentFile.getAbsolutePath(), StandardCharsets.US_ASCII);
String orderName = "";
List<LiteOrderItem> orderItemList = new ArrayList<>();
for (String line : lineList) {
if (line.split("=").length < 2) {
continue;
}
String value = line.split("=")[1];
if (line.startsWith("Name")) {
orderName = value;
}
if (line.startsWith("Item")) {
String[] itemStr = value.split("\\|", -1);
LiteOrderItem item = new LiteOrderItem();
item.setPn(itemStr[0]);
item.setRi(itemStr[1]);
int needNum = 1;
try {
needNum = Integer.valueOf(itemStr[2]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
item.setNeedNum(needNum);
int needReelCount = 1;
try {
needReelCount = Integer.valueOf(itemStr[3]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
item.setNeedReelCount(needReelCount);
orderItemList.add(item);
}
}
LiteOrder liteOrder = liteOrderManager.findByOrderNo(orderName);
if (liteOrder != null) {
return;
}
LiteOrder order = new LiteOrder();
order.setOrderItems(orderItemList);
order.setOrderNo(orderName);
liteOrderManager.createWithItems(order);
liteOrderCache.addOrderToMap(order);
checkOutService.checkOutLiteOrder(orderName, false, false);
}
}
}
package com.neotel.smfcore.custom.nexim.util;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.custom.nexim.bean.Notify;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
@Slf4j
public class NotifyUtil {
public static final String CHARSET_NAME = "ASCII";
public static Notify getNotifyByTask(String action,String obj, DataLog task){
Notify notify = new Notify();
notify.setAction(action);
notify.setObject(obj);
notify.setName(task.getBarcode());
String dateStr = DateUtil.format(new Date(), "yyyyMMdd HH:mm:ss");
String[] date = dateStr.split(" ");
notify.setDate(date[0]);
notify.setTime(date[1]);
if (StringUtils.isNotBlank(task.getSourceId())){
notify.setJobName(task.getSourceName());
} else {
notify.setJobName("");
}
notify.setDepot(task.getStorageName());
notify.setDemand(task.getPosName());
notify.setStock(task.getNum());
return notify;
}
/**
* 出入库通知,获取本地路径
* @param notify
* @return
*/
public static String getLocalFilePathByNotify(Notify notify) {
String localFilePath = notify.getName() + ".ETN";
log.info("本地文件路径为:" + localFilePath);
log.info("内容为:" + JSON.toJSONString(notify));
File file = new File(localFilePath); //创建一个文件
OutputStreamWriter out = null;
try {
out = new OutputStreamWriter(new FileOutputStream(file), CHARSET_NAME); //打开文件输出流
out.write("Action=" + notify.getAction());
out.write('\r');
out.write("Object=" + notify.getObject());
out.write('\r');
out.write("Name=" + notify.getName());
out.write('\r');
out.write("Date=" + notify.getDate());
out.write('\r');
out.write("Time=" + notify.getTime());
out.write('\r');
out.write("JOBNAME=" + notify.getJobName());
out.write('\r');
out.write("Depot=" + notify.getDepot());
out.write('\r');
out.write("Demand=" + notify.getDemand());
out.write('\r');
out.write("Stock=" + notify.getStock());
} catch (IOException e) {
e.printStackTrace();
} finally { //内容总执行
if (out != null) {
try {
out.close(); //关闭输出文件流
} catch (IOException el) {
}
}
}
return localFilePath;
}
}
package com.neotel.smfcore.custom.nexim.util;
import com.alibaba.fastjson.JSON;
import com.neotel.smfcore.common.utils.FileUtil;
import com.neotel.smfcore.common.utils.SmbUtil;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.custom.nexim.bean.Notify;
import com.neotel.smfcore.custom.nexim.bean.OrderReq;
import com.neotel.smfcore.custom.nexim.bean.OrderResp;
import com.neotel.smfcore.custom.nexim.enums.OrderType;
import jcifs.smb.SmbFile;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
public class OrderUtil {
//获取到远程最新的文件
public static SmbFile getLatestFile(String path) {
List<SmbFile> smbFileNameList = SmbUtil.smbFileList(path);
if (smbFileNameList != null && !smbFileNameList.isEmpty()) {
smbFileNameList = smbFileNameList.stream().sorted(Comparator.comparing(SmbFile::getDate).reversed()).collect(Collectors.toList());
return smbFileNameList.get(0);
}
return null;
}
/**
* 获取所有的line信息
* @param filePath
* @param cs
* @return
* @throws IOException
*/
public static List<String> getLines(String filePath, Charset cs) throws IOException {
Path path = Paths.get(filePath);
return Files.readAllLines(path, cs);
}
/**
* 获取本地路径
* @param localPath
* @param name
* @param lineList
* @param cs
* @return
*/
public static String getLocalFilePathByOrder(String localPath,String name,List<String> lineList,Charset cs) {
String localFilePath = "";
OutputStreamWriter out = null;
try {
localFilePath = localPath + name;
log.info("本地文件路径为:" + localFilePath);
File file = new File(localFilePath); //创建一个文件
File fileParent = file.getParentFile();
if (null != fileParent && !fileParent.exists()) {
fileParent.mkdirs();
}
out = new OutputStreamWriter(new FileOutputStream(file), cs); //打开文件输出流
for (int i = 0; i < lineList.size(); i++) {
String line = lineList.get(i);
out.write(line);
if ((i + 1) != lineList.size()) {
out.write('\r');
}
}
} catch (IOException e) {
localFilePath = "";
e.printStackTrace();
} finally { //内容总执行
if (out != null) {
try {
out.close(); //关闭输出文件流
} catch (IOException el) {
}
}
}
return localFilePath;
}
public static void uploadRemoteFilePath(List<String> lineList, String localRespFilePath, String writeRemoteFilePath, OrderResp resp) {
if (lineList != null && !lineList.isEmpty()) {
//开始写入本地文件
String localFilePath = OrderUtil.getLocalFilePathByOrder(localRespFilePath, resp.getName() + ".ANS", lineList, StandardCharsets.US_ASCII);
//同步到远程
if (StringUtils.isNotBlank(localFilePath)) {
SmbUtil.smbPut(writeRemoteFilePath, localFilePath);
//TODO 暂时不删除本地文件
//FileUtil.del(new File(localFilePath));
}
}
}
}
......@@ -373,4 +373,9 @@ smfcore.spbox.expireOut=\u8FC7\u671F\u7269\u6599\u51FA\u5E93
smfcore.humiture.codetemperature=\u51B7\u85CF\u533A\u6E29\u5EA6
smfcore.humiture.ntemperature=\u56DE\u6E29\u533A\u6E29\u5EA6
smfcore.cyclecount.executing=Cycle Count \u6B63\u5728\u6267\u884C
smfcore.eleckanban=\u7535\u5B50\u770B\u677F
\ No newline at end of file
smfcore.eleckanban=\u7535\u5B50\u770B\u677F
smfcore.agv.already.call=\u5DF2\u7ECF\u547C\u53EB
smfcore.agv.pickup=AGV\u5DF2\u88AB\u53EB\u6765\u53D6\u8D27
smfcore.agv.dispatch=\u8D27\u7269\u5DF2\u7ECF\u53D1\u9001\u5230\u5B58\u50A8\u7CFB\u7EDF
smfcore.agv.awaitingInstruction=\u7B49\u5F85\u6307\u793A
smfcore.agv.operation=AGV\u5728\u8FD0\u884C\u4E2D
\ No newline at end of file
......@@ -362,4 +362,10 @@ smfcore.spbox.expireOut=Expired solder paste out of storage
smfcore.humiture.codetemperature=Refrigeration zone temperature
smfcore.humiture.ntemperature=Return temperature zone temperature
smfcore.cyclecount.executing=Cycle Count are being executed
smfcore.eleckanban=DashBoard
\ No newline at end of file
smfcore.eleckanban=DashBoard
smfcore.agv.already.call=Already called
smfcore.agv.pickup=AGV has been called to pick up goods
smfcore.agv.dispatch=Goods dispatched to storage system
smfcore.agv.awaitingInstruction=Awaiting instruction
smfcore.agv.operation=AGV is in operation
\ No newline at end of file
......@@ -358,4 +358,10 @@ smfcore.spbox.backFail=\u56DE\u5E93\u9A8C\u8BC1\u5931\u8D25
smfcore.spbox.expireOut=\u8FC7\u671F\u7269\u6599\u51FA\u5E93
smfcore.humiture.codetemperature=\u51B7\u85CF\u533A\u6E29\u5EA6
smfcore.humiture.ntemperature=\u56DE\u6E29\u533A\u6E29\u5EA6
smfcore.cyclecount.executing=Cycle Count \u5B9F\u884C
\ No newline at end of file
smfcore.cyclecount.executing=Cycle Count \u5B9F\u884C
smfcore.agv.already.call=\u65E2\u306B\u547C\u3093\u3060
smfcore.agv.pickup=AGV\u304C\u5546\u54C1\u3092\u53D7\u3051\u53D6\u308B\u305F\u3081\u306B\u547C\u3073\u51FA\u3055\u308C\u307E\u3057\u305F
smfcore.agv.dispatch=\u4FDD\u7BA1\u30B7\u30B9\u30C6\u30E0\u306B\u767A\u9001\u3055\u308C\u308B\u5546\u54C1
smfcore.agv.awaitingInstruction=\u6307\u793A\u5F85\u3061
smfcore.agv.operation=AGV\u52D5\u4F5C\u4E2D\u3067\u3059
\ No newline at end of file
......@@ -358,4 +358,9 @@ smfcore.spbox.backFail=\u56DE\u5E93\u9A8C\u8BC1\u5931\u8D25
smfcore.spbox.expireOut=\u8FC7\u671F\u7269\u6599\u51FA\u5E93
smfcore.humiture.codetemperature=\u51B7\u85CF\u533A\u6E29\u5EA6
smfcore.humiture.ntemperature=\u56DE\u6E29\u533A\u6E29\u5EA6
smfcore.cyclecount.executing=Cycle Count \u6B63\u5728\u6267\u884C
\ No newline at end of file
smfcore.cyclecount.executing=Cycle Count \u6B63\u5728\u6267\u884C
smfcore.agv.already.call=\u5DF2\u7ECF\u547C\u53EB
smfcore.agv.pickup=AGV\u5DF2\u88AB\u53EB\u6765\u53D6\u8D27
smfcore.agv.dispatch=\u8D27\u7269\u5DF2\u7ECF\u53D1\u9001\u5230\u5B58\u50A8\u7CFB\u7EDF
smfcore.agv.awaitingInstruction=\u7B49\u5F85\u6307\u793A
smfcore.agv.operation=AGV\u5728\u8FD0\u884C\u4E2D
\ No newline at end of file
......@@ -359,4 +359,9 @@ smfcore.spbox.backFail=\u56DE\u5EAB\u9A57\u8B49\u5931\u6557
smfcore.spbox.expireOut=\u904E\u671F\u7269\u6599\u51FA\u5EAB
smfcore.humiture.codetemperature=\u51B7\u85CF\u533A\u6E29\u5EA6
smfcore.humiture.ntemperature=\u56DE\u6E29\u533A\u6E29\u5EA6
smfcore.cyclecount.executing=Cycle Count \u6B63\u5728\u57F7\u884C
\ No newline at end of file
smfcore.cyclecount.executing=Cycle Count \u6B63\u5728\u57F7\u884C
smfcore.agv.already.call=\u5DF2\u7D93\u547C\u7C72
smfcore.agv.pickup=AGV\u5DF2\u88AB\u53EB\u4F86\u53D6\u8CA8
smfcore.agv.dispatch=\u8CA8\u7269\u767C\u9001\u5230\u5B58\u5132\u7CFB\u7D71
smfcore.agv.awaitingInstruction=\u7B49\u5F85\u6307\u793A
smfcore.agv.operation=AGV\u6B63\u5728\u904B\u884C
\ No newline at end of file
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!