<template>
|
<div class="igbt-station-container">
|
<el-row :gutter="20" class="full-height">
|
<el-col :span="24">
|
<el-card shadow="hover" class="station-card">
|
<div class="card-header">
|
<div class="station-title">
|
<i class="el-icon-s-platform"></i>
|
<span class="head-font">工位编号: IGTB</span>
|
</div>
|
<el-button
|
type="primary"
|
icon="el-icon-search"
|
@click="openSerialDialog"
|
:class="{'is-connected': btnType === 'danger'}"
|
>
|
{{ btnType === 'danger' ? '串口已连接' : '串口连接' }}
|
</el-button>
|
</div>
|
|
<div class="content-section">
|
<div class="input-section">
|
<div class="input-with-label">
|
<label class="input-label">IGBT物料码:</label>
|
<el-input
|
v-model="igbtMateral"
|
placeholder="请扫描物料码"
|
class="material-input"
|
:class="{'is-scanning': isScanning}"
|
>
|
<template slot="append">
|
<i class="el-icon-scan"></i>
|
</template>
|
</el-input>
|
</div>
|
</div>
|
|
<div class="operation-status">
|
<div class="status-title">作业状态:</div>
|
<div class="status-content" :class="getStatusClass">
|
<i class="el-icon-info"></i>
|
{{operationProgress}}
|
</div>
|
</div>
|
</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
<el-dialog :title="title" :visible.sync="open1" width="1000" append-to-body>
|
<el-row
|
type="flex"
|
class="row-bg"
|
justify="center"
|
v-show="portsList.length === 0"
|
>
|
<el-col :span="7">
|
<div style="margin-top: 400px">
|
<span style="display: block">
|
仅支持Chrome 89+或者Edge 89+浏览器(安全上下文(HTTPS)中可用)
|
</span>
|
<el-button type="primary" @click="obtainAuthorization">授权</el-button>
|
</div>
|
</el-col>
|
</el-row>
|
<el-form
|
v-show="portsList.length > 0"
|
ref="form1"
|
:model="form1"
|
label-width="100px">
|
<el-row>
|
<el-col :span="24"
|
><div>
|
<el-form-item label="串口">
|
<el-select
|
v-model="form1.port"
|
filterable
|
placeholder="请选择串口"
|
:disabled="isDisable"
|
>
|
<el-option
|
v-for="item in portsList"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
>
|
</el-option>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="波特率">
|
<el-autocomplete
|
popper-class="my-autocomplete"
|
v-model="form1.baudRate"
|
:fetch-suggestions="querySearch"
|
placeholder="请输入波特率"
|
:disabled="isDisable"
|
>
|
<i class="el-icon-edit el-input__icon" slot="suffix"> </i>
|
<template slot-scope="{ item }">
|
<div class="name">{{ item.value }}</div>
|
<span class="addr">{{ item.address }}</span>
|
</template>
|
</el-autocomplete>
|
</el-form-item>
|
<el-form-item label="数据位">
|
<el-select
|
v-model="form1.dataBits"
|
placeholder="请选择数据位"
|
:disabled="isDisable"
|
>
|
<el-option label="7" value="7"></el-option>
|
<el-option label="8" value="8"></el-option>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="停止位">
|
<el-select
|
v-model="form1.stopBits"
|
placeholder="请选择停止位"
|
:disabled="isDisable"
|
>
|
<el-option label="1" value="1"></el-option>
|
<el-option label="2" value="2"></el-option>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="校验位">
|
<el-select
|
v-model="form1.parity"
|
placeholder="请选择校验位"
|
:disabled="isDisable"
|
>
|
<el-option label="None" value="none"></el-option>
|
<el-option label="Even" value="even"></el-option>
|
<el-option label="Odd" value="odd"></el-option>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="流控制">
|
<el-select
|
v-model="form1.flowControl"
|
placeholder="请选择流控制"
|
:disabled="isDisable"
|
>
|
<el-option label="None" value="none"></el-option>
|
<el-option label="HardWare" value="hardware"></el-option>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="显示历史">
|
<el-switch
|
v-model="form1.isShowHistory"
|
@change="loadHistory"
|
></el-switch>
|
<el-button
|
type="danger"
|
icon="el-icon-delete"
|
circle
|
title="清空历史"
|
@click="clearHistory"
|
></el-button>
|
</el-form-item>
|
<el-form-item label="发送区设置" v-show="isShowSendArea">
|
<el-form-item label="发送格式">
|
<el-radio-group v-model="form1.type">
|
<el-radio label="1">ASCII</el-radio>
|
<el-radio label="2">HEX</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
<el-form-item label="发送信息">
|
<el-input type="textarea" v-model="form1.sendMsg"></el-input>
|
</el-form-item>
|
<el-button type="primary" @click="sendCommon">发送</el-button>
|
</el-form-item>
|
|
<el-form-item>
|
<el-button :type="btnType" @click="connectBtn">{{
|
btnText
|
}}</el-button>
|
<el-button type="info" @click="obtainAuthorization"
|
>新增授权</el-button
|
>
|
</el-form-item>
|
</div>
|
</el-col>
|
</el-row>
|
</el-form>
|
</el-dialog>
|
</div>
|
</template>
|
<script>
|
import MySerialPort from "../../../../src/utils/MySerialPort";
|
import USBDevice from "../../../../src/utils/usb.json";
|
import {mesIssuePermissionToWork} from "../../../api/main/plc/mesToPlc";
|
|
|
export default {
|
name: "stationTerminal",
|
data() {
|
return {
|
operationProgress: '等待扫描物料码...',
|
url: "ws://192.168.10.105:8083/websocket/message/",
|
message: "",
|
text_content: "",
|
ws: null,
|
inputValue: '9',
|
igbtMateral: '',
|
open1: false,
|
keepReading: true,
|
form1: {
|
baudRate: "115200",
|
dataBits: "8",
|
stopBits: "1",
|
parity: "none",
|
flowControl: "none",
|
desc: "",
|
type: "1",
|
isShowHistory: false,
|
},
|
btnType: "primary",
|
btnText: "连接串口",
|
restaurants: [],
|
portsList: [],
|
isShowSendArea: false,
|
readType: 1,
|
title: "",
|
isConnected: false,
|
isScanning: false
|
}
|
},
|
beforeDestroy() {
|
this.exit();
|
},
|
created() {
|
this.initStation();
|
},
|
mounted() {
|
if ("serial" in navigator) {
|
this.myserialport = new MySerialPort();
|
this.getPorts();
|
navigator.serial.addEventListener("connect", (e) => {
|
this.$message.success("设备已连接");
|
this.getPorts();
|
});
|
navigator.serial.addEventListener("disconnect", (e) => {
|
this.$message.error("设备已断开");
|
});
|
this.restaurants = this.loadAll();
|
} else {
|
this.$message.error(
|
"当前为HTTP模式或者浏览器版本过低,不支持网页连接串口"
|
);
|
}
|
},
|
computed: {
|
isDisable() {
|
return this.btnType === "danger";
|
},
|
getStatusClass() {
|
if (this.operationProgress.includes('错误') || this.operationProgress.includes('失败')) {
|
return 'status-error'
|
} else if (this.operationProgress.includes('警告') || this.operationProgress.includes('注意')) {
|
return 'status-warning'
|
}
|
return 'status-normal'
|
}
|
},
|
methods: {
|
plcAllowWork(data){
|
mesIssuePermissionToWork({igbtMateral: data}).then(response => {
|
console.log(response.data)
|
});
|
},
|
openSerialDialog() {
|
this.open1 = true;
|
},
|
exit() {
|
if (this.ws) {
|
this.ws.close();
|
this.ws = null;
|
}
|
},
|
send() {
|
if (this.ws) {
|
this.ws.send(this.message);
|
} else {
|
alert("未连接到服务器");
|
}
|
},
|
|
initStation: async function () {
|
// this.conCom()
|
this.ws = new WebSocket(this.url+'IGBT');
|
const self = this;
|
this.ws.onopen = function (event) {
|
self.$message('websocket连接成功!');
|
};
|
this.ws.onmessage = function (event) {
|
console.log("收到数据:" + event.data)
|
if(event.data === '21'){
|
self.operationProgress = "采集数据推送完成"
|
self.igbtMateral = ''
|
setTimeout(() => {
|
self.operationProgress = "等待作业中"
|
}, 3000); // 5000毫秒 = 5秒
|
}
|
};
|
},
|
|
//接受数据的回调
|
callBack(value) {
|
if (this.form1.isShowHistory) this.form1.desc = this.readLi().join("");
|
else {
|
const scanValue = this.myserialport.hex2atostr(value).replace(/[\r\n]/g, '');
|
console.log("串口收到数据-------------------"+scanValue)
|
this.igbtMateral = scanValue
|
this.operationProgress = "扫码完成"
|
this.plcAllowWork(scanValue);
|
}
|
},
|
clearHistory() {
|
this.form1.desc = "";
|
this.myserialport.state.readValue = [];
|
},
|
loadHistory() {
|
if (this.form1.isShowHistory) this.form1.desc = this.readLi().join("");
|
else {
|
let temp = this.readLi();
|
if (temp.length > 0) this.form1.desc = temp[temp.length - 1].join("");
|
}
|
},
|
readLi() {
|
let readType = this.readType;
|
return this.myserialport.state.readValue.map((items, index) => {
|
const item = items.value;
|
const type = items.type; // 1接收,2发送
|
let body = [];
|
if (item !== undefined) {
|
let strArr = [];
|
for (let hex of Array.from(item)) {
|
strArr.push(hex.toString(16).toLocaleUpperCase());
|
}
|
if (strArr.includes("D") && strArr.includes("A")) {
|
if (strArr.indexOf("A") - strArr.indexOf("D") === 1) {
|
strArr.splice(strArr.indexOf("D"), 1);
|
strArr.splice(strArr.indexOf("A"), 1, <br key={0} />);
|
}
|
}
|
strArr = strArr.map((item) => {
|
if (typeof item === "string") {
|
if (readType === 1) {
|
return this.myserialport.hex2a(parseInt(item, 16));
|
} else if (readType === 2) {
|
return item + " ";
|
}
|
}
|
return item;
|
});
|
if (typeof strArr[strArr.length - 1] === "string") {
|
strArr.push("\r\n");
|
}
|
body.push(strArr.join(""));
|
}
|
return body;
|
});
|
},
|
conCom(){
|
try {
|
this.myserialport.state.baudRate = this.form1.baudRate;
|
this.myserialport.state.dataBits = this.form1.dataBits;
|
this.myserialport.state.stopBits = this.form1.stopBits;
|
this.myserialport.state.parity = this.form1.parity;
|
this.myserialport.state.flowControl = this.form1.flowControl;
|
this.myserialport.openPort(0, true, this.callBack);
|
console.log(this.form1.port)
|
} catch (error) {
|
this.$message.error("串口连接失败!请检查串口是否已被占用");
|
}
|
if (this.myserialport.state.isOpen) {
|
this.$message.success("串口连接成功");
|
}
|
},
|
//连接
|
async connectBtn() {
|
if (this.btnType === "primary") {
|
try {
|
this.myserialport.state.baudRate = this.form1.baudRate;
|
this.myserialport.state.dataBits = this.form1.dataBits;
|
this.myserialport.state.stopBits = this.form1.stopBits;
|
this.myserialport.state.parity = this.form1.parity;
|
this.myserialport.state.flowControl = this.form1.flowControl;
|
await this.myserialport.openPort(this.form1.port, true, this.callBack);
|
console.log(this.form1.port)
|
} catch (error) {
|
this.$message.error("串口连接失败!请检查串口是否已被占用");
|
}
|
if (this.myserialport.state.isOpen) {
|
this.$message.success("串口连接成功");
|
this.open1 = false;
|
this.btnType = "danger";
|
this.btnText = "关闭串口";
|
this.operationProgress = "串口已连接";
|
}
|
} else {
|
this.myserialport.openPort(this.form1.port, false, this.callBack);
|
this.$message.success("串口关闭成功");
|
this.btnType = "primary";
|
this.btnText = "连接串口";
|
this.operationProgress = "串口未连接";
|
}
|
},
|
//授权
|
async obtainAuthorization() {
|
if ("serial" in navigator) {
|
console.log("The Web Serial API is supported.");
|
if (!this.myserialport) this.myserialport = new MySerialPort();
|
try {
|
await this.myserialport.handleRequestPort();
|
this.$message.success("串口授权成功");
|
this.getPortInfo(this.myserialport.state.ports);
|
} catch (error) {
|
this.$message.warning("未选择新串口授权!");
|
}
|
} else {
|
this.$message.error(
|
"当前为HTTP模式或者浏览器版本过低,不支持网页连接串口"
|
);
|
}
|
},
|
//串口列表初始化
|
getPortInfo(portList) {
|
this.portsList = [];
|
portList.map((port, index) => {
|
const { usbProductId, usbVendorId } = port.getInfo();
|
if (usbProductId === undefined || usbVendorId === undefined) {
|
this.portsList.push({ label: "未知设备" + index, value: index });
|
} else {
|
const usbVendor = USBDevice.filter(
|
(item) => parseInt(item.vendor, 16) === usbVendorId
|
);
|
let usbProduct = [];
|
if (usbVendor.length === 1) {
|
usbProduct = usbVendor[0].devices.filter(
|
(item) => parseInt(item.devid, 16) === usbProductId
|
);
|
}
|
this.portsList.push({ label: usbProduct[0].devname, value: index });
|
}
|
});
|
},
|
// 发送
|
async sendCommon() {
|
if (this.myserialport.state.isOpen) {
|
if (this.form1.sendMsg.length !== 0) {
|
const writeType = this.form1.type;
|
let value = this.form1.sendMsg;
|
let arr = [];
|
if (writeType === 1) {
|
// ASCII
|
for (let i = 0; i < value.length; i++) {
|
arr.push(this.myserialport.a2hex(value[i]));
|
}
|
} else if (writeType === 2) {
|
// HEX
|
if (/^[0-9A-Fa-f]+$/.test(value) && value.length % 2 === 0) {
|
for (let i = 0; i < value.length; i = i + 2) {
|
arr.push(parseInt(value.substring(i, i + 2), 16));
|
}
|
} else {
|
this.$message.error("格式错误");
|
return;
|
}
|
}
|
this.myserialport.writeText(arr);
|
} else {
|
this.$message.warning("请输入发送的信息");
|
}
|
} else {
|
this.$message.warning("串口处于关闭状态,请连接串口");
|
}
|
},
|
async getPorts() {
|
await this.myserialport.getPorts();
|
this.getPortInfo(this.myserialport.state.ports);
|
},
|
querySearch(queryString, cb) {
|
var restaurants = this.restaurants;
|
var results = queryString
|
? restaurants.filter(this.createFilter(queryString))
|
: restaurants;
|
// 调用 callback 返回建议列表的数据
|
cb(results);
|
},
|
createFilter(queryString) {
|
return (restaurant) => {
|
return (
|
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) ===
|
0
|
);
|
};
|
},
|
loadAll() {
|
return [
|
{ value: "110" },
|
{ value: "300" },
|
{ value: "600" },
|
{ value: "1200" },
|
{ value: "2400" },
|
{ value: "4800" },
|
{ value: "7200" },
|
{ value: "9600" },
|
{ value: "14400" },
|
{ value: "19200" },
|
{ value: "28800" },
|
{ value: "38400" },
|
{ value: "56000" },
|
{ value: "57600" },
|
{ value: "76800" },
|
{ value: "115200" },
|
{ value: "230400" },
|
{ value: "460800" },
|
];
|
},
|
}
|
}
|
|
</script>
|
<style lang="scss" scoped>
|
.igbt-station-container {
|
height: 100vh;
|
padding: 20px;
|
background-color: #f5f7fa;
|
|
.full-height {
|
height: 100%;
|
}
|
|
.station-card {
|
height: 100%;
|
border-radius: 8px;
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 30px;
|
padding-bottom: 15px;
|
border-bottom: 1px solid #ebeef5;
|
|
.station-title {
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
|
i {
|
font-size: 24px;
|
color: #409EFF;
|
}
|
|
.head-font {
|
font-size: 20px;
|
font-weight: bold;
|
color: #303133;
|
}
|
}
|
|
.el-button {
|
&.is-connected {
|
background-color: #67C23A;
|
border-color: #67C23A;
|
}
|
}
|
}
|
|
.content-section {
|
padding: 20px 0;
|
|
.input-section {
|
margin-bottom: 30px;
|
|
.input-with-label {
|
display: flex;
|
align-items: center;
|
gap: 15px;
|
|
.input-label {
|
font-size: 16px;
|
color: #606266;
|
min-width: 100px;
|
}
|
|
.material-input {
|
width: 300px;
|
|
&.is-scanning {
|
.el-input__inner {
|
border-color: #409EFF;
|
box-shadow: 0 0 8px rgba(64, 158, 255, 0.4);
|
}
|
}
|
}
|
}
|
}
|
|
.operation-status {
|
background-color: #f8f9fa;
|
padding: 20px;
|
border-radius: 6px;
|
border: 1px solid #e4e7ed;
|
|
.status-title {
|
font-size: 16px;
|
color: #606266;
|
margin-bottom: 10px;
|
}
|
|
.status-content {
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
padding: 15px;
|
border-radius: 4px;
|
font-size: 16px;
|
font-weight: 500;
|
|
&.status-normal {
|
background-color: #f0f9eb;
|
color: #67C23A;
|
}
|
|
&.status-warning {
|
background-color: #fdf6ec;
|
color: #E6A23C;
|
}
|
|
&.status-error {
|
background-color: #fef0f0;
|
color: #F56C6C;
|
}
|
|
i {
|
font-size: 20px;
|
}
|
}
|
}
|
}
|
}
|
}
|
</style>
|