说明:解决生产上过秤重量手动输入出错问题
效果图:
一:代码部分
1、创建一个名字为seriaport.js文件(随便定义,为下面页面引入使用)
export default class MySerialPort {
constructor() {
this.state = {
portIndex: undefined,
ports: [],
isOpen: false,
writeType: 1,
readType: 1,
isScroll: true,
readValue: [],
status:false,
//port参数
baudRate: "9600",
dataBits: "8",
stopBits: "1",
parity: "none",
flowControl: "none",
};
this.keepReading=false;
this.getPorts = this.getPorts.bind(this);
this.handleRequestPort = this.handleRequestPort.bind(this);
this.handleChildrenChange = this.handleChildrenChange.bind(this);
this.readText = this.readText.bind(this);
this.writeText = this.writeText.bind(this);
this.handleClear = this.handleClear.bind(this);
this.a2hex = this.a2hex.bind(this);
this.hex2a = this.hex2a.bind(this);
this.hex2atostr=this.hex2atostr.bind(this);
this.reader={};
this.closed;
}
async getPorts() {
// 获取已授权的全部串口
let ports = await navigator.serial.getPorts();
this.setState({
ports,
});
}
async handleRequestPort() {
// 请求授权
try {
await navigator.serial.requestPort();
await this.getPorts();
} catch (e) {
this.$message.error(e.toString());
}
}
async openPort(portIndex, isOpen,callBack=null) {
// 打开串口
let port = this.state.ports[portIndex];
if (!isOpen) {
// 关闭串口
this.keepReading = false;
this.reader.cancel();
await this.closed;
this.handlePortOpen({
portIndex,
isOpen,
});
} else {
await port.open({
baudRate: this.state.baudRate,
dataBits: this.state.dataBits,
stopBits: this.state.stopBits,
parity: this.state.parity,
flowControl: this.state.flowControl,
});
this.handlePortOpen({
portIndex,
isOpen,
});
this.keepReading = true;
this.closed=this.readUntilClosed(portIndex,callBack);
}
}
async readUntilClosed(portIndex,callBack=null) {
let port = this.state.ports[portIndex];
while (port.readable && this.keepReading) {
this.reader = port.readable.getReader();
try {
let readCache=[]
while (true) {
const { value, done } = await this.reader.read();
if (done) {
break;
}
readCache.push(...value)
setTimeout(() => {
if(readCache.length>0){
this.readText(readCache);
callBack(readCache)
readCache=[]
}
}, 300);//串口缓存
}
} catch (error) {
this.$message.error(error.toString());
} finally {
this.reader.releaseLock();
}
await port.close();
}
}
handlePortOpen({ portIndex, isOpen }) {
// 处理打开串口
this.setState({
portIndex,
isOpen,
});
}
handleChildrenChange(type, value) {
this.setState({
[type]: value,
});
}
portWrite(value) {
return new Promise(async (resolve, reject) => {
if (!this.state.isOpen) {
this.$message.error("串口未打开");
reject();
return;
} else {
let port = this.state.ports[this.state.portIndex];
const writer = port.writable.getWriter();
await writer.write(new Uint8Array(value));
writer.releaseLock();
resolve(value);
}
});
}
readText(value) {
// console.log(value, "读取");
let newValue = this.state.readValue.concat({
value,
type: 1,
});
this.setState({
readValue: newValue,
});
}
writeText(value) {
// console.log(value, "写入");
this.portWrite(value).then((res) => {
let newValue = this.state.readValue.concat({
value: res,
type: 1,
});
this.setState({
readValue: newValue,
});
});
}
handleClear() {
this.setState({
readValue: [],
});
}
componentDidMount() {
this.getPorts();
}
handleState(status) {
this.setState({
status,
});
}
setState(obj){
Object.keys(this.state).forEach(key => {
if(obj[key]!=undefined){
this.state[key]=obj[key]
}
});
}
//字节转字符串
hex2atostr (arr) {
return String.fromCharCode.apply(String, arr);
}
hex2a(hexx) {
return String.fromCharCode(hexx);
}
//字符转16进制
a2hex(str) {
return str.charCodeAt(0);
}
}
2、创建一个为usb.json 文件(随便定义,为下面页面引入使用)
文件过大,下载地址(因为大部分是参考这位大佬的,一部分是按照自己的需求进行更改的ZhangY1217的博客_CSDN博客-java,c#,java23种设计模式领域博主)
文件下载链接:https://download.csdn.net/download/ZhangY1217/86662302
3、页面代码部分
<template>
<span>
<a-row :gutter="[10, 1]">
<a-col :span="6">
<a-form-item :labelCol="{ span: 6 }" :wrapperCol="{ span: 16 }">
<template v-slot:label>
//v-has="'cont:Scales'" 为管理员权限,方便调试,普通用户进入页面直接授权,是为了第一次进行调试
<a v-has="'cont:Scales'" @click="getScales"><a-icon type="windows"/></a>
<span> 重 量 </span>
</template>
<!-- <a-input-number /> -->
<a-input-search
placeholder=" "
enter-button="换端口"
v-model="ObjFrom.qtReal"
@pressEnter="OnScreen"
:formatter="value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
:parser="value => `${value}`.replace(/\$\s?|(,*)/g, '')"
:precision="2"
style="width: 100%"
@search="obtainAuthorization"
:key="time1"
/>
</a-form-item>
</a-col>
</a-row>
</span>
</template>
//js部分
<script>
//第一步新建的文件名称
import MySerialPort from '@api/seriaport.js'
//第二步新建的
import USBDevice from '@api/usb.json'
export default {
name: 'MzInProducts',
components: {
MzOrderlist,
LbCard,
modalScales
},
data() {
return {
// 电子秤
form: {
baudRate: '9600',
dataBits: '8',
stopBits: '1',
parity: 'none',
flowControl: 'none',
desc: '',
type: '1',
isShowHistory: false,
port: '1'
},
btnType: 'primary',
//这个portsList是我自己写死的,因为我的电子秤是这个名称
portsList: [
{
label: 'PL2303 Serial Port / Mobile Action MA-8910P',
value: 1
}
],
ObjFrom:{},
readType: 1,
time1: '',
}
},
mounted() {
this.loadData()
},
methods: {
// 链接配置页面
// 电子称配置
loadData() {
console.log(navigator)
console.log('serial' in navigator)
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('当前浏览器不支持,请换成谷歌浏览器8 9 以上浏览器')
}
},
// 链接电子秤
async connectBtn() {
if (this.btnType == 'primary') {
try {
this.myserialport.state.baudRate = this.form.baudRate
this.myserialport.state.dataBits = this.form.dataBits
this.myserialport.state.stopBits = this.form.stopBits
this.myserialport.state.parity = this.form.parity
this.myserialport.state.flowControl = this.form.flowControl
await this.myserialport.openPort(this.form.port, true, this.callBack)
} catch (error) {
this.$message.error('串口连接失败!请关闭其他页面占用电子称的系统,确保电子秤的是唯一的值')
}
if (this.myserialport.state.isOpen) {
this.$message.success('电子秤链接成功')
this.btnType = 'dashed'
this.btnText = '关闭电子称'
}
} else {
this.myserialport.openPort(this.form.port, false, this.callBack)
this.$message.success('串口关闭成功')
this.btnType = 'primary'
this.btnText = '链接电子秤'
}
},
//接受数据的回调
callBack(value) {
//this.ObjFrom.qtReal 是input 的值
if (this.form.isShowHistory) this.ObjFrom.qtReal = this.getNumberFromStr(this.readLi().join(''))
else {
if (value.length > 0) this.ObjFrom.qtReal = this.getNumberFromStr(this.myserialport.hex2atostr(value))
}
console.log(this.ObjFrom.qtReal)
this.time1 = Date.now()
},
clearHistory() {
this.form.desc = ''
this.myserialport.state.readValue = []
},
loadHistory() {
if (this.form.isShowHistory) {
this.form.desc = this.readLi().join('')
console.log(temp[temp.length - 1].join(''))
} else {
let temp = this.readLi()
if (temp.length > 0) this.form.desc = temp[temp.length - 1].join('')
console.log(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
})
},
//授权
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)
}
console.log(usbProduct[0].devname)
this.portsList.push({ label: usbProduct[0].devname, value: index })
if (this.portsList.length > 0) {
this.form.port = this.portsList[0].value
}
}
})
},
async getPorts() {
await this.myserialport.getPorts()
this.getPortInfo(this.myserialport.state.ports)
this.connectBtn()
},
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' }
]
},
// 截取方法
getNumberFromStr(str) {
let matchArr = str.match(/\d+\.\d+/)
if (matchArr && matchArr.length) {
let num = parseFloat(matchArr[0])
.toFixed(2)
.toString()
if (num.indexOf('.') === -1) {
return num
}
let numArr = num.split('.')
if (numArr[0] !== '') {
numArr[0] = parseInt(numArr[0]).toString()
}
if (numArr[0] === '0' && numArr[0].length > 1) {
numArr[0] = numArr[0].slice(1)
}
return numArr.join('.')
}
return ''
},
// ScalesFrom页面是调试页面
getScales() {
this.$refs.ScalesFrom.showDrawer()
}
}
}
</script>
注: ScalesFrom 后期我在更新上去
4、这个搭建只适合(只支持本机访问和https的,你这个要弄nginx和自己生成证书来用https的才能访问)但是可以通过别的方式解决本地服务器,使用一个局域网访问的需求(解决方法5)
5、解决谷歌服务器安全机制(因为谷歌浏览器有安全机制,我们是http访问,而不是https,https不会有这种问题)
解决方案:
1、谷歌浏览器访问:chrome://flags/#unsafely-treat-insecure-origin-as-secure
设置为Enabled ---一定点击下面Relaunch
2、打开电脑终端:
chrome(为你自己安装路径) --unsafely-treat-insecure-origin-as-secure=http://192.168.0.99(http://192.168.0.99 这个是我们服务地址,改为你自己的)
我的地址是("C:\Program Files (x86)\Chromiumbrowser\Chromium.exe")则是"C:\Program Files (x86)\Chromiumbrowser\Chromium.exe" --unsafely-treat-insecure-origin-as-secure=http://192.168.0.99
图片:
设置成功之后图片:(这个X号不能删除,否则会清空,我自己电脑试过几次会清空)
问题图片:
文章来源:https://www.toymoban.com/news/detail-469454.html
注:有问题私信留言文章来源地址https://www.toymoban.com/news/detail-469454.html
到了这里,关于纯vue 获取usb串口,实现电子秤的对接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!