|
|
@ -8,21 +8,22 @@ import cc.yunxi.domain.po.*;
|
|
|
|
import cc.yunxi.domain.vo.device.*;
|
|
|
|
import cc.yunxi.domain.vo.device.*;
|
|
|
|
import cc.yunxi.domain.vo.file.FileUploadRespVO;
|
|
|
|
import cc.yunxi.domain.vo.file.FileUploadRespVO;
|
|
|
|
import cc.yunxi.service.*;
|
|
|
|
import cc.yunxi.service.*;
|
|
|
|
import cc.yunxi.utils.DeviceStatusManager;
|
|
|
|
import cc.yunxi.utils.CommandUtil;
|
|
|
|
import cc.yunxi.utils.RedisTool;
|
|
|
|
import cc.yunxi.utils.RedisTool;
|
|
|
|
import cc.yunxi.utils.UserContext;
|
|
|
|
import cc.yunxi.utils.UserContext;
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
import cn.hutool.http.HttpUtil;
|
|
|
|
|
|
|
|
import cn.hutool.json.JSONObject;
|
|
|
|
|
|
|
|
import cn.hutool.json.JSONUtil;
|
|
|
|
import cn.hutool.json.JSONUtil;
|
|
|
|
import io.swagger.annotations.Api;
|
|
|
|
import io.swagger.annotations.Api;
|
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
|
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
|
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
|
|
|
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
@ -44,8 +45,6 @@ import java.util.concurrent.TimeUnit;
|
|
|
|
@Validated
|
|
|
|
@Validated
|
|
|
|
public class DeviceController {
|
|
|
|
public class DeviceController {
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
private DeviceStatusManager deviceStatusManager;
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private IRecycleDeviceService deviceService;
|
|
|
|
private IRecycleDeviceService deviceService;
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
@ -61,11 +60,15 @@ public class DeviceController {
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private IRecyclerService recyclerService;
|
|
|
|
private IRecyclerService recyclerService;
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
private IRecycleStaffDeviceService staffDeviceService;
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
private IClientService clientService;
|
|
|
|
private IClientService clientService;
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private ICommonService commonService;
|
|
|
|
private ICommonService commonService;
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private RedisTool redisTool;
|
|
|
|
private RedisTool redisTool;
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
private CommandUtil commandUtil;
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private IFileService fileService;
|
|
|
|
private IFileService fileService;
|
|
|
@ -129,7 +132,8 @@ public class DeviceController {
|
|
|
|
//获取设备配置
|
|
|
|
//获取设备配置
|
|
|
|
@PostMapping("/conf")
|
|
|
|
@PostMapping("/conf")
|
|
|
|
@ApiOperation("机柜获取设备配置")
|
|
|
|
@ApiOperation("机柜获取设备配置")
|
|
|
|
public CommonResult<DeviceRespVO> conf(@RequestBody DeviceVO deviceVO) {
|
|
|
|
public CommonResult<DeviceRespVO> conf(@RequestBody DeviceVO deviceVO, HttpServletRequest request) {
|
|
|
|
|
|
|
|
|
|
|
|
CommonResult<DeviceRespVO> result = new CommonResult<>();
|
|
|
|
CommonResult<DeviceRespVO> result = new CommonResult<>();
|
|
|
|
DeviceRespVO respVO = new DeviceRespVO();
|
|
|
|
DeviceRespVO respVO = new DeviceRespVO();
|
|
|
|
RecycleDevice device = deviceService.getByDeviceCode(deviceVO.getDeviceCode());
|
|
|
|
RecycleDevice device = deviceService.getByDeviceCode(deviceVO.getDeviceCode());
|
|
|
@ -150,7 +154,7 @@ public class DeviceController {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
respVO.setDeviceCode(device.getDeviceCode());
|
|
|
|
respVO.setDeviceCode(device.getDeviceCode());
|
|
|
|
respVO.setDeviceName(device.getDeviceName());
|
|
|
|
respVO.setDeviceName(device.getDeviceName());
|
|
|
|
respVO.setUserStatus(device.getUseStatus());
|
|
|
|
respVO.setUseStatus(device.getUseStatus());
|
|
|
|
respVO.setCleanLock(Integer.valueOf(config.getCleanLock()));
|
|
|
|
respVO.setCleanLock(Integer.valueOf(config.getCleanLock()));
|
|
|
|
respVO.setUsePhoneLogin(config.getAllowPhoneLogin());
|
|
|
|
respVO.setUsePhoneLogin(config.getAllowPhoneLogin());
|
|
|
|
//二维码
|
|
|
|
//二维码
|
|
|
@ -334,9 +338,9 @@ public class DeviceController {
|
|
|
|
if (device == null) {
|
|
|
|
if (device == null) {
|
|
|
|
throw new BizIllegalException("登录失败:未查询到对应设备信息!");
|
|
|
|
throw new BizIllegalException("登录失败:未查询到对应设备信息!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
String recyclerPhone = device.getBeLongRecyclerPhone();
|
|
|
|
RecycleStaffDevice staffDevice = staffDeviceService.getByDeviceCode(loginReqVO.getDeviceCode(), loginReqVO.getPhone());
|
|
|
|
if (StrUtil.isEmpty(recyclerPhone) || !recyclerPhone.equals(loginReqVO.getPhone())) {
|
|
|
|
if (null == staffDevice) {
|
|
|
|
throw new BizIllegalException("登录失败:你不是当前设备的清运员或当前设备未分配清运员!");
|
|
|
|
throw new BizIllegalException("登录失败:你不是当前设备的管理人员!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//登录成功
|
|
|
|
//登录成功
|
|
|
@ -489,26 +493,27 @@ public class DeviceController {
|
|
|
|
//设备心跳上报
|
|
|
|
//设备心跳上报
|
|
|
|
@PostMapping("/heartbeat")
|
|
|
|
@PostMapping("/heartbeat")
|
|
|
|
@ApiOperation("设备心跳上报")
|
|
|
|
@ApiOperation("设备心跳上报")
|
|
|
|
public CommonResult<String> heartbeat(@RequestBody DeviceStatus des) {
|
|
|
|
public CommonResult<String> heartbeat(@RequestBody DeviceVO dev) {
|
|
|
|
deviceStatusManager.putDeviceStatus(des);
|
|
|
|
log.info("设备心跳上报:" + dev.getDeviceCode());
|
|
|
|
return CommonResult.success(des.getDeviceCode());
|
|
|
|
return CommonResult.success(dev.getDeviceCode());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ApiOperation("设备命令下发")
|
|
|
|
@ApiOperation("设备命令下发")
|
|
|
|
@PostMapping("/command")
|
|
|
|
@PostMapping("/command")
|
|
|
|
public CommonResult<String> command(@RequestBody CommandVO cmd) {
|
|
|
|
public CommonResult<String> command(@RequestBody CommandVO cmd) {
|
|
|
|
String deviceCode = cmd.getDeviceCode();
|
|
|
|
String deviceCode = cmd.getDeviceCode();
|
|
|
|
DeviceStatus status = deviceStatusManager.getDeviceStatus(deviceCode);
|
|
|
|
Object value = redisTool.getValue(deviceCode);
|
|
|
|
|
|
|
|
if (null == value) {
|
|
|
|
|
|
|
|
return CommonResult.error(400, "设备不在线");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceStatus status = JSONUtil.toBean(value.toString(), DeviceStatus.class);
|
|
|
|
// 判断设备是否在线
|
|
|
|
// 判断设备是否在线
|
|
|
|
Boolean online = status.getOnline();
|
|
|
|
Boolean online = status.getOnline();
|
|
|
|
if (!online) return CommonResult.error(400, "设备不在线");
|
|
|
|
if (!online) return CommonResult.error(400, "设备不在线");
|
|
|
|
// 下发指令
|
|
|
|
// 下发指令
|
|
|
|
String ip = status.getIp();
|
|
|
|
String ip = status.getIp();
|
|
|
|
Integer port = status.getPort();
|
|
|
|
CommonResult _result = commandUtil.sendCommand(cmd, ip);
|
|
|
|
String res = HttpUtil.post("http://" + ip + ":" + port + "/command", JSONUtil.toJsonStr(cmd), 1000);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 记录指令下发事件
|
|
|
|
// 记录指令下发事件
|
|
|
|
CommonResult _result = JSONUtil.toBean(res, CommonResult.class);
|
|
|
|
|
|
|
|
RecycleDeviceEvent event = new RecycleDeviceEvent();
|
|
|
|
RecycleDeviceEvent event = new RecycleDeviceEvent();
|
|
|
|
event.setDeviceCode(deviceCode);
|
|
|
|
event.setDeviceCode(deviceCode);
|
|
|
|
event.setBucketCode("");//
|
|
|
|
event.setBucketCode("");//
|
|
|
@ -534,49 +539,98 @@ public class DeviceController {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ApiOperation("获取设备状态")
|
|
|
|
@ApiOperation("获取设备状态")
|
|
|
|
@PostMapping("/status")
|
|
|
|
@PostMapping("/getStatus")
|
|
|
|
public CommonResult<String> command(@RequestBody DeviceVO deviceVO) {
|
|
|
|
public CommonResult<String> getStatus(@RequestBody DeviceVO deviceVO) {
|
|
|
|
String deviceCode = deviceVO.getDeviceCode();
|
|
|
|
Object value = redisTool.getValue(deviceVO.getDeviceCode());
|
|
|
|
CommandVO cmd = new CommandVO();
|
|
|
|
if (value == null) {
|
|
|
|
cmd.setCmd(CMDEnum.status);
|
|
|
|
return CommonResult.error(400, "获取失败,设备不在线!");
|
|
|
|
cmd.setDeviceCode(deviceCode);
|
|
|
|
}
|
|
|
|
cmd.setOptTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
|
|
|
DeviceStatus status = JSONUtil.toBean(value.toString(), DeviceStatus.class);
|
|
|
|
cmd.setRemark("获取设备状态");
|
|
|
|
CommandVO command = new CommandVO();
|
|
|
|
DeviceStatus status = deviceStatusManager.getDeviceStatus(deviceCode);
|
|
|
|
command.setCmd(CMDEnum.status);
|
|
|
|
// 判断设备是否在线
|
|
|
|
command.setDeviceCode(deviceVO.getDeviceCode());
|
|
|
|
Boolean online = status.getOnline();
|
|
|
|
command.setOptTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
|
|
|
if (!online) return CommonResult.error(400, "设备不在线");
|
|
|
|
command.setData("");
|
|
|
|
String ip = status.getIp();
|
|
|
|
command.setRemark("");
|
|
|
|
Integer port = status.getPort();
|
|
|
|
CommonResult commonResult = commandUtil.sendCommand(command, status.getIp());
|
|
|
|
String post = HttpUtil.post("http://" + ip + ":" + port + "/command", JSONUtil.toJsonStr(cmd), 1000);
|
|
|
|
Object data = commonResult.getData();
|
|
|
|
JSONObject json = JSONUtil.parseObj(post);
|
|
|
|
DeviceStatus status1 = JSONUtil.toBean(data.toString(), DeviceStatus.class);
|
|
|
|
Integer code = json.getInt("code");
|
|
|
|
deviceService.updateDevice(status1);
|
|
|
|
if (code == 200 || code == 201) {
|
|
|
|
status1.getBucketList().forEach(bucket -> {
|
|
|
|
String data = json.getStr("data");
|
|
|
|
bucketService.updateStatus(bucket);
|
|
|
|
if (StrUtil.isEmpty(data)) {
|
|
|
|
});
|
|
|
|
DeviceStatus deviceStatus = JSONUtil.toBean(data, DeviceStatus.class);
|
|
|
|
return CommonResult.success("获取成功!");
|
|
|
|
deviceStatusManager.putDeviceStatus(deviceStatus);
|
|
|
|
}
|
|
|
|
return CommonResult.success("状态已更新", "success");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
@ApiOperation("接受设备状态")
|
|
|
|
return CommonResult.error(400, "获取状态失败");
|
|
|
|
@PostMapping("/receiveStatus")
|
|
|
|
}
|
|
|
|
public CommonResult<String> receiveStatus(@RequestBody DeviceStatus des, HttpServletRequest request) {
|
|
|
|
|
|
|
|
String deviceCode = des.getDeviceCode();
|
|
|
|
|
|
|
|
String host = request.getRemoteHost();
|
|
|
|
|
|
|
|
des.setIp(host);//ip
|
|
|
|
|
|
|
|
des.setOnline(true);//在线
|
|
|
|
|
|
|
|
log.info("设备编码:{},状态上报:{},host:{}", deviceCode, JSONUtil.toJsonStr(des), host);
|
|
|
|
|
|
|
|
Object value = redisTool.getValue(deviceCode);
|
|
|
|
|
|
|
|
if (value == null) {
|
|
|
|
|
|
|
|
//更新设备状态
|
|
|
|
|
|
|
|
deviceService.updateDevice(des);
|
|
|
|
|
|
|
|
log.info("首次更新设备状态:{},详情:{}", des.getDeviceCode(), JSONUtil.toJsonStr(des));
|
|
|
|
|
|
|
|
//更新桶配置
|
|
|
|
|
|
|
|
des.getBucketList().forEach(bucket -> {
|
|
|
|
|
|
|
|
bucketService.updateStatus(bucket);
|
|
|
|
|
|
|
|
log.info("首次更新桶状态:{},详情:{}", bucket.getBucketCode(), JSONUtil.toJsonStr(bucket));
|
|
|
|
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
return CommonResult.error(400, json.getStr("msg"));
|
|
|
|
DeviceStatus status = JSONUtil.toBean(value.toString(), DeviceStatus.class);
|
|
|
|
|
|
|
|
//判断是否发生了改变
|
|
|
|
|
|
|
|
boolean changed = deviceService.isChanged(status, des);
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
|
|
|
|
deviceService.updateDevice(des);
|
|
|
|
|
|
|
|
log.info("更新设备状态:{},详情:{}", des.getDeviceCode(), JSONUtil.toJsonStr(des));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//更新设备状态
|
|
|
|
|
|
|
|
List<BucketStatus> oldList = status.getBucketList();
|
|
|
|
|
|
|
|
List<BucketStatus> newList = des.getBucketList();
|
|
|
|
|
|
|
|
for (BucketStatus now : newList) {
|
|
|
|
|
|
|
|
//判断是否在旧桶列表中
|
|
|
|
|
|
|
|
Optional<BucketStatus> first = oldList.stream()
|
|
|
|
|
|
|
|
.filter(old -> old.getBucketCode().equals(now.getBucketCode()))
|
|
|
|
|
|
|
|
.findFirst();
|
|
|
|
|
|
|
|
if (first.isPresent()) {
|
|
|
|
|
|
|
|
BucketStatus old = first.get();
|
|
|
|
|
|
|
|
boolean flag = bucketService.isChanged(old, now);
|
|
|
|
|
|
|
|
if (flag) {
|
|
|
|
|
|
|
|
bucketService.updateStatus(now);
|
|
|
|
|
|
|
|
log.info("更新桶状态:{},详情:{}", now.getBucketCode(), JSONUtil.toJsonStr(now));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
bucketService.updateStatus(now);
|
|
|
|
|
|
|
|
log.info("首次更新桶状态:{},详情:{}", now.getBucketCode(), JSONUtil.toJsonStr(now));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
redisTool.setValue(deviceCode, JSONUtil.toJsonStr(des), 1000 * 60 * 60l);
|
|
|
|
|
|
|
|
CommonResult<String> success = CommonResult.success(deviceCode);
|
|
|
|
|
|
|
|
success.setMsg("success");
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ApiOperation("接收实时重量")
|
|
|
|
@ApiOperation("接收实时重量")
|
|
|
|
@PostMapping("/weight")
|
|
|
|
@PostMapping("/weight")
|
|
|
|
public CommonResult<String> weight(@RequestBody WeightVO weight) {
|
|
|
|
public CommonResult<String> weight(@RequestBody WeightVO weight) {
|
|
|
|
DeviceStatus deviceStatus = deviceStatusManager.getDeviceStatus(weight.getDeviceCode());
|
|
|
|
Object value = redisTool.getValue(weight.getDeviceCode());
|
|
|
|
List<BucketStatus> bucketList = deviceStatus.getBucketList();
|
|
|
|
if (value != null) {
|
|
|
|
boolean flag = false;
|
|
|
|
DeviceStatus status = JSONUtil.toBean(value.toString(), DeviceStatus.class);
|
|
|
|
for (BucketStatus bucketStatus : bucketList) {
|
|
|
|
List<BucketStatus> bucketList = status.getBucketList();
|
|
|
|
if (bucketStatus.getBucketCode().equals(weight.getBucketCode()))
|
|
|
|
if (bucketList.size() > 0) {
|
|
|
|
flag = true;
|
|
|
|
Optional<BucketStatus> first = bucketList
|
|
|
|
}
|
|
|
|
.stream()
|
|
|
|
if (!flag) {
|
|
|
|
.filter(bucketStatus -> bucketStatus.getBucketCode().equals(weight.getBucketCode()))
|
|
|
|
CommonResult.error(400, "设备编码与桶编码不匹配");
|
|
|
|
.findFirst();
|
|
|
|
|
|
|
|
if (!first.isPresent()) {
|
|
|
|
|
|
|
|
CommonResult.error(400, "设备编码与桶编码不匹配");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
redisTool.setValue(weight.getBucketCode(), JSONUtil.toJsonStr(weight), 1000 * 60l);//一分钟过期
|
|
|
|
redisTool.setValue(weight.getBucketCode(), JSONUtil.toJsonStr(weight), 1000 * 60l);//一分钟过期
|
|
|
|
return CommonResult.success("success");
|
|
|
|
return CommonResult.success("success");
|
|
|
@ -585,24 +639,17 @@ public class DeviceController {
|
|
|
|
@ApiOperation("获取实时重量")
|
|
|
|
@ApiOperation("获取实时重量")
|
|
|
|
@PostMapping("/getWeight")
|
|
|
|
@PostMapping("/getWeight")
|
|
|
|
public CommonResult<WeightVO> getWeight(@RequestBody DeviceBucketVO deviceBucketVO) {
|
|
|
|
public CommonResult<WeightVO> getWeight(@RequestBody DeviceBucketVO deviceBucketVO) {
|
|
|
|
// DeviceStatus deviceStatus = deviceStatusManager.getDeviceStatus(deviceBucketVO.getDeviceCode());
|
|
|
|
long expire = redisTool.getKeyExpire(deviceBucketVO.getBucketCode(), TimeUnit.SECONDS);
|
|
|
|
// List<BucketStatus> bucketList = deviceStatus.getBucketList();
|
|
|
|
boolean exist = redisTool.existsKey(deviceBucketVO.getBucketCode());
|
|
|
|
// boolean flag = false;
|
|
|
|
if (!exist || expire <= 0) {
|
|
|
|
// for (BucketStatus bucketStatus : bucketList) {
|
|
|
|
|
|
|
|
// if (bucketStatus.getBucketCode().equals(deviceBucketVO.getBucketCode()))
|
|
|
|
|
|
|
|
// flag = true;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (!flag) {
|
|
|
|
|
|
|
|
// CommonResult.error(400, "设备编码与桶编码不匹配");
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
long keyExpire = redisTool.getKeyExpire(deviceBucketVO.getBucketCode(), TimeUnit.SECONDS);
|
|
|
|
|
|
|
|
if (keyExpire == -1) {
|
|
|
|
|
|
|
|
return CommonResult.error(400, "获取实时重量失败");
|
|
|
|
return CommonResult.error(400, "获取实时重量失败");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Object value = redisTool.getValue(deviceBucketVO.getBucketCode());
|
|
|
|
Object value = redisTool.getValue(deviceBucketVO.getBucketCode());
|
|
|
|
if (value != null) {
|
|
|
|
if (value != null) {
|
|
|
|
WeightVO weightVO = JSONUtil.toBean(value.toString(), WeightVO.class);
|
|
|
|
WeightVO weightVO = JSONUtil.toBean(value.toString(), WeightVO.class);
|
|
|
|
return CommonResult.success(weightVO);
|
|
|
|
CommonResult<WeightVO> success = CommonResult.success(weightVO);
|
|
|
|
|
|
|
|
success.setMsg("success");
|
|
|
|
|
|
|
|
return success;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
return CommonResult.error(400, "获取重量失败");
|
|
|
|
return CommonResult.error(400, "获取重量失败");
|
|
|
|
}
|
|
|
|
}
|
|
|
|