add:新增rfid读写器查询接口以及输送线驱动

This commit is contained in:
2025-08-29 17:41:54 +08:00
parent 9c8077751a
commit 8e3176795d
32 changed files with 2263 additions and 277 deletions

View File

@@ -24,4 +24,12 @@ export function edit(data) {
})
}
export default { add, edit, del }
export function excelImport(data) {
return request({
url: 'api/storageCell/excelImport',
method: 'post',
data
})
}
export default { add, edit, del, excelImport }

View File

@@ -82,6 +82,7 @@ import agv_ndc_two from '@/views/acs/device/driver/agv/agv_ndc_two'
import xg_agv from '@/views/acs/device/driver/agv/xg_agv'
import xg_agv_car from '@/views/acs/device/driver/agv/xg_agv_car.vue'
import standard_manipulator from '@/views/acs/device/driver/one_manipulator/standard_manipulator.vue'
import standard_storage from '@/views/acs/device/driver/standard_storage'
export default {
name: 'DeviceConfig',
components: {
@@ -93,7 +94,8 @@ export default {
xg_agv,
belt_conveyor,
standard_manipulator,
xg_agv_car
xg_agv_car,
standard_storage
},
dicts: ['device_type'],
mixins: [crud],

View File

@@ -0,0 +1,315 @@
<template>
<!-- 标准版-货架-->
<div>
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span class="role-span">指令相关</span>
</div>
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="78px">
<el-row>
<el-col :span="8">
<el-form-item label="是否请求wms" label-width="150px">
<el-switch v-model="form.reqWms" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span class="role-span">设备协议</span>
</div>
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="78px">
<el-row>
<el-col :span="12">
<el-form-item label="排:" label-width="150px" prop="x">
<el-input v-model.number="form.x" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="巷道:" label-width="150px" prop="tunnel">
<el-input v-model.number="form.tunnel" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="最大列:" label-width="150px" prop="maxY">
<el-input v-model.number="form.maxY" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最小列:" label-width="150px" prop="minY">
<el-input v-model.number="form.minY" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="最大层:" label-width="150px" prop="maxZ">
<el-input v-model.number="form.maxZ" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最小层:" label-width="150px" prop="minZ">
<el-input v-model.number="form.minZ" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span class="role-span" />
<el-button
:loading="false"
icon="el-icon-check"
size="mini"
style="float: right; padding: 6px 9px"
type="primary"
@click="doSubmit"
>保存
</el-button>
</div>
</el-card>
</div>
</template>
<script>
import {
queryDriverConfig,
updateConfig,
testRead,
testwrite
} from '@/api/acs/device/driverConfig'
import { selectOpcList } from '@/api/acs/device/opc'
import { selectPlcList } from '@/api/acs/device/opcPlc'
import { selectListByOpcID } from '@/api/acs/device/opcPlc'
import crud from '@/mixins/crud'
import deviceCrud from '@/api/acs/device/device'
export default {
name: 'StandardStorage',
mixins: [crud],
props: {
parentForm: {
type: Object,
required: true
}
},
data() {
const checkMaxY = (rule, value, callback) => {
if (value < this.form.minY) {
callback(new Error('最大列应大于最小列!'))
} else {
callback()
}
}
const checkMinY = (rule, value, callback) => {
if (value > this.form.maxY) {
callback(new Error('最小列应小于最大列!'))
} else {
callback()
}
}
const checkMaxZ = (rule, value, callback) => {
if (value < this.form.minZ) {
callback(new Error('最大层应大于最小层!'))
} else {
callback()
}
}
const checkMinZ = (rule, value, callback) => {
if (value > this.form.maxZ) {
callback(new Error('最小层应小于最大层!'))
} else {
callback()
}
}
return {
device_code: '',
device_id: '',
plc_id: '',
plc_code: '',
opc_id: '',
opc_code: '',
configLoading: false,
dataOpcservers: [],
dataOpcPlcs: [],
deviceList: [],
data1: [],
data2: [],
form: {
x: 0,
tunnel: 0,
maxY: 0,
maxZ: 0,
minY: 0,
minZ: 0,
reqWms: true
},
rules: {
x: [
{ required: true, message: '排不能为空', trigger: 'blur' }
],
tunnel: [
{ required: true, type: 'number', min: 0, message: '请输入大于0的数字', trigger: 'blur' }
],
maxY: [
{ required: true, type: 'number', min: 0, message: '请输入大于0的数字', trigger: 'blur' },
{ validator: checkMaxY, trigger: 'blur' }
],
maxZ: [
{ required: true, type: 'number', min: 0, message: '请输入大于0的数字', trigger: 'blur' },
{ validator: checkMaxZ, trigger: 'blur' }
],
minY: [
{ required: true, type: 'number', min: 0, message: '请输入大于0的数字', trigger: 'blur' },
{ validator: checkMinY, trigger: 'blur' }
],
minZ: [
{ required: true, type: 'number', min: 0, message: '请输入大于0的数字', trigger: 'blur' },
{ validator: checkMinZ, trigger: 'blur' }
]
}
}
},
created() {
this.$nextTick(() => {
// 从父表单获取设备编码
this.device_id = this.$props.parentForm.device_id
this.device_code = this.$props.parentForm.device_code
queryDriverConfig(this.device_id, this.$props.parentForm.driver_code).then(data => {
// 给表单赋值,并且属性不能为空
if (data.form) {
const arr = Object.keys(data.form)
// 不为空
if (arr.length > 0) {
data.form.x = parseInt(data.form.x)
data.form.tunnel = parseInt(data.form.tunnel)
data.form.minY = parseInt(data.form.minY)
data.form.maxY = parseInt(data.form.maxY)
data.form.minZ = parseInt(data.form.minZ)
data.form.maxZ = parseInt(data.form.maxZ)
this.form = data.form
}
}
// 给表单赋值,并且属性不能为空
if (data.parentForm) {
const arr = Object.keys(data.parentForm)
// 不为空
if (arr.length > 0) {
this.opc_code = data.parentForm.opc_code
this.plc_code = data.parentForm.plc_code
}
}
this.data1 = data.rs
this.data2 = data.ws
this.sliceItem()
})
selectPlcList().then(data => {
this.dataOpcPlcs = data
this.plc_id = this.$props.parentForm.opc_plc_id
})
selectOpcList().then(data => {
this.dataOpcservers = data
this.opc_id = this.$props.parentForm.opc_server_id
})
deviceCrud.selectDeviceList().then(data => {
this.deviceList = data
})
})
},
methods: {
changeOpc(val) {
this.dataOpcservers.forEach(item => {
if (item.opc_id === val) {
this.opc_code = item.opc_code
}
})
selectListByOpcID(val).then(data => {
this.dataOpcPlcs = data
this.plc_id = ''
this.plc_code = ''
if (this.dataOpcPlcs && this.dataOpcPlcs.length > 0) {
this.plc_id = this.dataOpcPlcs[0].plc_id
this.plc_code = this.dataOpcPlcs[0].plc_code
}
this.sliceItem()
})
},
changePlc(val) {
this.dataOpcPlcs.forEach(item => {
if (item.plc_id === val) {
this.plc_code = item.plc_code
this.sliceItem()
return
}
})
},
test_read1() {
testRead(this.data1, this.opc_id).then(data => {
this.data1 = data
this.notify('操作成功!', 'success')
}).catch(err => {
console.log(err.response.data.message)
})
},
test_write1() {
testwrite(this.data2, this.opc_id).then(data => {
this.notify('操作成功!', 'success')
}).catch(err => {
console.log(err.response.data.message)
})
},
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.configLoading = true
// 根据驱动类型判断是否为路由设备
const parentForm = this.parentForm
parentForm.is_route = true
parentForm.plc_id = this.plc_id
parentForm.opc_id = this.opc_id
updateConfig(parentForm, this.form, this.data1, this.data2).then(res => {
this.notify('保存成功', 'success')
this.configLoading = false
}).catch(err => {
this.configLoading = false
console.log(err.response.data.message)
})
}
})
},
sliceItem() { // 拼接DB的Item值
this.data1.forEach(item => {
const str = item.code
// 是否包含.
if (str.search('.') !== -1) {
// 截取最后一位
item.code = this.opc_code + '.' + this.plc_code + '.' + this.device_code + '.' + str.slice(str.lastIndexOf('.') + 1)
} else {
item.code = this.opc_code + '.' + this.plc_code + '.' + this.device_code + '.' + item.code
}
})
this.data2.forEach(item => {
const str = item.code
// 是否包含.
if (str.search('.') !== -1) {
// 截取最后一位
item.code = this.opc_code + '.' + this.plc_code + '.' + this.device_code + '.' + str.slice(str.lastIndexOf('.') + 1)
} else {
item.code = this.opc_code + '.' + this.plc_code + '.' + this.device_code + '.' + item.code
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,116 @@
<template>
<el-dialog
title="导入Excel文件"
append-to-body
:visible.sync="dialogVisible"
destroy-on-close
width="400px"
:show-close="true"
@close="close"
@open="open"
>
<el-upload
ref="upload"
class="upload-demo"
action=""
drag
:on-exceed="is_one"
:limit="1"
:auto-upload="false"
:multiple="false"
:show-file-list="true"
:on-change="uploadByJsqd"
:file-list="fileList"
accept=".xlsx,.xls"
>
<i class="el-icon-upload" />
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<div slot="tip" class="el-upload__tip">只能上传Excel文件且不超过10MB</div>
</el-upload>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="submit"> </el-button>
</span>
</el-dialog>
</template>
<script>
import crudStorageCell from '@/api/acs/device/storageCell'
import CRUD, { crud } from '@crud/crud'
export default {
name: 'UploadDialog',
mixins: [crud()],
components: {},
props: {
dialogShow: {
type: Boolean,
default: false
},
openParam: {
type: String
}
},
data() {
return {
dialogVisible: false,
fileList: [],
file1: ''
}
},
watch: {
dialogShow: {
handler(newValue, oldValue) {
this.dialogVisible = newValue
}
},
openParam: {
handler(newValue, oldValue) {
this.opendtlParam = newValue
}
}
},
methods: {
open() {
},
close() {
this.$emit('update:dialogShow', false)
},
is_one() {
this.crud.notify('只能上传一个excel文件', CRUD.NOTIFICATION_TYPE.WARNING)
},
// 文件校验方法
beforeAvatarUpload(file) {
// 不能导入大小超过2Mb的文件
if (file.size > 10 * 1024 * 1024) {
return false
}
return true
},
// 文件发生改变就会触发的事件
uploadByJsqd(file) {
this.file1 = file
},
submit() {
if (this.beforeAvatarUpload(this.file1)) {
this.fileList.name = this.file1.name
this.fileList.url = ''
var formdata = new FormData()
formdata.append('file', this.file1.raw)
// excelImport请求接口 formdata传递参数
crudStorageCell.excelImport(formdata).then((res) => {
this.crud.notify('导入成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
this.$emit('tableChanged3', '')
this.$emit('update:dialogShow', false)
})
} else {
this.crud.notify('文件过大请上传小于10MB的文件〜', CRUD.NOTIFICATION_TYPE.WARNING)
}
}
}
}
</script>

View File

@@ -40,7 +40,18 @@
class="filter-item"
/>
</el-form-item>
<rrOperation />
<rrOperation :permission="permission">
<el-button
slot="right"
class="filter-item"
type="warning"
icon="el-icon-upload2"
size="mini"
@click="uploadShow = true"
>
{{ $t('auto.common.import') }}
</el-button>
</rrOperation>
</el-form>
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
@@ -101,6 +112,7 @@
<!--分页组件-->
<pagination />
</div>
<UploadDialog :dialog-show.sync="uploadShow" @tableChanged3="tableChanged3" />
</div>
</template>
@@ -112,11 +124,12 @@ import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import rrOperation from '@crud/RR.operation'
import i18n from '@/i18n'
import UploadDialog from '@/views/acs/device/storageCell/UploadDialog'
const defaultForm = { storage_id: null, storage_code: null, x: null, y: null, z: null, address: null, remark: null, is_active: null, is_delete: null, create_by: null, create_time: null, update_by: null, update_time: null, parent_storage_code: null }
export default {
name: 'StorageCell',
components: { pagination, crudOperation, udOperation, rrOperation },
components: { pagination, crudOperation, udOperation, rrOperation, UploadDialog },
mixins: [presenter(), header(), form(defaultForm), crud()],
cruds() {
return CRUD({ title: i18n.t('storageCell.title'), url: 'api/storageCell', idField: 'storage_id', sort: 'storage_id,desc', crudMethod: { ...crudStorageCell }})
@@ -128,6 +141,7 @@ export default {
edit: ['admin', 'storageCell:edit'],
del: ['admin', 'storageCell:del']
},
uploadShow: false,
rules: {
storage_id: [
{ required: true, message: '货位标识不能为空', trigger: 'blur' }
@@ -166,6 +180,9 @@ export default {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
},
tableChanged3() {
this.crud.toQuery()
}
}
}

View File

@@ -0,0 +1,235 @@
<template>
<div class="app-container">
<!-- 工具栏 -->
<div class="head-container">
<el-form
:inline="true"
class="demo-form-inline"
label-position="right"
label-width="80px"
label-suffix=":"
>
<el-form-item label="监听端口">
<el-input
v-model="query.search"
clearable
style="width: 300px"
size="mini"
placeholder="请输入监听端口"
class="filter-item"
/>
</el-form-item>
<!-- 新增滑块开关 -->
<el-form-item label="设备监听">
<el-switch
v-model="rfidStatus"
active-color="#13ce66"
inactive-color="#ccc"
@change="open"
/>
</el-form-item>
<rrOperation />
</el-form>
<crudOperation :permission="permission" />
<!-- 表格 -->
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
size="mini"
style="width: 100%;"
@selection-change="crud.selectionChangeHandler"
>
<el-table-column prop="device_code" label="设备编号" />
<el-table-column prop="ip" label="IP" />
<el-table-column prop="status" label="连接状态">
<template slot-scope="scope">
<span :style="{ color: scope.row.status === 1 ? '#13ce66' : '#ff4949' }">
{{ scope.row.status === 1 ? '已连接' : '未连接' }}
</span>
</template>
</el-table-column>
<el-table-column prop="start_time" label="开始连接时间" />
<el-table-column
prop="online_time"
label="在线时间"
:min-width="flexWidth('online_time', crud.data, '在线时间')"
/>
<el-table-column
prop="rfid"
label="当前rfid值"
:min-width="flexWidth('rfid', crud.data, '当前rfid值')"
/>
<!-- <el-table-column prop="is_used" label="是否启用 ">-->
<!-- <template slot-scope="scope">-->
<!-- <el-switch-->
<!-- v-model="scope.row.is_used"-->
<!-- active-color="#409EFF"-->
<!-- inactive-color="#F56C6C"-->
<!-- @change="changeEnabled(scope.row, scope.row.is_used)"-->
<!-- />-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column
v-permission="['admin', 'mdPbMeasureunit:edit', 'mdPbMeasureunit:del']"
fixed="right"
label="操作"
width="150px"
align="center"
>
<template slot-scope="scope">
<udOperation :data="scope.row" :permission="permission" />
</template>
</el-table-column>
</el-table>
<el-input
v-model="logText"
type="textarea"
:rows="15"
placeholder="设备运行日志..."
readonly
style="width: 100%; margin-top: 5px;"
/>
<!-- 分页 -->
<pagination />
</div>
</div>
</template>
<script>
import crudRfid from './vehicle_rfid'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = {
id: null,
ip: null,
port: null,
device_code: null,
is_used: true,
online_time: null,
create_id: null,
create_name: null,
create_time: null,
update_id: null,
update_name: null,
update_time: null,
status: null,
start_time: null
}
export default {
dicts: ['is_used'],
name: 'VehicleRfid',
components: { pagination, crudOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
cruds() {
return CRUD({
title: '计量单位',
url: 'api/rfid',
optShow: {
add: true
},
idField: 'id',
sort: 'id,desc',
crudMethod: { ...crudRfid }
})
},
data() {
return {
intervalId: null,
permission: {},
rfidStatus: false, // 新增开关状态变量
logText: '', // 展示日志内容
logTextList: [], // 存储日志行数组最多10条
query: {
search: '8160' // 设置默认监听端口
}
}
},
mounted() {
// 读写器定时器
this.intervalId = setInterval(() => {
this.crud.toQuery()
this.updateLogFromResult()
}, 3000)
},
beforeDestroy() {
if (this.intervalId) {
clearInterval(this.intervalId)
}
},
methods: {
[CRUD.HOOK.beforeRefresh]() {
return true
},
// updateLogFromResult() {
// const logList = this.crud.data.map(item => item.log).filter(log => !!log)
// if (logList.length === 0) return
// const newLog = logList.join('\n')
// //每次都添加日志
// this.logTextList.unshift(newLog) // 新日志放最前
// if (this.logTextList.length > 15) {
// this.logTextList.pop() // 最多保留10条
// }
// this.logText = this.logTextList.join('\n\n') // 更新展示内容
// },
updateLogFromResult() {
const s = this.crud.data
const logList = this.crud.data.map(item => item.log).filter(log => !!log)
if (logList.length === 0) {
return
}
const newLog = logList.join('\n')
// 判断最新日志是否和旧的重复,避免重复添加
if (!this.logTextList.includes(newLog)) {
this.logTextList.unshift(newLog) // 新日志放最前
if (this.logTextList.length > 15) {
this.logTextList.pop() // 最多保留10条
}
this.logText = this.logTextList.join('\n\n') // 更新展示内容
}
},
changeEnabled(data, val) {
this.$confirm('此操作将改变' + data.device_code + '状态, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
crudRfid.edit(data).then(() => {
this.crud.notify('操作成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
}).catch(() => {
data.is_used = !val
})
}).catch(() => {
data.is_used = !val
})
},
open(val) {
debugger
const payload = {
port: this.query.search == null ? '8160' : this.query.search,
isOpen: val ? '1' : '0'
}
crudRfid.open(JSON.stringify(payload)).then(res => {
this.crud.toQuery()
this.crud.notify('操作成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
}).catch(() => {
this.$message.error('操作失败')
this.rfidStatus = !val
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,49 @@
import request from '@/utils/request'
export function open(data) {
return request({
url: 'api/rfid/open',
method: 'post',
data
})
}
export function add(data) {
return request({
url: 'api/rfid/add',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/rfid/delete',
method: 'post',
data: ids
})
}
export function edit(data) {
return request({
url: 'api/rfid/update',
method: 'post',
data
})
}
export function getUnit(params) {
return request({
url: 'api/rfid',
method: 'get',
params
})
}
export function getSelect() {
return request({
url: 'api/rfid/select',
method: 'get'
})
}
export default { add, edit, del, getUnit, getSelect, open }