diff --git a/package.json b/package.json index adc853d..815aaf3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gateway-app", - "version": "1.0.3", + "version": "1.0.6", "description": "An Electron application with Vue", "main": "./out/main/index.js", "author": "example.com", diff --git a/resources/config/write/预设写配置.json b/resources/config/write/预设写配置.json new file mode 100644 index 0000000..6f24588 --- /dev/null +++ b/resources/config/write/预设写配置.json @@ -0,0 +1 @@ +[{"name":"属性1","addr":"60000","len":"2","data":"15"},{"name":"属性2","addr":"50000","len":"1","data":"1"}] \ No newline at end of file diff --git a/resources/service/config/config.yaml b/resources/service/config/config.yaml index fab3831..3a67e5d 100644 --- a/resources/service/config/config.yaml +++ b/resources/service/config/config.yaml @@ -1,5 +1,5 @@ server: - address: ":8000" + address: ":8888" openapiPath: "/api.json" swaggerPath: "/swagger" diff --git a/resources/service/plugins/config.yaml b/resources/service/plugins/config.yaml new file mode 100644 index 0000000..5cc5b2f --- /dev/null +++ b/resources/service/plugins/config.yaml @@ -0,0 +1,28 @@ +server: + address: ":8888" + staticRoot: "./resource/template" + apiPrefix: "/api/v1" + pluginPrefix: "/plugins" + file: + upload_path: "./resource/upload" + dir_mode: "byDate" + + + + +logger: + level: debug + path: "./resource/logs/server.log" + max_size: 10 + max_age: 30 + + +database: + default: + kind: "mysql" + dsn: "root:mysql_YiniNx@tcp(123.60.30.87:3306)/goside?charset=utf8mb4&parseTime=True&loc=Local" + max_idle_conns: 10 + max_open_conns: 100 + # kind: "SQLite" + # dsn: "./resource/db/db.db" + # max_idle_conns: 10 \ No newline at end of file diff --git a/resources/service/plugins/serial.exe b/resources/service/plugins/serial.exe new file mode 100644 index 0000000..959d7cd Binary files /dev/null and b/resources/service/plugins/serial.exe differ diff --git a/src/main/index.js b/src/main/index.js index 2f3ff69..937ed05 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -2,6 +2,7 @@ import { app, shell, BrowserWindow, ipcMain, globalShortcut, dialog } from 'elec import { join } from 'path'; import { electronApp, optimizer, is } from '@electron-toolkit/utils'; import icon from '../../resources/icon.png?asset'; +// const fs = require('fs'); //关闭exe命令 function killProcessByName(processName) { @@ -164,6 +165,44 @@ if (!gotTheLock) { }); }); + // 处理来自渲染进程的请求 + // ipcMain.handle('readFile', async (event, path) => { + // return new Promise((resolve, reject) => { + // fs.readFile(join(__dirname, path), 'utf8', (err, data) => { + // if (err) { + // reject(err); + // } else { + // resolve(data); + // } + // }); + // }); + // }); + // + // ipcMain.handle('writeFile', async (event, path, content) => { + // return new Promise((resolve, reject) => { + // fs.writeFile(join(__dirname, path), content, 'utf8', (err) => { + // if (err) { + // reject(err); + // } else { + // resolve(); + // } + // }); + // }); + // }); + // + // // 处理来自渲染进程的请求 + // ipcMain.handle('readDirectory', async (event, directoryPath) => { + // return new Promise((resolve, reject) => { + // fs.readdir(join(__dirname, directoryPath), (err, files) => { + // if (err) { + // reject(err); + // } else { + // resolve(files); + // } + // }); + // }); + // }); + // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits // explicitly with Cmd + Q. diff --git a/src/preload/index.js b/src/preload/index.js index 77f3ef6..121f8c3 100644 --- a/src/preload/index.js +++ b/src/preload/index.js @@ -1,9 +1,21 @@ -import { contextBridge } from 'electron' +import { contextBridge, ipcRenderer } from 'electron' import { electronAPI } from '@electron-toolkit/preload' // Custom APIs for renderer // 自定义的主进程方法API -const api = {} +// const api = { +// readFile: async (path) => { +// const result = await ipcRenderer.invoke('readFile', path); +// return result; +// }, +// writeFile: async (path, content) => { +// await ipcRenderer.invoke('writeFile', path, content); +// }, +// readDirectory: async (directoryPath) => { +// const result = await ipcRenderer.invoke('readDirectory', directoryPath); +// return result; +// } +// } // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise @@ -12,11 +24,11 @@ const api = {} if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('electron', electronAPI) - contextBridge.exposeInMainWorld('api', api) + // contextBridge.exposeInMainWorld('api', api) } catch (error) { console.error(error) } } else { window.electron = electronAPI - window.api = api + // window.api = api } diff --git a/src/renderer/src/assets/iconfont/iconfont.css b/src/renderer/src/assets/iconfont/iconfont.css index 93643a2..c73ab7b 100644 --- a/src/renderer/src/assets/iconfont/iconfont.css +++ b/src/renderer/src/assets/iconfont/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4622943 */ - src: url('iconfont.woff2?t=1721353754291') format('woff2'), - url('iconfont.woff?t=1721353754291') format('woff'), - url('iconfont.ttf?t=1721353754291') format('truetype'); + src: url('iconfont.woff2?t=1729646274723') format('woff2'), + url('iconfont.woff?t=1729646274723') format('woff'), + url('iconfont.ttf?t=1729646274723') format('truetype'); } .iconfont { @@ -13,6 +13,26 @@ -moz-osx-font-smoothing: grayscale; } +.icon-com001:before { + content: "\e62c"; +} + +.icon-chuankou:before { + content: "\e6be"; +} + +.icon-chuankou1:before { + content: "\e60a"; +} + +.icon-chuankoushezhi:before { + content: "\e600"; +} + +.icon-chuankou2:before { + content: "\e6fc"; +} + .icon-shousuoshangjiantou:before { content: "\e9b6"; } diff --git a/src/renderer/src/assets/iconfont/iconfont.ttf b/src/renderer/src/assets/iconfont/iconfont.ttf index 895a7cc..d073ed6 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.ttf and b/src/renderer/src/assets/iconfont/iconfont.ttf differ diff --git a/src/renderer/src/assets/iconfont/iconfont.woff b/src/renderer/src/assets/iconfont/iconfont.woff index 24c7448..e338b4e 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.woff and b/src/renderer/src/assets/iconfont/iconfont.woff differ diff --git a/src/renderer/src/assets/iconfont/iconfont.woff2 b/src/renderer/src/assets/iconfont/iconfont.woff2 index 47eb9ff..82b44c7 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.woff2 and b/src/renderer/src/assets/iconfont/iconfont.woff2 differ diff --git a/src/renderer/src/layout/Aside.vue b/src/renderer/src/layout/Aside.vue index aa4c262..b8fa2a9 100644 --- a/src/renderer/src/layout/Aside.vue +++ b/src/renderer/src/layout/Aside.vue @@ -10,7 +10,9 @@ const settings = defineProps({ 断路器调试 - 视频监控 + 视频监控 + 设备参数读写 + 空开标定 @@ -36,7 +38,7 @@ const settings = defineProps({ color: #333; font-size: 16px; .iconfont { - margin-right: 10px; + margin-right: 6px; color: #999; font-size: 22px; } diff --git a/src/renderer/src/router/index.js b/src/renderer/src/router/index.js index 9ce525c..00ba9e3 100644 --- a/src/renderer/src/router/index.js +++ b/src/renderer/src/router/index.js @@ -26,12 +26,30 @@ const router = createRouter({ keepAlive: true } }, + { + path: 'videoplayer', + name: 'videoplayer', + component: () => import('@renderer/views/system/videoplayer/index.vue'), + meta: { + menu: 'videoplayer', + keepAlive: true + } + }, + { + path: 'serialport', + name: 'serialport', + component: () => import('@renderer/views/system/serialport/index.vue'), + meta: { + menu: 'serialport', + keepAlive: true + } + }, { - path: 'videoplayer', - name: 'videoplayer', - component: () => import('@renderer/views/system/videoplayer/index.vue'), + path: 'devicestandard', + name: 'devicestandard', + component: () => import('@renderer/views/system/serialport/devicestandard.vue'), meta: { - menu: 'videoplayer', + menu: 'devicestandard', keepAlive: true } }, diff --git a/src/renderer/src/stores/seralPort.js b/src/renderer/src/stores/seralPort.js new file mode 100644 index 0000000..00029e6 --- /dev/null +++ b/src/renderer/src/stores/seralPort.js @@ -0,0 +1,18 @@ +import { defineStore } from 'pinia'; + +export const useSerialPortStore = defineStore('seralPort', { + state: () => ({ + serialport: { + port: '', + baudrate: 115200, + databits: 8, + parity: 0, + stopbits: 0 + } + }), + actions: { + setSerialport(data) { + this.serialport = data; + } + } +}); diff --git a/src/renderer/src/util/config.js b/src/renderer/src/util/config.js index 131727c..ec49de3 100644 --- a/src/renderer/src/util/config.js +++ b/src/renderer/src/util/config.js @@ -17,9 +17,14 @@ // } export default { - url: 'http://127.0.0.1:8000', - wsUrl: 'ws://127.0.0.1:8000', + url: 'http://127.0.0.1:8888', + wsUrl: 'ws://127.0.0.1:8888', swichWsUrl: 'ws://127.0.0.1:8001', + serialPortUrl: 'http://127.0.0.1:8888', + + // 远程连接串口使用 + // serialPortUrl: 'http://120.77.172.42:7202', + // serialPortUrl: 'http://192.168.1.17:8888', } diff --git a/src/renderer/src/views/system/serialport/devicestandard.vue b/src/renderer/src/views/system/serialport/devicestandard.vue new file mode 100644 index 0000000..aadffe6 --- /dev/null +++ b/src/renderer/src/views/system/serialport/devicestandard.vue @@ -0,0 +1,1172 @@ + + + + + + + + 电参数校准 + 读取 + 写入 + + + + + + {{ item.name }}{{ item.unit ? '(' + item.unit + ')' : '' }} + + + + + + + + + + + 电参数 + 刷新 + + + + + + + + + + + {{ item.name }}{{ item.unit ? '(' + item.unit + ')' : '' }} + {{ item.value != undefined ? (parseInt(item.value) * item.accuracy).toFixed(3) : '-' }} + + + + + + + + + + + + {{ item.name }}{{ item.unit ? '(' + item.unit + ')' : '' }} + {{ item.value != undefined ? (parseInt(item.value) * item.accuracy).toFixed(3) : '-' }} + + + + + + + + + + + + + + + diff --git a/src/renderer/src/views/system/serialport/index.vue b/src/renderer/src/views/system/serialport/index.vue new file mode 100644 index 0000000..d3be698 --- /dev/null +++ b/src/renderer/src/views/system/serialport/index.vue @@ -0,0 +1,760 @@ + + + + + + + + + + + 获取端口列表 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 关闭 + {{ connectionState ? '已打开' : '打开' }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ item.name }} + + + 读取 + + + + 写入 + + + + 一键读取 + 一键写入 + + + + + + + + + + + diff --git a/src/renderer/src/views/system/serialport/js/fun.js b/src/renderer/src/views/system/serialport/js/fun.js new file mode 100644 index 0000000..a857ab2 --- /dev/null +++ b/src/renderer/src/views/system/serialport/js/fun.js @@ -0,0 +1,296 @@ +/* + * @Descripttion: + * @version: + * @Author: lfzxs@qq.com + * @Date: 2024-10-16 13:52:11 + * @LastEditors: lfzxs@qq.com + * @LastEditTime: 2024-10-18 16:33:31 + */ + +export function buildHead(d_length,d_remote_id,d_local_id,d_cmd,d_dev_type,d_data_type,dataStart,dataLength,dataValue,activeData) { + // 初始化data为一个32位2进制数 + + let data = "0000" + d_data_type + d_dev_type + d_cmd + toBinaryString(d_local_id,9) + toBinaryString(d_remote_id,9) + d_length; + + console.log("获取头部",data); + + let hex_data = binaryToHex(data); + let dataBuffer = ''; + let buffer = hexToBuffer(hex_data); + //根据d_cmd的值拼接命令格式 + switch (d_cmd) { + case "0000"://特殊指令 + break; + case "0011"://地址读 + buffer = concatenateBuffers(htonl(buffer),htonl(toFixedSizeBuffer(parseInt(dataStart),4)),htonl(toFixedSizeBuffer(parseInt(dataLength),4))); + break; + case "0100"://地址写 + + if(activeData.dataType === 'hex'){ + dataValue = rearrangeHexStr(dataValue); + dataBuffer = hexToBuffer(dataValue); + }else if(activeData.dataType === 'hexString'){ + dataBuffer = hexToBuffer(dataValue); + }else{ + dataBuffer = stringToHexBuffer(dataValue,dataLength*4*2); + } + console.log("dataBuffer",dataBuffer) + buffer = concatenateBuffers(htonl(buffer),htonl(toFixedSizeBuffer(parseInt(dataStart),4)),htonl(toFixedSizeBuffer(parseInt(dataLength),4)),dataBuffer) + break; + } + console.log("dataValue-buffer",dataValue,buffer) + + let result = bufferToHex(buffer); + + return result; +} + +export function allBuildHead(d_length,d_remote_id,d_local_id,d_cmd,d_dev_type,d_data_type,dataStart,dataLength,dataValue,allList) { + // 初始化data为一个32位2进制数 + + let data = "0000" + d_data_type + d_dev_type + d_cmd + toBinaryString(d_local_id,9) + toBinaryString(d_remote_id,9) + d_length; + + console.log("获取头部",data); + + let hex_data = binaryToHex(data); + + let buffer = hexToBuffer(hex_data); + let allBufferList = []; + //根据d_cmd的值拼接命令格式 + switch (d_cmd) { + case "0000"://特殊指令 + break; + case "0011"://地址读 + buffer = concatenateBuffers(htonl(buffer),htonl(toFixedSizeBuffer(parseInt(dataStart),4)),htonl(toFixedSizeBuffer(parseInt(dataLength),4))); + break; + case "0100"://地址写 + allBufferList = allList.map(item=>{ + return stringToHexBuffer(item.inputValue,item.lengthValue*4*2); + }) + buffer = concatenateBuffers(htonl(buffer),htonl(toFixedSizeBuffer(parseInt(dataStart),4)),htonl(toFixedSizeBuffer(parseInt(dataLength),4)),...allBufferList) + break; + } + + let result = bufferToHex(buffer); + + return result; +} + +export function bufferToHex(buffer) { + let hexString = ''; + const view = new Uint8Array(buffer); + for (let i = 0; i < view.length; i++) { + const hex = view[i].toString(16).padStart(2, '0'); + hexString += hex; + } + return hexString; +} + +export function toBinaryString(number, length) { + let binaryString = number.toString(2); + return binaryString.padStart(length, '0'); +} + +export function binaryToHex(binaryStr) { + // 确保二进制字符串长度是4的倍数 + if (binaryStr.length % 4 !== 0) { + binaryStr = binaryStr.padStart(binaryStr.length + 4 - (binaryStr.length % 4), '0'); + } + + let hexStr = ''; + for (let i = 0; i < binaryStr.length; i += 4) { + // 取出每4位二进制字符串 + const binaryChunk = binaryStr.slice(i, i + 4); + // 转换成十六进制字符并追加到结果字符串中 + hexStr += parseInt(binaryChunk, 2).toString(16); + } + + return hexStr; +} + + +export function hexToBuffer(hexStr) { + if (hexStr.length % 2 !== 0) { + throw new Error('Invalid hex string'); + } + + let buffer = new ArrayBuffer(hexStr.length / 2); + let view = new Uint8Array(buffer); + + for (let i = 0; i < hexStr.length; i += 2) { + view[i / 2] = parseInt(hexStr.substr(i, 2), 16); + } + + return buffer; +} + +export function swapEndian(buffer) { + const swappedBuffer = new ArrayBuffer(buffer.byteLength); + const view = new DataView(buffer); + const swappedView = new DataView(swappedBuffer); + + for (let i = 0; i < buffer.byteLength; i++) { + swappedView.setUint8(i, view.getUint8(buffer.byteLength - 1 - i)); + } + + return swappedBuffer; +} + + +export function htonl(buffer) { + if (buffer.byteLength !== 4) { + throw new Error('Buffer must be exactly 4 bytes.'); + } + + let view = new DataView(buffer); + let number = view.getUint32(0); + + let swappedNumber = ((number & 0xFF) << 24) | + ((number & 0xFF00) << 8) | + ((number & 0xFF0000) >> 8) | + ((number >> 24) & 0xFF); + + let swappedBuffer = new ArrayBuffer(4); + let swappedView = new DataView(swappedBuffer); + swappedView.setUint32(0, swappedNumber); + + return swappedBuffer; +} + + + +//将数字转换成指定字节大小的buffer +export function toFixedSizeBuffer(number, byteSize) { + if (byteSize <= 0) { + throw new Error('Byte size must be greater than zero.'); + } + + let buffer = new ArrayBuffer(byteSize); + let view = new DataView(buffer); + + // 根据指定字节数设置数值到缓冲区中 + for (let i = 0; i < byteSize; i++) { + view.setUint8(byteSize - 1 - i, number & 0xFF); + number = number >> 8; + } + + return buffer; +} + + +export function concatenateBuffers(...buffers) { + + // 确保所有元素均为 ArrayBuffer + buffers.forEach(buffer => { + if (!(buffer instanceof ArrayBuffer)) { + throw new TypeError('All arguments must be ArrayBuffers'); + } + }); + // 计算总长度 + let totalLength = buffers.reduce((sum, buffer) => sum + buffer.byteLength, 0); + + // 创建一个新的 ArrayBuffer 并使用 Uint8Array 进行操作 + let concatenatedBuffer = new Uint8Array(totalLength); + let offset = 0; + + // 逐个复制每个缓冲区的内容 + buffers.forEach(buffer => { + concatenatedBuffer.set(new Uint8Array(buffer), offset); + offset += buffer.byteLength; + }); + + return concatenatedBuffer.buffer; +} + +export function stringToHexBuffer(str,length) { + // return new TextEncoder().encode(str).buffer; + const bufferView = new TextEncoder().encode(str); + + // 计算需要补充的00的数量 + const targetLength = length / 2; // 目标长度(字节数) + const paddingLength = targetLength - bufferView.length; + + // 创建一个新的Uint8Array,长度为目标长度 + const paddedBufferView = new Uint8Array(targetLength); + + // 将原始Uint8Array的内容复制到新的Uint8Array中 + paddedBufferView.set(bufferView); + + // 填充00 + if (paddingLength > 0) { + paddedBufferView.fill(0, bufferView.length, targetLength); + } + + // 返回格式化后的Uint8Array + return paddedBufferView.buffer; +} + +export function hexToString(hexStr) { + let str = ''; + for (let i = 0; i < hexStr.length; i += 2) { + if(hexStr.substr(i, 2)!=='00'){ + str += String.fromCharCode(parseInt(hexStr.substr(i, 2), 16)); + } + } + return str; +} + +export function rearrangeHexStr(hexStr) { + // 将输入的十六进制字符串解析为字节数组 + // 将输入的十六进制字符串按每8位分组 + const groups = []; + for (let i = 0; i < hexStr.length; i += 8) { + groups.push(hexStr.slice(i, i + 8)); + } + + // 对每个组内的每两位进行倒序拼接 + const rearrangedGroups = groups.map(group => { + let rearrangedGroup = ''; + for (let i = 0; i < group.length; i += 2) { + rearrangedGroup = group.substr(i, 2) + rearrangedGroup; + } + return rearrangedGroup; + }); + + // 将所有组重新拼接成一个字符串 + const rearrangedHexStr = rearrangedGroups.join(''); + + return rearrangedHexStr; +} + +//高低位交换 +export function rearrangeHexStr4(hexStr) { + let rearrangedGroup = ''; + for (let i = 0; i < hexStr.length; i += 2) { + rearrangedGroup = hexStr.substr(i, 2) + rearrangedGroup; + } + return rearrangedGroup; +} + +export function hexToVersion(hexStr) { + let str = rearrangeHexStr(hexStr); + str = hexToDec(str); + return str; +} + +export function versionToHex(hexStr) { + let str = decToHex(hexStr); + str = rearrangeHexStr(str); + return str; +} + +// 十六进制转十进制 +export function hexToDec(hexStr) { + // 去除前缀 "0x"(如果存在) + hexStr = hexStr.replace(/^0x/, ''); + // 将十六进制字符串转换为十进制数字 + return parseInt(hexStr, 16); +} + +// 十进制转十六进制 +export function decToHex(decNum) { + // 将十进制数字转换为十六进制字符串 + let hexStr = decNum.toString(16); + // 可选:添加前缀 "0x" + return `0x${hexStr}`; +}