Compare commits
14 Commits
e4d6de61ae
...
master_lz
| Author | SHA1 | Date | |
|---|---|---|---|
| cd1ba55b26 | |||
|
|
687be5840e | ||
|
|
d9f808ecc1 | ||
| c1d84aaf81 | |||
|
|
dec15eb913 | ||
| 18e014d9cb | |||
| 2cb03b146a | |||
|
|
7d48c78c9b | ||
|
|
c80dce6419 | ||
|
|
4802e11f7c | ||
|
|
fde2f18ff5 | ||
|
|
41e0b0b5b4 | ||
|
|
fb05360c5e | ||
| df9479f870 |
@@ -119,5 +119,10 @@
|
|||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-test</artifactId>
|
<artifactId>spring-test</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
<version>4.1.100.Final</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -155,5 +155,12 @@
|
|||||||
<artifactId>spring-test</artifactId>
|
<artifactId>spring-test</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
<version>4.1.100.Final</version> <!-- 使用较新稳定版本 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -21,5 +21,11 @@
|
|||||||
<groupId>top.wms</groupId>
|
<groupId>top.wms</groupId>
|
||||||
<artifactId>wms-common</artifactId>
|
<artifactId>wms-common</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.annotation</groupId>
|
||||||
|
<artifactId>javax.annotation-api</artifactId>
|
||||||
|
<version>1.3.2</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -18,5 +18,12 @@
|
|||||||
<groupId>top.wms</groupId>
|
<groupId>top.wms</groupId>
|
||||||
<artifactId>wms-common</artifactId>
|
<artifactId>wms-common</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.annotation</groupId>
|
||||||
|
<artifactId>javax.annotation-api</artifactId>
|
||||||
|
<version>1.3.2</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ public interface MaterialInfoService extends BaseService<MaterialInfoResp, Mater
|
|||||||
* */
|
* */
|
||||||
void uploadMaterialPhotos(MultipartFile file);
|
void uploadMaterialPhotos(MultipartFile file);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 称重时照片抓取
|
* 称重时照片抓取
|
||||||
* */
|
* */
|
||||||
|
|||||||
@@ -413,12 +413,11 @@ public class MaterialInfoServiceImpl extends BaseServiceImpl<MaterialInfoMapper,
|
|||||||
.forEach(code -> log.warn("物料编码 [{}] 不存在,照片更新失败", code));
|
.forEach(code -> log.warn("物料编码 [{}] 不存在,照片更新失败", code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String catchPhoto(MultipartFile file){
|
public String catchPhoto(MultipartFile file) {
|
||||||
String photoStoragePath = "catch" + DateUtil.today() + "/";
|
String photoStoragePath = "catch" + DateUtil.today() + "/";
|
||||||
FileInfo fileInfo = fileService.upload(file, photoStoragePath, null, true, true);
|
FileInfo fileInfo = fileService.upload(file, photoStoragePath, null, true, true);
|
||||||
CheckUtils.throwIfEmpty(fileInfo.getUrl(),"上传失败,请重新上传");
|
CheckUtils.throwIfNull(fileInfo, "照片上传失败");
|
||||||
return fileInfo.getUrl();
|
return fileInfo.getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,9 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 处理本地存储文件 URL
|
// 处理本地存储文件 URL
|
||||||
FileInfo fileInfo = uploadPretreatment.upload();
|
FileInfo fileInfo = null;
|
||||||
|
try {
|
||||||
|
fileInfo = uploadPretreatment.upload();
|
||||||
String domain = StrUtil.appendIfMissing(storage.getDomain(), StringConstants.SLASH);
|
String domain = StrUtil.appendIfMissing(storage.getDomain(), StringConstants.SLASH);
|
||||||
fileInfo.setUrl(URLUtil.normalize(domain + fileInfo.getPath() + fileInfo.getFilename()));
|
fileInfo.setUrl(URLUtil.normalize(domain + fileInfo.getPath() + fileInfo.getFilename()));
|
||||||
if (StrUtil.isNotBlank(fileInfo.getThFilename())) {
|
if (StrUtil.isNotBlank(fileInfo.getThFilename())) {
|
||||||
@@ -108,6 +110,9 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
|
|||||||
} else {
|
} else {
|
||||||
fileInfo.setThUrl(fileInfo.getUrl());
|
fileInfo.setThUrl(fileInfo.getUrl());
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
return fileInfo;
|
return fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,4 +44,14 @@ public class WorkOrderReq implements Serializable {
|
|||||||
@NotEmpty(message = "称重列表不能为空")
|
@NotEmpty(message = "称重列表不能为空")
|
||||||
private List<WorkOrderInfoReq> workOrderInfos;
|
private List<WorkOrderInfoReq> workOrderInfos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动填写的物料数量
|
||||||
|
*/
|
||||||
|
private String inputQuantity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电子秤重量
|
||||||
|
*/
|
||||||
|
private String ahDeviceWeight;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -79,9 +79,15 @@ public class WorkOrderResp extends BaseDetailResp {
|
|||||||
private String photoUrl;
|
private String photoUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 总重量
|
* 计算总重量(标重)
|
||||||
*/
|
*/
|
||||||
@Schema(description = "总重量")
|
@Schema(description = "计算总重量(标重)")
|
||||||
|
private BigDecimal totalCalculatedWeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际总重量
|
||||||
|
*/
|
||||||
|
@Schema(description = "实际总重量")
|
||||||
private BigDecimal totalWeight;
|
private BigDecimal totalWeight;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,13 +22,29 @@ public interface WorkOrderService extends BaseService<WorkOrderResp, WorkOrderRe
|
|||||||
* @param id 任务工单主键id
|
* @param id 任务工单主键id
|
||||||
* @return 任务工单详情信息
|
* @return 任务工单详情信息
|
||||||
*/
|
*/
|
||||||
List<WorkOrderInfoResp> getDetail(Long id);
|
WorkOrderResp getDetail(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建任务工单
|
* 创建任务工单
|
||||||
*
|
*
|
||||||
* @param req 创建任务工单参数
|
* @param req 创建任务工单参数
|
||||||
* @return 任务工单信息
|
* @return 任务工单信息
|
||||||
*/
|
*/
|
||||||
WorkOrderResp addWorKerOrder(WorkOrderReq req);
|
WorkOrderResp addWorKerOrder(WorkOrderReq req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取任务工单详情信息
|
||||||
|
*
|
||||||
|
* @param id 任务工单主键id
|
||||||
|
* @return 任务工单详情信息
|
||||||
|
*/
|
||||||
|
List<WorkOrderInfoResp> getWorkOrderInfos(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验物料数量和重量是否匹配
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int validateWeighing(WorkOrderReq req);
|
||||||
}
|
}
|
||||||
@@ -4,19 +4,20 @@ import cn.hutool.core.bean.BeanUtil;
|
|||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.date.DatePattern;
|
import cn.hutool.core.date.DatePattern;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||||
import top.continew.starter.extension.crud.service.BaseServiceImpl;
|
import top.continew.starter.extension.crud.service.BaseServiceImpl;
|
||||||
import top.wms.admin.common.context.UserContextHolder;
|
import top.wms.admin.common.context.UserContextHolder;
|
||||||
|
import top.wms.admin.material.mapper.MaterialInfoMapper;
|
||||||
|
import top.wms.admin.material.model.entity.MaterialInfoDO;
|
||||||
|
import top.wms.admin.system.service.ConfigService;
|
||||||
import top.wms.admin.weighManage.mapper.WorkOrderInfoMapper;
|
import top.wms.admin.weighManage.mapper.WorkOrderInfoMapper;
|
||||||
import top.wms.admin.weighManage.mapper.WorkOrderMapper;
|
import top.wms.admin.weighManage.mapper.WorkOrderMapper;
|
||||||
import top.wms.admin.weighManage.model.entity.WorkOrderDO;
|
import top.wms.admin.weighManage.model.entity.WorkOrderDO;
|
||||||
@@ -39,18 +40,21 @@ import java.util.List;
|
|||||||
* @since 2026/03/03 17:09
|
* @since 2026/03/03 17:09
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class WorkOrderServiceImpl extends BaseServiceImpl<WorkOrderMapper, WorkOrderDO, WorkOrderResp, WorkOrderResp, WorkOrderQuery, WorkOrderReq> implements WorkOrderService {
|
public class WorkOrderServiceImpl extends BaseServiceImpl<WorkOrderMapper, WorkOrderDO, WorkOrderResp, WorkOrderResp, WorkOrderQuery, WorkOrderReq> implements WorkOrderService {
|
||||||
|
|
||||||
private final WorkOrderInfoMapper workOrderInfoMapper;
|
private final WorkOrderInfoMapper workOrderInfoMapper;
|
||||||
|
|
||||||
|
private final MaterialInfoMapper materialInfoMapper;
|
||||||
|
|
||||||
|
private final ConfigService configService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResp<WorkOrderResp> page(WorkOrderQuery query, PageQuery pageQuery) {
|
public PageResp<WorkOrderResp> page(WorkOrderQuery query, PageQuery pageQuery) {
|
||||||
QueryWrapper<WorkOrderDO> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<WorkOrderDO> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.like(StrUtil.isNotBlank(query.getMaterialName()), "m.material_name", query.getMaterialName());
|
queryWrapper.like(StrUtil.isNotBlank(query.getMaterialName()), "m.material_name", query.getMaterialName());
|
||||||
queryWrapper.eq(null != query.getOrderNo(), "w.order_no", query.getOrderNo());
|
queryWrapper.eq(null != query.getOrderNo(), "w.order_no", query.getOrderNo());
|
||||||
queryWrapper.groupBy("w.id");
|
|
||||||
this.sort(queryWrapper, pageQuery);
|
this.sort(queryWrapper, pageQuery);
|
||||||
|
|
||||||
IPage<WorkOrderResp> page = baseMapper.selectWorkOrderPage(new Page<>(pageQuery.getPage(), pageQuery
|
IPage<WorkOrderResp> page = baseMapper.selectWorkOrderPage(new Page<>(pageQuery.getPage(), pageQuery
|
||||||
@@ -60,17 +64,38 @@ private final WorkOrderInfoMapper workOrderInfoMapper;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<WorkOrderInfoResp> getDetail(Long id) {
|
public WorkOrderResp getDetail(Long id) {
|
||||||
return baseMapper.getDetail(id);
|
WorkOrderDO workOrderDO = baseMapper.selectById(id);
|
||||||
|
WorkOrderResp workOrderResp = BeanUtil.copyProperties(workOrderDO, WorkOrderResp.class);
|
||||||
|
if (null != workOrderDO.getMaterialId()) {
|
||||||
|
MaterialInfoDO materialInfoDO = materialInfoMapper.selectById(workOrderDO.getMaterialId());
|
||||||
|
workOrderResp.setMaterialName(materialInfoDO.getMaterialName());
|
||||||
|
workOrderResp.setEncoding(materialInfoDO.getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<WorkOrderInfoDO> workOrderInfos = workOrderInfoMapper.selectList(new QueryWrapper<WorkOrderInfoDO>()
|
||||||
|
.eq("work_order_id", id));
|
||||||
|
if (CollUtil.isNotEmpty(workOrderInfos)) {
|
||||||
|
BigDecimal bigDecimal = new BigDecimal("0");
|
||||||
|
for (WorkOrderInfoDO workOrderInfoDO : workOrderInfos) {
|
||||||
|
bigDecimal = bigDecimal.add(workOrderInfoDO.getCalculatedWeight());
|
||||||
|
}
|
||||||
|
workOrderResp.setTotalCalculatedWeight(bigDecimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return workOrderResp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<WorkOrderInfoResp> getWorkOrderInfos(Long id) {
|
||||||
|
return baseMapper.getDetail(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterDelete(List<Long> ids) {
|
public void afterDelete(List<Long> ids) {
|
||||||
workOrderInfoMapper.delete(new QueryWrapper<WorkOrderInfoDO>().in("work_order_id", ids));
|
workOrderInfoMapper.delete(new QueryWrapper<WorkOrderInfoDO>().in("work_order_id", ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorkOrderResp addWorKerOrder(WorkOrderReq req) {
|
public WorkOrderResp addWorKerOrder(WorkOrderReq req) {
|
||||||
if (CollUtil.isEmpty(req.getWorkOrderInfos())) {
|
if (CollUtil.isEmpty(req.getWorkOrderInfos())) {
|
||||||
@@ -84,11 +109,13 @@ private final WorkOrderInfoMapper workOrderInfoMapper;
|
|||||||
totalCount += workOrderInfoReq.getQuantity();
|
totalCount += workOrderInfoReq.getQuantity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WorkOrderDO workOrder = new WorkOrderDO();
|
WorkOrderDO workOrder = new WorkOrderDO();
|
||||||
workOrder.setOrderNo(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN) + System.currentTimeMillis());
|
// 生成纯数字订单号:年月日时分秒 + 6位随机数
|
||||||
String title = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN) + "-"
|
String timestamp = DateUtil.format(new Date(), "yyyyMMddHHmmss");
|
||||||
+ UserContextHolder.getUsername() + "-" + req.getMaterialName();
|
String randomNum = String.format("%06d", (int)(Math.random() * 1000000));
|
||||||
|
workOrder.setOrderNo(timestamp + randomNum);
|
||||||
|
String title = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN) + "-" + UserContextHolder
|
||||||
|
.getUsername() + "-" + req.getMaterialName();
|
||||||
workOrder.setTitle(title);
|
workOrder.setTitle(title);
|
||||||
workOrder.setMaterialId(req.getMaterialId());
|
workOrder.setMaterialId(req.getMaterialId());
|
||||||
workOrder.setTotalWeight(totalWeight);
|
workOrder.setTotalWeight(totalWeight);
|
||||||
@@ -100,8 +127,40 @@ private final WorkOrderInfoMapper workOrderInfoMapper;
|
|||||||
workOrderInfoDOList.forEach(workOrderInfoDO -> workOrderInfoDO.setWorkOrderId(workOrder.getId()));
|
workOrderInfoDOList.forEach(workOrderInfoDO -> workOrderInfoDO.setWorkOrderId(workOrder.getId()));
|
||||||
workOrderInfoMapper.insertBatch(workOrderInfoDOList);
|
workOrderInfoMapper.insertBatch(workOrderInfoDOList);
|
||||||
|
|
||||||
|
|
||||||
return BeanUtil.copyProperties(workOrder, WorkOrderResp.class);
|
return BeanUtil.copyProperties(workOrder, WorkOrderResp.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int validateWeighing(WorkOrderReq req) {
|
||||||
|
log.info("validateWeighing req:{}", req);
|
||||||
|
String configValue = configService.getConfigValue("weight_float_ratio");
|
||||||
|
BigDecimal weightFloat = new BigDecimal("0.06");
|
||||||
|
if (StrUtil.isNotBlank(configValue)) {
|
||||||
|
weightFloat = new BigDecimal(configValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
//计算标准重量
|
||||||
|
MaterialInfoDO materialInfoDO = materialInfoMapper.selectById(req.getMaterialId());
|
||||||
|
BigDecimal standardWeight = materialInfoDO.getUnitWeight().subtract(new BigDecimal(req.getInputQuantity()));
|
||||||
|
BigDecimal electronicWeight = new BigDecimal(req.getAhDeviceWeight());
|
||||||
|
|
||||||
|
// 检查 electronicWeight 是否大于 standardWeight
|
||||||
|
if (electronicWeight.compareTo(standardWeight) <= 0) {
|
||||||
|
log.error("电子秤重量必须大于标准重量");
|
||||||
|
return 500; // 电子秤重量必须大于标准重量
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算比值:(electronicWeight - standardWeight) / standardWeight
|
||||||
|
BigDecimal weightDifference = electronicWeight.subtract(standardWeight);
|
||||||
|
BigDecimal ratio = weightDifference.divide(standardWeight, 4, BigDecimal.ROUND_HALF_UP);
|
||||||
|
|
||||||
|
// 检查比值是否超过 6%
|
||||||
|
if (ratio.compareTo(weightFloat) > 0) {
|
||||||
|
log.error("比值超过 6%,当前比值: {}", ratio);
|
||||||
|
return 500; // 比值超过 6%
|
||||||
|
}
|
||||||
|
|
||||||
|
return 200; // 验证通过
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,10 @@
|
|||||||
m.photo_url,
|
m.photo_url,
|
||||||
m.unit_weight,
|
m.unit_weight,
|
||||||
u.card_no,
|
u.card_no,
|
||||||
u.username createUserString,
|
u.username createUserString
|
||||||
count(*) as totalCount
|
|
||||||
from sys_work_order w
|
from sys_work_order w
|
||||||
left join sys_material_info m on w.material_id = m.id
|
left join sys_material_info m on w.material_id = m.id
|
||||||
left join sys_user u on w.create_user = u.id
|
left join sys_user u on w.create_user = u.id
|
||||||
left join sys_work_order_info wi on w.id = wi.work_order_id
|
|
||||||
${ew.customSqlSegment}
|
${ew.customSqlSegment}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,13 @@
|
|||||||
<artifactId>jakarta.websocket-api</artifactId>
|
<artifactId>jakarta.websocket-api</artifactId>
|
||||||
<version>2.1.0</version>
|
<version>2.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.hikvision</groupId>
|
||||||
|
<artifactId>MvCameraControlWrapper</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${project.basedir}/Library/MvCameraControlWrapper.jar</systemPath>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<finalName>${project.parent.name}</finalName>
|
<finalName>${project.parent.name}</finalName>
|
||||||
|
|||||||
50
wms-webapi/Library/JavaEnv.bat
Normal file
50
wms-webapi/Library/JavaEnv.bat
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
:: 检查java安装环境
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
:: 设置全局错误码
|
||||||
|
set /A errorcode=0
|
||||||
|
|
||||||
|
:: 开启扩展功能
|
||||||
|
setlocal ENABLEEXTENSIONS
|
||||||
|
setlocal ENABLEDELAYEDEXPANSION
|
||||||
|
|
||||||
|
::通过向临时文件写内容,判断是否有文件读写权限
|
||||||
|
echo > tmp
|
||||||
|
if exist tmp (
|
||||||
|
del /F/Q tmp >nul 2>&1
|
||||||
|
) else (
|
||||||
|
echo Please switch to an administrator account to run this batch!!!
|
||||||
|
set /A errorcode=1
|
||||||
|
goto:END
|
||||||
|
)
|
||||||
|
|
||||||
|
::判断JAVA_HOME是否被定义
|
||||||
|
if "%JAVA_HOME%"=="" (
|
||||||
|
echo JAVA_HOME not set. Please make sure that java is correctly installed.
|
||||||
|
set /A errorcode=2
|
||||||
|
goto:END
|
||||||
|
)
|
||||||
|
|
||||||
|
:: 判断CLASSPATH是否被定义
|
||||||
|
if "%CLASSPATH%"=="" (
|
||||||
|
echo CLASSPATH not set. Please make sure that java is correctly installed.
|
||||||
|
set /A errorcode=3
|
||||||
|
goto:END
|
||||||
|
)
|
||||||
|
|
||||||
|
:: 判断java版本是否比 1.7.0高,需先将版本信息重定向到文件中,再对文件内容进行分析
|
||||||
|
java -version >nul 2> JavaVer.tmp
|
||||||
|
for /F "tokens=1,2,3*" %%i in (JavaVer.tmp) do (
|
||||||
|
if "%%j" == "version" (
|
||||||
|
if %%k LSS "1.7.0" (
|
||||||
|
echo Java version is less than "1.7.0", warnings or errors may occur.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
del /F/Q JavaVer.tmp >nul 2>&1
|
||||||
|
|
||||||
|
:END
|
||||||
|
|
||||||
|
::返回错误码
|
||||||
|
exit /B %errorcode%
|
||||||
BIN
wms-webapi/Library/MvCameraControlWrapper.jar
Normal file
BIN
wms-webapi/Library/MvCameraControlWrapper.jar
Normal file
Binary file not shown.
@@ -74,12 +74,21 @@
|
|||||||
<version>2.9.2</version>
|
<version>2.9.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- pom.xml -->
|
<!-- WebSocket API -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jakarta.websocket</groupId>
|
<groupId>jakarta.websocket</groupId>
|
||||||
<artifactId>jakarta.websocket-api</artifactId>
|
<artifactId>jakarta.websocket-api</artifactId>
|
||||||
<version>2.1.0</version>
|
<version>2.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 本地库:MvCameraControlWrapper -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.hikvision</groupId>
|
||||||
|
<artifactId>MvCameraControlWrapper</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${project.basedir}/Library/MvCameraControlWrapper.jar</systemPath>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.dromara.x.file.storage.spring.EnableFileStorage;
|
import org.dromara.x.file.storage.spring.EnableFileStorage;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
|
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
|
||||||
@@ -29,6 +30,7 @@ import top.continew.starter.web.model.R;
|
|||||||
@RestController
|
@RestController
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@EnableScheduling
|
||||||
public class WmsAdminApplication {
|
public class WmsAdminApplication {
|
||||||
|
|
||||||
private final ProjectProperties projectProperties;
|
private final ProjectProperties projectProperties;
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import top.wms.admin.controller.weighManage.ah.ScaleWebSocketHandler;
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSocket
|
@EnableWebSocket
|
||||||
public class webSocket implements WebSocketConfigurer {
|
public class WebSocketConfig implements WebSocketConfigurer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||||
// 注册WebSocket端点,允许所有跨域请求
|
// 注册WebSocket端点,允许所有跨域请求
|
||||||
@@ -46,7 +46,7 @@ public class MaterialInfoController extends BaseController<MaterialInfoService,
|
|||||||
@GetMapping("/code/{code}")
|
@GetMapping("/code/{code}")
|
||||||
public MaterialInfoDO getMaterialInfoByCode(@PathVariable String code) {
|
public MaterialInfoDO getMaterialInfoByCode(@PathVariable String code) {
|
||||||
MaterialInfoDO materialInfoDO = baseService.getMaterialInfoByCode(code);
|
MaterialInfoDO materialInfoDO = baseService.getMaterialInfoByCode(code);
|
||||||
CheckUtils.throwIfEmpty(materialInfoDO,"未查询到相关物料信息");
|
CheckUtils.throwIfEmpty(materialInfoDO, "未查询到相关物料信息");
|
||||||
return materialInfoDO;
|
return materialInfoDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,10 +82,11 @@ public class MaterialInfoController extends BaseController<MaterialInfoService,
|
|||||||
baseService.uploadMaterialPhotos(zipFile);
|
baseService.uploadMaterialPhotos(zipFile);
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "照片抓取", description = "照片抓取")
|
@Operation(summary = "照片抓取", description = "照片抓取")
|
||||||
@PostMapping("/import/catch")
|
@PostMapping("/import/catch")
|
||||||
public String catchPhoto(@RequestParam("file")MultipartFile file) {
|
public String catchPhoto(@RequestParam("file") MultipartFile file) {
|
||||||
CheckUtils.throwIfEmpty(file,"照片抓取失败,请重新抓取");
|
CheckUtils.throwIfEmpty(file, "照片抓取失败,请重新抓取");
|
||||||
return baseService.catchPhoto(file);
|
return baseService.catchPhoto(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package top.wms.admin.controller.tcp.config;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
import top.wms.admin.controller.tcp.handler.TcpServerHandler;
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.*;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.codec.LineBasedFrameDecoder;
|
||||||
|
import io.netty.handler.codec.string.StringDecoder;
|
||||||
|
import io.netty.handler.codec.string.StringEncoder;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class NettyTcpServer {
|
||||||
|
|
||||||
|
@Value("${tcp.server.port:9005}")
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@Value("${tcp.server.boss-threads:1}")
|
||||||
|
private int bossThreads;
|
||||||
|
|
||||||
|
@Value("${tcp.server.worker-threads:4}")
|
||||||
|
private int workerThreads;
|
||||||
|
|
||||||
|
private EventLoopGroup bossGroup;
|
||||||
|
private EventLoopGroup workerGroup;
|
||||||
|
private Channel serverChannel;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void start() throws InterruptedException {
|
||||||
|
log.info("正在启动TCP服务端,端口: {}", port);
|
||||||
|
|
||||||
|
bossGroup = new NioEventLoopGroup(bossThreads);
|
||||||
|
workerGroup = new NioEventLoopGroup(workerThreads);
|
||||||
|
|
||||||
|
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||||
|
bootstrap.group(bossGroup, workerGroup)
|
||||||
|
.channel(NioServerSocketChannel.class)
|
||||||
|
.option(ChannelOption.SO_BACKLOG, 128)
|
||||||
|
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||||
|
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(SocketChannel ch) {
|
||||||
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
|
|
||||||
|
// 解决TCP粘包问题
|
||||||
|
pipeline.addLast(new LineBasedFrameDecoder(1024));
|
||||||
|
|
||||||
|
// 字符串编解码
|
||||||
|
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
|
||||||
|
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
|
||||||
|
|
||||||
|
// 关键修复:直接new,不要从Spring容器获取
|
||||||
|
pipeline.addLast(new TcpServerHandler());
|
||||||
|
|
||||||
|
log.debug("新连接接入,处理器已添加");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ChannelFuture future = bootstrap.bind(port).sync();
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
serverChannel = future.channel();
|
||||||
|
log.info("TCP服务端启动成功,端口: {}", port);
|
||||||
|
} else {
|
||||||
|
log.error("TCP服务端启动失败", future.cause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void stop() {
|
||||||
|
log.info("正在关闭TCP服务端...");
|
||||||
|
try {
|
||||||
|
if (serverChannel != null) {
|
||||||
|
serverChannel.close().sync();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error("关闭服务端通道时发生错误", e);
|
||||||
|
} finally {
|
||||||
|
if (bossGroup != null) {
|
||||||
|
bossGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
if (workerGroup != null) {
|
||||||
|
workerGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("TCP服务端已关闭");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package top.wms.admin.controller.tcp.config;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class SimpleRequestMatcher {
|
||||||
|
|
||||||
|
// 用队列存储响应
|
||||||
|
private final BlockingQueue<String> responseQueue = new LinkedBlockingQueue<>(1);
|
||||||
|
|
||||||
|
// 当前等待的请求标识
|
||||||
|
private volatile boolean isWaiting = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待响应
|
||||||
|
*/
|
||||||
|
public String waitForResponse(int timeoutSeconds) {
|
||||||
|
isWaiting = true;
|
||||||
|
try {
|
||||||
|
log.info("等待响应, 超时={}秒", timeoutSeconds);
|
||||||
|
// 从队列取响应,最多等待timeoutSeconds秒
|
||||||
|
String response = responseQueue.poll(timeoutSeconds, TimeUnit.SECONDS);
|
||||||
|
if (response != null) {
|
||||||
|
log.info("收到响应: {}", response);
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
log.error("等待响应超时");
|
||||||
|
return "TIMEOUT";
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error("等待响应被中断: {}", e.getMessage());
|
||||||
|
return "ERROR";
|
||||||
|
} finally {
|
||||||
|
isWaiting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收响应
|
||||||
|
*/
|
||||||
|
public void handleResponse(String response) {
|
||||||
|
try {
|
||||||
|
// 如果有请求在等待,把响应放入队列
|
||||||
|
if (isWaiting) {
|
||||||
|
responseQueue.offer(response);
|
||||||
|
log.info("响应接收成功: {}", response);
|
||||||
|
} else {
|
||||||
|
log.warn("没有正在等待的请求, 响应被丢弃: {}", response);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理响应失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空队列
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
responseQueue.clear();
|
||||||
|
isWaiting = false;
|
||||||
|
log.info("请求匹配器已清空");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package top.wms.admin.controller.tcp.handler;
|
||||||
|
|
||||||
|
import top.wms.admin.controller.tcp.config.SimpleRequestMatcher;
|
||||||
|
import top.wms.admin.controller.tcp.util.SpringContextUtil;
|
||||||
|
import top.wms.admin.controller.tcp.manager.ChannelManager;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class TcpServerHandler extends SimpleChannelInboundHandler<String> {
|
||||||
|
|
||||||
|
private ChannelManager channelManager;
|
||||||
|
private SimpleRequestMatcher requestMatcher;
|
||||||
|
|
||||||
|
private ChannelManager getChannelManager() {
|
||||||
|
if (channelManager == null) {
|
||||||
|
channelManager = SpringContextUtil.getBean(ChannelManager.class);
|
||||||
|
}
|
||||||
|
return channelManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleRequestMatcher getRequestMatcher() {
|
||||||
|
if (requestMatcher == null) {
|
||||||
|
requestMatcher = SpringContextUtil.getBean(SimpleRequestMatcher.class);
|
||||||
|
}
|
||||||
|
return requestMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getClientId(ChannelHandlerContext ctx) {
|
||||||
|
return ctx.channel().remoteAddress().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelActive(ChannelHandlerContext ctx) {
|
||||||
|
String clientId = getClientId(ctx);
|
||||||
|
getChannelManager().addChannel(clientId, ctx.channel());
|
||||||
|
log.info("✅ VM客户端连接成功: {}", clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelInactive(ChannelHandlerContext ctx) {
|
||||||
|
String clientId = getClientId(ctx);
|
||||||
|
getChannelManager().removeChannel(clientId);
|
||||||
|
log.info("❌ VM客户端断开连接: {}", clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
|
||||||
|
String clientId = getClientId(ctx);
|
||||||
|
log.info("📥 收到VM数据 [{}]: {}", clientId, msg);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 直接把收到的消息交给匹配器(不解析,不匹配)
|
||||||
|
String cleanMsg = msg.trim();
|
||||||
|
getRequestMatcher().handleResponse(cleanMsg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理消息失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
|
log.error("连接异常: {}", cause.getMessage());
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package top.wms.admin.controller.tcp.manager;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class ChannelManager {
|
||||||
|
|
||||||
|
// 存储所有VM客户端的连接
|
||||||
|
private final ConcurrentHashMap<String, Channel> channels = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 添加连接
|
||||||
|
public void addChannel(String clientId, Channel channel) {
|
||||||
|
channels.put(clientId, channel);
|
||||||
|
log.info("VM客户端 [{}] 已连接,当前在线客户端数: {}", clientId, channels.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除连接
|
||||||
|
public void removeChannel(String clientId) {
|
||||||
|
channels.remove(clientId);
|
||||||
|
log.info("VM客户端 [{}] 已断开,当前在线客户端数: {}", clientId, channels.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取第一个连接的VM(如果有多个VM,可以根据需要修改)
|
||||||
|
public Channel getFirstChannel() {
|
||||||
|
return channels.values().stream().findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据ID获取连接
|
||||||
|
public Channel getChannel(String clientId) {
|
||||||
|
return channels.get(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有VM连接
|
||||||
|
public boolean hasConnection() {
|
||||||
|
return !channels.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有连接
|
||||||
|
public ConcurrentHashMap<String, Channel> getAllChannels() {
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package top.wms.admin.controller.tcp.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
// 指令请求
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CommandRequest {
|
||||||
|
private String processId; // 要执行的流程ID,如 "PROCESS_1"
|
||||||
|
private String param; // 参数,如 "10.5"
|
||||||
|
private Integer priority; // 优先级(可选)
|
||||||
|
|
||||||
|
// 构建完整的指令字符串
|
||||||
|
public String buildCommand() {
|
||||||
|
// 格式:*PROCESS_1,10.5#
|
||||||
|
return String.format("*%s,%s#", processId, param);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package top.wms.admin.controller.tcp.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
// VM返回的结果
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class VMResult {
|
||||||
|
private String processId; // 流程ID
|
||||||
|
private String status; // OK 或 NG
|
||||||
|
private double value; // 测量值
|
||||||
|
private long timestamp; // 时间戳
|
||||||
|
|
||||||
|
public VMResult(String processId, String status, double value) {
|
||||||
|
this.processId = processId;
|
||||||
|
this.status = status;
|
||||||
|
this.value = value;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package top.wms.admin.controller.tcp.service;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import top.wms.admin.controller.tcp.manager.ChannelManager;
|
||||||
|
import top.wms.admin.controller.tcp.model.VMResult;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class CommandService {
|
||||||
|
|
||||||
|
// 统计
|
||||||
|
private final AtomicInteger successCount = new AtomicInteger(0);
|
||||||
|
private final AtomicInteger failCount = new AtomicInteger(0);
|
||||||
|
|
||||||
|
// 存储最近100条结果
|
||||||
|
private final ConcurrentLinkedQueue<VMResult> resultQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
// 处理VM返回的结果
|
||||||
|
public void processResult(String processId, String status, double value) {
|
||||||
|
VMResult result = new VMResult(processId, status, value);
|
||||||
|
|
||||||
|
// 更新统计
|
||||||
|
if ("OK".equalsIgnoreCase(status)) {
|
||||||
|
successCount.incrementAndGet();
|
||||||
|
} else {
|
||||||
|
failCount.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储结果
|
||||||
|
resultQueue.offer(result);
|
||||||
|
if (resultQueue.size() > 100) {
|
||||||
|
resultQueue.poll(); // 只保留最近100条
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("处理结果 - 流程: {}, 状态: {}, 数值: {}", processId, status, value);
|
||||||
|
|
||||||
|
// 这里可以添加你的业务逻辑,比如:
|
||||||
|
// 1. 存入数据库
|
||||||
|
// 2. 通过WebSocket推送给前端
|
||||||
|
// 3. 触发其他业务操作
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取统计信息
|
||||||
|
public String getStatistics() {
|
||||||
|
return String.format("成功: %d, 失败: %d, 总数: %d", successCount.get(), failCount.get(), successCount
|
||||||
|
.get() + failCount.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最新结果
|
||||||
|
public VMResult getLatestResult() {
|
||||||
|
return resultQueue.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChannelManager channelManager;
|
||||||
|
|
||||||
|
@Scheduled(cron = "*/1 * * * * ?")
|
||||||
|
public void sendAndWait() {
|
||||||
|
// 1. 检查连接
|
||||||
|
log.info("查询时间========");
|
||||||
|
Channel channel = channelManager.getFirstChannel();
|
||||||
|
channel.writeAndFlush("001");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package top.wms.admin.controller.tcp.util;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SpringContextUtil implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
|
SpringContextUtil.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> requiredType) {
|
||||||
|
return applicationContext.getBean(requiredType);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
package top.wms.admin.controller.vm;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.x.file.storage.core.FileInfo;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import top.continew.starter.core.validation.CheckUtils;
|
||||||
|
import top.wms.admin.controller.tcp.config.SimpleRequestMatcher;
|
||||||
|
import top.wms.admin.controller.tcp.manager.ChannelManager;
|
||||||
|
import top.wms.admin.system.service.FileService;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/vm")
|
||||||
|
public class VmCommandController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChannelManager channelManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SimpleRequestMatcher requestMatcher;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FileService fileService;
|
||||||
|
|
||||||
|
@GetMapping("/send")
|
||||||
|
public String sendAndWait(@RequestParam String msg) {
|
||||||
|
// 1. 检查连接
|
||||||
|
Channel channel = channelManager.getFirstChannel();
|
||||||
|
if (channel == null) {
|
||||||
|
return "ERROR: VM未连接";
|
||||||
|
}
|
||||||
|
String sendMsg = msg;
|
||||||
|
channel.writeAndFlush(sendMsg);
|
||||||
|
log.info("发送指令: {}", sendMsg);
|
||||||
|
// 3. 等待响应
|
||||||
|
String response = requestMatcher.waitForResponse(20);
|
||||||
|
CheckUtils.throwIf("TIMEOUT".equals(response), "响应超时,请重试");
|
||||||
|
if (StrUtil.equals(response, "success") || StrUtil.equals(response, "failed")) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
if (StrUtil.equals(response, msg)) {
|
||||||
|
response = "success";
|
||||||
|
} else {
|
||||||
|
response = "failed";
|
||||||
|
}
|
||||||
|
// 4. 返回结果
|
||||||
|
return response; // 直接返回VM的响应
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础路径
|
||||||
|
private static final String BASE_PATH = "C:/Users/14725/Desktop/material";
|
||||||
|
|
||||||
|
// 固定照片名称
|
||||||
|
private static final String PHOTO_NAME = "001.bmp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最新的001.bmp照片
|
||||||
|
*
|
||||||
|
* @return 图片文件
|
||||||
|
*/
|
||||||
|
/*@GetMapping("/latest-photo")
|
||||||
|
public ResponseEntity<byte[]> getLatestPhoto() {
|
||||||
|
try {
|
||||||
|
// 获取当前日期
|
||||||
|
LocalDate now = LocalDate.now();
|
||||||
|
String yearMonth = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
|
||||||
|
String day = now.format(DateTimeFormatter.ofPattern("dd"));
|
||||||
|
|
||||||
|
// 构建完整的文件路径
|
||||||
|
// 格式: C:/Users/14725/Desktop/material/202603/20260312/001.bmp
|
||||||
|
String filePath = String.format("%s/%s/%s%s/%s", BASE_PATH, yearMonth, yearMonth, day, PHOTO_NAME);
|
||||||
|
|
||||||
|
// 读取图片文件
|
||||||
|
Path imagePath = Paths.get(filePath);
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!Files.exists(imagePath)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件字节
|
||||||
|
byte[] imageBytes = Files.readAllBytes(imagePath);
|
||||||
|
|
||||||
|
// 设置响应头
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.valueOf("image/bmp"));
|
||||||
|
headers.setContentLength(imageBytes.length);
|
||||||
|
|
||||||
|
// 返回图片
|
||||||
|
return new ResponseEntity<>(imageBytes, headers, HttpStatus.OK);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
@GetMapping("/latest-photo")
|
||||||
|
public String getLatestPhoto() {
|
||||||
|
try {
|
||||||
|
// 获取当前日期
|
||||||
|
LocalDate now = LocalDate.now();
|
||||||
|
String yearMonth = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
|
||||||
|
String day = now.format(DateTimeFormatter.ofPattern("dd"));
|
||||||
|
|
||||||
|
// 构建完整的本地文件路径
|
||||||
|
// 格式: C:/Users/14725/Desktop/material/202603/20260312/001.bmp
|
||||||
|
String filePath = String.format("%s/%s/%s%s/%s", BASE_PATH, yearMonth, yearMonth, day, PHOTO_NAME);
|
||||||
|
|
||||||
|
// 读取图片文件
|
||||||
|
Path imagePath = Paths.get(filePath);
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!Files.exists(imagePath)) {
|
||||||
|
return null; // 或者抛出异常,根据业务需求决定
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将文件转换为MultipartFile
|
||||||
|
File file = imagePath.toFile();
|
||||||
|
FileInputStream input = new FileInputStream(file);
|
||||||
|
MultipartFile multipartFile = new MockMultipartFile(
|
||||||
|
file.getName(), // 文件名
|
||||||
|
file.getName(), // 原始文件名
|
||||||
|
"image/bmp", // 内容类型
|
||||||
|
input // 文件输入流
|
||||||
|
);
|
||||||
|
|
||||||
|
// 构建MinIO存储路径
|
||||||
|
String photoStoragePath = "catch" + DateUtil.today() + "/";
|
||||||
|
|
||||||
|
// 使用现有的fileService上传到MinIO
|
||||||
|
FileInfo fileInfo = fileService.upload(multipartFile, photoStoragePath, null, true, true);
|
||||||
|
|
||||||
|
// 检查上传结果
|
||||||
|
CheckUtils.throwIfNull(fileInfo, "照片上传失败");
|
||||||
|
|
||||||
|
// 关闭输入流
|
||||||
|
input.close();
|
||||||
|
return fileInfo.getUrl();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException("处理图片失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package top.wms.admin.controller.weighManage;
|
package top.wms.admin.controller.weighManage;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import top.continew.starter.extension.crud.enums.Api;
|
import top.continew.starter.extension.crud.enums.Api;
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||||
import top.continew.starter.log.annotation.Log;
|
import top.continew.starter.log.annotation.Log;
|
||||||
|
import top.continew.starter.web.model.R;
|
||||||
import top.wms.admin.common.controller.BaseController;
|
import top.wms.admin.common.controller.BaseController;
|
||||||
import top.wms.admin.weighManage.model.query.WorkOrderQuery;
|
import top.wms.admin.weighManage.model.query.WorkOrderQuery;
|
||||||
import top.wms.admin.weighManage.model.req.WorkOrderReq;
|
import top.wms.admin.weighManage.model.req.WorkOrderReq;
|
||||||
@@ -34,11 +36,17 @@ public class WorkOrderController extends BaseController<WorkOrderService, WorkOr
|
|||||||
|
|
||||||
@Log(ignore = true)
|
@Log(ignore = true)
|
||||||
@SaCheckPermission("workOrder:record:detail")
|
@SaCheckPermission("workOrder:record:detail")
|
||||||
@GetMapping(value = "/{id}")
|
@GetMapping(value = "/info/{id}")
|
||||||
public List<WorkOrderInfoResp> getDetail(@PathVariable("id") Long id) {
|
public List<WorkOrderInfoResp> getWorkOrderInfos(@PathVariable("id") Long id) {
|
||||||
return baseService.getDetail(id);
|
return baseService.getWorkOrderInfos(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Log(ignore = true)
|
||||||
|
@SaCheckPermission("workOrder:record:detail")
|
||||||
|
@GetMapping(value = "/{id}")
|
||||||
|
public WorkOrderResp getDetail(@PathVariable("id") Long id) {
|
||||||
|
return baseService.getDetail(id);
|
||||||
|
}
|
||||||
|
|
||||||
@SaCheckPermission("workOrder:record:add")
|
@SaCheckPermission("workOrder:record:add")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@@ -46,4 +54,23 @@ public class WorkOrderController extends BaseController<WorkOrderService, WorkOr
|
|||||||
return baseService.addWorKerOrder(req);
|
return baseService.addWorKerOrder(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/validateWeighing")
|
||||||
|
public R validateWeighing(@RequestBody WorkOrderReq req) {
|
||||||
|
if (null == req.getMaterialId()) {
|
||||||
|
return R.fail("400", "物料主键id不能为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(req.getInputQuantity())) {
|
||||||
|
return R.fail("400", "输入数量不能为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(req.getAhDeviceWeight())) {
|
||||||
|
return R.fail("400", "电子称重量不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
int validateResult = baseService.validateWeighing(req);
|
||||||
|
if (validateResult != 200) {
|
||||||
|
return R.fail("500", "重量不匹配!");
|
||||||
|
}
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ import java.util.regex.Pattern;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class AHDZCConnect {
|
public class AHDZCConnect {
|
||||||
|
|
||||||
private static final String PORT_NAME = "COM5";
|
private static final String PORT_NAME = "COM12";
|
||||||
private static final int BAUD_RATE = 9600;
|
private static final int BAUD_RATE = 9600;
|
||||||
private static final int DATA_BITS = 8;
|
private static final int DATA_BITS = 8;
|
||||||
private static final int STOP_BITS = 1;
|
private static final int STOP_BITS = 1;
|
||||||
@@ -52,10 +52,9 @@ public class AHDZCConnect {
|
|||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
// 项目启动时初始化并启动服务
|
// 项目启动时初始化并启动服务
|
||||||
if (false) {
|
|
||||||
ScaleService();
|
ScaleService();
|
||||||
start();
|
start();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spring.datasource:
|
|||||||
# 请务必提前创建好名为 wms_admin 的数据库,如果使用其他数据库名请注意同步修改 DB_NAME 配置
|
# 请务必提前创建好名为 wms_admin 的数据库,如果使用其他数据库名请注意同步修改 DB_NAME 配置
|
||||||
url: jdbc:p6spy:mysql://127.0.0.1:3306/wms?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
url: jdbc:p6spy:mysql://127.0.0.1:3306/wms?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||||
username: root
|
username: root
|
||||||
password: root
|
password: test123$
|
||||||
# PostgreSQL 配置
|
# PostgreSQL 配置
|
||||||
# url: jdbc:p6spy:mysql://192.168.2.30:${DB_PORT:3306}/continew?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
# url: jdbc:p6spy:mysql://192.168.2.30:${DB_PORT:3306}/continew?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||||
# username: ${DB_USER:root}
|
# username: ${DB_USER:root}
|
||||||
|
|||||||
Reference in New Issue
Block a user