New file |
| | |
| | | import React, { useEffect, useRef, useState } from "react"; |
| | | import { getToken } from '@/utils/token-util' |
| | | import Http from '@/utils/http'; |
| | | import { Badge, Button, Descriptions, Drawer, Input, Card, Select, message } from 'antd'; |
| | | import { |
| | | PageContainer, |
| | | } from '@ant-design/pro-components'; |
| | | import './index.less' |
| | | import { WEBSOCKET_BASE_URL } from '@/config/setting'; |
| | | |
| | | const Main = () => { |
| | | const [deviceInfos, setDeviceInfos] = useState([]); |
| | | const [ws, setWs] = useState(null); |
| | | const [openOpera, setOpenOpera] = useState(false); |
| | | const [currentData, setCurrentData] = useState(null); |
| | | const [taskNo, setTaskNo] = useState(null); |
| | | const [staNo, setStaNo] = useState(null); |
| | | |
| | | useEffect(() => { |
| | | console.log('yes'); |
| | | connect(); |
| | | |
| | | return () => { |
| | | if (ws) { |
| | | ws.close(); |
| | | } |
| | | } |
| | | }, []) |
| | | |
| | | useEffect(() => { |
| | | console.log('yes'); |
| | | if (ws) { |
| | | ws.onopen = function () { |
| | | console.log("open"); |
| | | |
| | | sendWs(JSON.stringify({ |
| | | "url": "login", |
| | | "data": { |
| | | "token": getToken() |
| | | } |
| | | })) |
| | | } |
| | | |
| | | ws.onmessage = function (e) { |
| | | const result = JSON.parse(e.data); |
| | | if (result.url == "login") { |
| | | setInterval(function () { |
| | | getDeviceInfo(); |
| | | }, 1000) |
| | | } else if (result.url == "/conveyor/list") { |
| | | const data = JSON.parse(result.data); |
| | | setDeviceInfos(data); |
| | | } |
| | | } |
| | | |
| | | ws.onclose = function (e) { |
| | | console.log("close"); |
| | | reconnect(); |
| | | } |
| | | |
| | | ws.onerror = function (e) { |
| | | console.log(e); |
| | | } |
| | | } |
| | | }, [ws]); |
| | | |
| | | const connect = () => { |
| | | var newWs = new WebSocket(WEBSOCKET_BASE_URL + "/ws/conveyor/websocket"); |
| | | setWs(newWs); |
| | | } |
| | | |
| | | const reconnect = () => { |
| | | setTimeout(() => { |
| | | console.log('WebSocketClient: Attempting to reconnect...'); |
| | | connect(); |
| | | }, 3000); |
| | | } |
| | | |
| | | const sendWs = (message) => { |
| | | if (ws.readyState == WebSocket.OPEN) { |
| | | ws.send(message) |
| | | } |
| | | } |
| | | |
| | | const getDeviceInfo = () => { |
| | | sendWs(JSON.stringify({ |
| | | "url": "/conveyor/list", |
| | | "data": {} |
| | | })) |
| | | } |
| | | |
| | | const showOpera = (data) => { |
| | | setOpenOpera(true); |
| | | setCurrentData(data); |
| | | |
| | | setTaskNo(data.taskNo); |
| | | setStaNo(data.staNo); |
| | | }; |
| | | |
| | | const closeOpera = () => { |
| | | setOpenOpera(false); |
| | | }; |
| | | |
| | | const taskNoChange = (e) => { |
| | | setTaskNo(e.target.value) |
| | | } |
| | | |
| | | const staNoChange = (e) => { |
| | | setStaNo(e.target.value) |
| | | } |
| | | |
| | | const conveyorOperator = async (type) => { |
| | | try { |
| | | const resp = await Http.doPost('api/basConveyor/operator/sta', { |
| | | conveyorNo: currentData.conveyorNo, |
| | | siteNo: currentData.siteNo, |
| | | taskMode: type, |
| | | taskNo: taskNo, |
| | | staNo: staNo |
| | | }); |
| | | if (resp.code === 200) { |
| | | message.success("请求成功"); |
| | | return true; |
| | | } else { |
| | | message.warning(resp.msg); |
| | | return false; |
| | | } |
| | | } catch (error) { |
| | | message.warning("请求失败"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | let codeContent = (<PageContainer |
| | | header={{ |
| | | breadcrumb: {}, |
| | | }} |
| | | > |
| | | <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-around' }}> |
| | | {deviceInfos.map(item => { |
| | | if (item == null) { |
| | | return; |
| | | } |
| | | let tmpTitle = item.siteNo + "站" |
| | | let tmpData = [ |
| | | { |
| | | key: '1', |
| | | label: '任务号', |
| | | children: item.taskNo, |
| | | }, |
| | | { |
| | | key: '2', |
| | | label: '自动', |
| | | children: item.autoing, |
| | | }, |
| | | { |
| | | key: '3', |
| | | label: '有物', |
| | | children: item.loading, |
| | | }, |
| | | { |
| | | key: '4', |
| | | label: '可入', |
| | | children: item.inEnable, |
| | | }, |
| | | { |
| | | key: '5', |
| | | label: '可出', |
| | | children: item.outEnable, |
| | | }, |
| | | { |
| | | key: '6', |
| | | label: '能入', |
| | | children: item.canining, |
| | | }, |
| | | { |
| | | key: '7', |
| | | label: '能出', |
| | | children: item.canouting, |
| | | }, |
| | | { |
| | | key: '8', |
| | | label: '高低类型', |
| | | children: item.locType1$, |
| | | }, |
| | | { |
| | | key: '9', |
| | | label: '宽窄类型', |
| | | children: item.locType2$, |
| | | }, |
| | | { |
| | | key: '10', |
| | | label: '轻重类型', |
| | | children: item.locType3$, |
| | | }, |
| | | { |
| | | key: '11', |
| | | label: '工作模式', |
| | | children: item.workMode$, |
| | | }, |
| | | { |
| | | key: '12', |
| | | label: '目标站', |
| | | children: item.staNo, |
| | | }, |
| | | ]; |
| | | return <div key={item.id} style={{ width: '45%', marginBottom: '30px' }}> |
| | | <div style={{ marginBottom: '10px' }}> |
| | | <span style={{ marginRight: '10px' }}>{tmpTitle}</span> |
| | | <Button type="primary" onClick={() => showOpera(item)}>操作</Button> |
| | | </div> |
| | | <Descriptions size="small" bordered items={tmpData} /> |
| | | </div> |
| | | })} |
| | | </div> |
| | | </PageContainer>); |
| | | |
| | | if (currentData) { |
| | | codeContent = ( |
| | | <> |
| | | {codeContent} |
| | | <Drawer title="操作面板" onClose={closeOpera} open={openOpera}> |
| | | <h2 style={{ marginBottom: '15px' }}>{currentData.siteNo}站</h2> |
| | | |
| | | <div style={{ marginBottom: '15px' }}> |
| | | <Card title="相关指令" style={{ width: 300 }}> |
| | | <div className="commandItem"> |
| | | <Input value={taskNo} addonBefore="工作号" onChange={taskNoChange} /> |
| | | <Input value={staNo} addonBefore="目标站" onChange={staNoChange} /> |
| | | <Button type="primary" onClick={() => conveyorOperator('taskNoAndStaNo')}>保存</Button> |
| | | </div> |
| | | </Card> |
| | | </div> |
| | | </Drawer> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | return (codeContent); |
| | | } |
| | | |
| | | export default Main; |
New file |
| | |
| | | .commandItem { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .commandItem > Button { |
| | | width: 120px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .commandItem > div { |
| | | width: 120px; |
| | | margin-bottom: 10px; |
| | | } |
| | |
| | | colProps={{ md: 12, xl: 12 }} |
| | | /> |
| | | </ProForm.Group> |
| | | <ProForm.Group> |
| | | <ProFormText |
| | | name="staNo" |
| | | label="目标站" |
| | | colProps={{ md: 12, xl: 12 }} |
| | | /> |
| | | </ProForm.Group> |
| | | |
| | | </ProForm> |
| | | </Modal> |
| | |
| | | setSearchParam={setSearchParam} |
| | | />, |
| | | }, |
| | | { |
| | | title: '目标站', |
| | | dataIndex: 'staNo', |
| | | valueType: 'text', |
| | | hidden: false, |
| | | width: 140, |
| | | filterDropdown: (props) => <TextFilter |
| | | name='staNo' |
| | | {...props} |
| | | actionRef={actionRef} |
| | | setSearchParam={setSearchParam} |
| | | />, |
| | | }, |
| | | |
| | | { |
| | | title: '操作', |
| | |
| | | import com.zy.asrs.wcs.common.domain.BaseParam; |
| | | import com.zy.asrs.wcs.common.domain.KeyValVo; |
| | | import com.zy.asrs.wcs.common.domain.PageParam; |
| | | import com.zy.asrs.wcs.core.domain.param.ConveyorOperatorParam; |
| | | import com.zy.asrs.wcs.core.domain.param.ShuttleOperatorParam; |
| | | import com.zy.asrs.wcs.core.entity.BasConveyor; |
| | | import com.zy.asrs.wcs.core.entity.Motion; |
| | | import com.zy.asrs.wcs.core.entity.Task; |
| | | import com.zy.asrs.wcs.core.entity.TaskCtg; |
| | | import com.zy.asrs.wcs.core.model.enums.DeviceCtgType; |
| | | import com.zy.asrs.wcs.core.model.enums.TaskCtgType; |
| | | import com.zy.asrs.wcs.core.model.enums.TaskStsType; |
| | | import com.zy.asrs.wcs.core.service.BasConveyorService; |
| | | import com.zy.asrs.wcs.core.utils.Utils; |
| | | import com.zy.asrs.wcs.rcs.News; |
| | | import com.zy.asrs.wcs.rcs.cache.SlaveConnection; |
| | | import com.zy.asrs.wcs.rcs.entity.Device; |
| | | import com.zy.asrs.wcs.rcs.model.enums.LiftProtocolStatusType; |
| | | import com.zy.asrs.wcs.rcs.model.enums.SlaveType; |
| | | import com.zy.asrs.wcs.rcs.service.DeviceService; |
| | | import com.zy.asrs.wcs.rcs.thread.DevpThread; |
| | | import com.zy.asrs.wcs.rcs.thread.LiftThread; |
| | | import com.zy.asrs.wcs.rcs.thread.ShuttleThread; |
| | | import com.zy.asrs.wcs.system.controller.BaseController; |
| | | import com.zy.asrs.wcs.utils.ExcelUtil; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.*; |
| | | |
| | | @RestController |
| | | @RequestMapping("/api") |
| | |
| | | |
| | | @Autowired |
| | | private BasConveyorService basConveyorService; |
| | | @Autowired |
| | | private DeviceService deviceService; |
| | | |
| | | @PreAuthorize("hasAuthority('core:basConveyor:list')") |
| | | @PostMapping("/basConveyor/page") |
| | |
| | | ExcelUtil.build(ExcelUtil.create(basConveyorService.list(), BasConveyor.class), response); |
| | | } |
| | | |
| | | //手动操作 |
| | | @PreAuthorize("hasAuthority('core:basConveyor:operator')") |
| | | @PostMapping("/basConveyor/operator/sta") |
| | | @Transactional |
| | | public synchronized R conveyorOperator(@RequestBody ConveyorOperatorParam param) { |
| | | if (Cools.isEmpty(param.getConveyorNo(), param.getTaskMode())) { |
| | | return R.error("参数为空"); |
| | | } |
| | | |
| | | Integer conveyorNo = param.getConveyorNo(); |
| | | |
| | | Device device = deviceService.getOne(new LambdaQueryWrapper<Device>() |
| | | .eq(Device::getDeviceType, DeviceCtgType.CONVEYOR.val()) |
| | | .eq(Device::getStatus, 1) |
| | | .eq(Device::getDeviceNo, conveyorNo)); |
| | | if (device == null) { |
| | | return R.error("设备不存在"); |
| | | } |
| | | |
| | | DevpThread devpThread = (DevpThread) SlaveConnection.get(SlaveType.Conveyor, device.getId().intValue()); |
| | | if (devpThread == null) { |
| | | return R.error("设备离线"); |
| | | } |
| | | |
| | | if (param.getTaskMode().equals("taskNoAndStaNo")) { |
| | | //写入工作号和目标站 |
| | | if (Cools.isEmpty(param.getSiteNo())) { |
| | | return R.error("站点不存在"); |
| | | } |
| | | |
| | | boolean result = devpThread.writeWorkSta(param.getSiteNo(), Short.parseShort(param.getTaskNo()), param.getStaNo().shortValue()); |
| | | if (!result) { |
| | | return R.error("保存失败"); |
| | | } |
| | | return R.ok("保存成功"); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.zy.asrs.wcs.core.domain.param; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class ConveyorOperatorParam { |
| | | |
| | | // 输送线号 |
| | | private Integer conveyorNo; |
| | | |
| | | // 站点号 |
| | | private Integer siteNo; |
| | | |
| | | // 命令类型 |
| | | private String taskMode; |
| | | |
| | | // 工作号 |
| | | private String taskNo; |
| | | |
| | | // 目标站 |
| | | private Integer staNo; |
| | | |
| | | } |
| | |
| | | @ApiModelProperty(value= "工作模式") |
| | | private Integer workMode; |
| | | |
| | | /** |
| | | * 目标站 |
| | | */ |
| | | @ApiModelProperty(value= "目标站") |
| | | private Integer staNo; |
| | | |
| | | public BasConveyorSta() {} |
| | | |
| | | public BasConveyorSta(Long conveyorId,Integer conveyorNo,Long updateBy,Long createBy,Date createTime,Date updateTime,String memo,Integer deleted,Long hostId,Integer siteNo,String inEnable,String outEnable,String autoing,String loading,String canining,String canouting,Integer locType1,Integer locType2,Integer locType3,String locNo,String qrCodeValue) { |
| | |
| | | station.setInEnable(inEnable?"Y":"N"); |
| | | station.setOutEnable(outEnable?"Y":"N"); |
| | | station.setWorkMode(workMode); |
| | | station.setStaNo((int) staNo); |
| | | station.setLocType1(0); // 高低类型{0:未知,1:低库位,2:高库位} |
| | | station.setLocType2(0); // 宽窄类型{0:未知,1:窄库位,2:宽库位} |
| | | station.setLocType3(0); // 轻重类型{0:未知,1:轻库位,2:重库位} |
New file |
| | |
| | | package com.zy.asrs.wcs.rcs.ws; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.zy.asrs.framework.common.R; |
| | | import com.zy.asrs.framework.common.SpringUtils; |
| | | import com.zy.asrs.wcs.common.config.ConfigProperties; |
| | | import com.zy.asrs.wcs.common.security.JwtSubject; |
| | | import com.zy.asrs.wcs.core.entity.BasConveyorSta; |
| | | import com.zy.asrs.wcs.core.service.BasConveyorStaService; |
| | | import com.zy.asrs.wcs.rcs.ws.model.WebSocketMessage; |
| | | import com.zy.asrs.wcs.utils.JwtUtil; |
| | | import io.jsonwebtoken.Claims; |
| | | import lombok.Data; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.websocket.*; |
| | | import javax.websocket.server.ServerEndpoint; |
| | | import java.io.IOException; |
| | | import java.util.List; |
| | | import java.util.concurrent.CopyOnWriteArraySet; |
| | | |
| | | @Component |
| | | @Slf4j |
| | | @Service |
| | | @ServerEndpoint("/ws/conveyor/websocket") |
| | | @Data |
| | | public class ConveyorWebSocket { |
| | | |
| | | //客户端在线人数 |
| | | private static int onlineClient = 0; |
| | | |
| | | //客户端池 |
| | | private static CopyOnWriteArraySet<ConveyorWebSocket> webSocketServers = new CopyOnWriteArraySet<>(); |
| | | |
| | | private Session session; |
| | | |
| | | private String username; |
| | | |
| | | private Long hostId; |
| | | |
| | | //管道ID |
| | | private String sessionId; |
| | | |
| | | @OnOpen |
| | | public void onOpen(Session session) { |
| | | this.session = session; |
| | | this.sessionId = session.getId(); |
| | | |
| | | //将websocket对象进行保存 |
| | | webSocketServers.add(this); |
| | | //添加在线人数 |
| | | addOnlineClient(); |
| | | log.info("有新窗口开始监听:" + session.getId() + ",当前在线人数为:" + getOnlineClient()); |
| | | } |
| | | |
| | | /** |
| | | * 连接关闭调用的方法 |
| | | */ |
| | | @OnClose |
| | | public void onClose() { |
| | | webSocketServers.remove(this); //从set中删除 |
| | | subOnlineClient(); //在线数减1 |
| | | log.info("关闭的连接:" + sessionId); |
| | | log.info("有一连接关闭!当前在线人数为" + getOnlineClient()); |
| | | } |
| | | |
| | | /** |
| | | * 收到客户端消息后调用的方法 |
| | | * @ Param message 客户端发送过来的消息 |
| | | */ |
| | | @OnMessage |
| | | public void onMessage(String message, Session session) throws IOException { |
| | | BasConveyorStaService basConveyorStaService = SpringUtils.getBean(BasConveyorStaService.class); |
| | | WebSocketMessage socketMessage = JSON.parseObject(message, WebSocketMessage.class); |
| | | if (socketMessage.getUrl().equals("login")) { |
| | | try { |
| | | // 解析token |
| | | ConfigProperties configProperties = SpringUtils.getBean(ConfigProperties.class); |
| | | JSONObject data = JSON.parseObject(socketMessage.getData()); |
| | | Claims claims = JwtUtil.parseToken(data.getString("token"), configProperties.getTokenKey()); |
| | | JwtSubject jwtSubject = JwtUtil.getJwtSubject(claims); |
| | | this.username = jwtSubject.getUsername(); |
| | | this.hostId = jwtSubject.getHostId(); |
| | | socketMessage.setData(JSON.toJSONString(R.ok("auth success"))); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | socketMessage.setData(JSON.toJSONString(R.error("auth fail"))); |
| | | } |
| | | this.sendMessage(JSON.toJSONString(socketMessage)); |
| | | } else if (socketMessage.getUrl().equals("/conveyor/list")) { |
| | | if (this.hostId != null) { |
| | | // 根据输送线plc遍历 |
| | | List<BasConveyorSta> list = basConveyorStaService.list(); |
| | | socketMessage.setData(JSON.toJSONString(list)); |
| | | this.sendMessage(JSON.toJSONString(socketMessage)); |
| | | } |
| | | } |
| | | // log.info("收到来自连接:" + sessionId + "的信息:" + message); |
| | | } |
| | | |
| | | /** |
| | | * @ Param session |
| | | * @ Param error |
| | | */ |
| | | @OnError |
| | | public void onError(Session session, Throwable error) { |
| | | log.error("发生错误"); |
| | | error.printStackTrace(); |
| | | } |
| | | |
| | | /** |
| | | * 实现服务器主动推送 |
| | | */ |
| | | public void sendMessage(String message) throws IOException { |
| | | this.session.getBasicRemote().sendText(message); |
| | | } |
| | | |
| | | /** |
| | | * 服务器主动推送给指定用户 |
| | | */ |
| | | public void sendMessage(String message, String account) throws IOException { |
| | | for (ConveyorWebSocket item : webSocketServers) { |
| | | item.sendMessage(message); |
| | | } |
| | | } |
| | | |
| | | public void sendMessage(String message, int userId) throws IOException { |
| | | for (ConveyorWebSocket item : webSocketServers) { |
| | | item.sendMessage(message); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 服务器主动推送给指定用户 |
| | | */ |
| | | public static boolean sendMessageGlobal(String message, String account) throws IOException { |
| | | boolean tag = false; |
| | | for (ConveyorWebSocket item : webSocketServers) { |
| | | tag = true; |
| | | item.sendMessage(message); |
| | | } |
| | | return tag; |
| | | } |
| | | |
| | | public static boolean sendMessageGlobal(String message, int userId) throws IOException { |
| | | boolean tag = false; |
| | | for (ConveyorWebSocket item : webSocketServers) { |
| | | tag = true; |
| | | item.sendMessage(message); |
| | | } |
| | | return tag; |
| | | } |
| | | |
| | | public static synchronized int getOnlineClient() { |
| | | return onlineClient; |
| | | } |
| | | |
| | | public static synchronized void addOnlineClient() { |
| | | ConveyorWebSocket.onlineClient++; |
| | | } |
| | | |
| | | public static synchronized void subOnlineClient() { |
| | | if (ConveyorWebSocket.onlineClient > 0) { |
| | | ConveyorWebSocket.onlineClient--; |
| | | } |
| | | } |
| | | |
| | | } |