|
@@ -0,0 +1,332 @@
|
|
|
+import { getHardwareCheckPath } from "./env";
|
|
|
+const path = require("path");
|
|
|
+const net = require("net");
|
|
|
+const childProcess = require("child_process");
|
|
|
+
|
|
|
+// const isDevelopment = process.env.NODE_ENV !== "production";
|
|
|
+const hardwareCheckTool = getHardwareCheckPath();
|
|
|
+const hardwareCheckToolDir = path.dirname(hardwareCheckTool);
|
|
|
+// const HOST = "192.168.11.143";
|
|
|
+const HOST = "127.0.0.1";
|
|
|
+const PORT = "7013";
|
|
|
+const TIMESTAMP_BASE = "2000/01/01 00:00:00";
|
|
|
+
|
|
|
+let tcpClient = null;
|
|
|
+// 是否要停止硬件设备
|
|
|
+let needStartHardware = true;
|
|
|
+// 上位机软件进程
|
|
|
+let hardwareProcess = null;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 十六进制字符转十进制数
|
|
|
+ * @param {String} str 十六进制字符
|
|
|
+ */
|
|
|
+function hexToInt(str) {
|
|
|
+ return parseInt(str, 16);
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 十进制数转十六进制字符
|
|
|
+ * @param {Number} number 十进制数
|
|
|
+ */
|
|
|
+function intToHex(number) {
|
|
|
+ const hex = number.toString(16);
|
|
|
+ return hex.length < 2 ? `0` + hex : hex;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 分割字符串
|
|
|
+ * @param {String} str 字符串
|
|
|
+ * @param {Number} len 片段长度
|
|
|
+ */
|
|
|
+function splitStr(str, len = 2) {
|
|
|
+ let arrs = [];
|
|
|
+ let spinCount = Math.ceil(str.length / len);
|
|
|
+ for (let i = 0; i < spinCount; i++) {
|
|
|
+ arrs[i] = str.slice(i * len, (i + 1) * len);
|
|
|
+ }
|
|
|
+ return arrs;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建自定义crc16
|
|
|
+ * @param {Array} arr 数组
|
|
|
+ * @returns hex string
|
|
|
+ */
|
|
|
+function createCrc16(arr) {
|
|
|
+ let tcrc = 0xffff;
|
|
|
+ for (let i = 0, len = arr.length; i < len; i++) {
|
|
|
+ tcrc ^= "0x" + arr[i];
|
|
|
+ for (let j = 0; j < 8; j++) {
|
|
|
+ if (tcrc & 0x0001) {
|
|
|
+ tcrc >>= 1;
|
|
|
+ tcrc = tcrc & 0x7fff;
|
|
|
+ tcrc ^= 0xd164;
|
|
|
+ } else {
|
|
|
+ tcrc >>= 1;
|
|
|
+ tcrc = tcrc & 0x7fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ tcrc ^= 0x7013;
|
|
|
+ tcrc = intToHex(tcrc);
|
|
|
+ return tcrc.slice(-2) + tcrc.slice(0, 2);
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 创建解密自定义crc16
|
|
|
+ * @param {Array} arr 数组
|
|
|
+ * @returns hex string
|
|
|
+ */
|
|
|
+function createDecrytCrc16(arr) {
|
|
|
+ let tcrc = 0xffff;
|
|
|
+ for (let i = 0, len = arr.length; i < len; i++) {
|
|
|
+ tcrc ^= "0x" + arr[i];
|
|
|
+ for (let j = 0; j < 8; j++) {
|
|
|
+ if (tcrc & 0x0001) {
|
|
|
+ tcrc >>= 1;
|
|
|
+ tcrc = tcrc & 0x7fff;
|
|
|
+ tcrc ^= 0xc641;
|
|
|
+ } else {
|
|
|
+ tcrc >>= 1;
|
|
|
+ tcrc = tcrc & 0x7fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ tcrc ^= 0x673d;
|
|
|
+ tcrc = intToHex(tcrc);
|
|
|
+ return tcrc.slice(-2) + tcrc.slice(0, 2);
|
|
|
+}
|
|
|
+
|
|
|
+function addCircle(num1, num2) {
|
|
|
+ const num = num1 + num2;
|
|
|
+ return num > 256 ? num - 256 : num;
|
|
|
+}
|
|
|
+
|
|
|
+function parseHardwareCode(content) {
|
|
|
+ return {
|
|
|
+ pre: content.slice(0, 2),
|
|
|
+ id: content.slice(2, 14),
|
|
|
+ timestamp: content.slice(14, 22),
|
|
|
+ code: content.slice(22, 34),
|
|
|
+ encryptTypeData: content.slice(34, 46),
|
|
|
+ customCrc16: content.slice(-4)
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function getEncryptType(encryptTypeData) {
|
|
|
+ const total = splitStr(encryptTypeData).reduce(
|
|
|
+ (sum, num) => sum + hexToInt(num),
|
|
|
+ 0
|
|
|
+ );
|
|
|
+ return total % 9;
|
|
|
+}
|
|
|
+
|
|
|
+function getEncryptNums(data, encryptType) {
|
|
|
+ const [n1, n2, n3, n4, n5, n6] = splitStr(data).map(num => hexToInt(num));
|
|
|
+ let encryptNums = [];
|
|
|
+ switch (encryptType) {
|
|
|
+ case 0:
|
|
|
+ encryptNums = [n1 + 3, n3 + 2, n2 + 7];
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ encryptNums = [n2 + 4, n4 + 4, n3 + 9];
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ encryptNums = [n3 + 5, n4 + 6, n2 + 1];
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ encryptNums = [n4 + 6, n5 + 0, n1 + 3];
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ encryptNums = [n5 + 7, n4 + 1, n2 + 5];
|
|
|
+ break;
|
|
|
+ case 5:
|
|
|
+ encryptNums = [n6 + 8, n1 + 3, n1 + 5];
|
|
|
+ break;
|
|
|
+ case 6:
|
|
|
+ encryptNums = [n2 + 9, n3 + 5, n1 + 3];
|
|
|
+ break;
|
|
|
+ case 7:
|
|
|
+ encryptNums = [n2 + 2, n6 + 7, n1 + 2];
|
|
|
+ break;
|
|
|
+ case 8:
|
|
|
+ encryptNums = [n4 + 1, n2 + 6, n2 + 1];
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return encryptNums;
|
|
|
+}
|
|
|
+
|
|
|
+function getDecryptCode(code, encryptNums) {
|
|
|
+ let ucTempMac = splitStr(code).map(num => hexToInt(num));
|
|
|
+ let ucCheckIdReturn = "";
|
|
|
+ ucTempMac[0] = addCircle(ucTempMac[0], encryptNums[0]);
|
|
|
+ ucTempMac[1] = addCircle(ucTempMac[1], encryptNums[0]);
|
|
|
+ ucCheckIdReturn = createDecrytCrc16(ucTempMac.map(num => intToHex(num)));
|
|
|
+ ucTempMac[2] = addCircle(ucTempMac[2], encryptNums[1]);
|
|
|
+ ucTempMac[3] = addCircle(ucTempMac[3], encryptNums[1]);
|
|
|
+ ucCheckIdReturn += createDecrytCrc16(ucTempMac.map(num => intToHex(num)));
|
|
|
+ ucTempMac[4] = addCircle(ucTempMac[4], encryptNums[2]);
|
|
|
+ ucTempMac[5] = addCircle(ucTempMac[5], encryptNums[2]);
|
|
|
+ ucCheckIdReturn += createDecrytCrc16(ucTempMac.map(num => intToHex(num)));
|
|
|
+ return ucCheckIdReturn;
|
|
|
+}
|
|
|
+
|
|
|
+function getAnswerCode(codes) {
|
|
|
+ const encryptType = getEncryptType(codes.encryptTypeData);
|
|
|
+ // console.log("encryptType:", encryptType);
|
|
|
+ const nums = getEncryptNums(codes.encryptTypeData, encryptType);
|
|
|
+ // console.log("EncryptNums:", nums);
|
|
|
+ const decryptCode = getDecryptCode(codes.code, nums);
|
|
|
+ // console.log("decryptCode:", decryptCode);
|
|
|
+ const answerCode = [codes.pre, codes.id, codes.timestamp, decryptCode].join(
|
|
|
+ ""
|
|
|
+ );
|
|
|
+ const checkCode = createCrc16(splitStr(answerCode));
|
|
|
+ // console.log(checkCode);
|
|
|
+
|
|
|
+ return [answerCode, checkCode].join("");
|
|
|
+}
|
|
|
+
|
|
|
+function checkTimestamp(timestamp) {
|
|
|
+ const time = hexToInt(timestamp);
|
|
|
+ const timeNow = new Date(time * 1000 + new Date(TIMESTAMP_BASE).getTime());
|
|
|
+ const now = new Date().getTime();
|
|
|
+
|
|
|
+ const deviation = 5000; // 5s范围内都是合法
|
|
|
+ return timeNow < now + deviation && timeNow > now - deviation;
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+const checkToolIsStart = () => {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const cmd = process.platform === "win32" ? "tasklist" : "ps aux";
|
|
|
+ childProcess.exec(cmd, (err, stdout, stderr) => {
|
|
|
+ if (err) {
|
|
|
+ reject(err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const toolInfo = stdout.split("\n").find(line => {
|
|
|
+ return line.toLowerCase().includes("ArtControl.exe");
|
|
|
+ });
|
|
|
+ if (toolInfo) {
|
|
|
+ console.log(toolInfo);
|
|
|
+ resolve(toolInfo);
|
|
|
+ } else {
|
|
|
+ resolve("none");
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const startHardwareCheckProcess = () => {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ try {
|
|
|
+ hardwareProcess = childProcess.spawn(`${hardwareCheckTool}`, {
|
|
|
+ cwd: hardwareCheckToolDir
|
|
|
+ });
|
|
|
+ hardwareProcess.on("close", (code, signal) => {
|
|
|
+ console.log(`hardware machine over!${signal}`);
|
|
|
+ });
|
|
|
+ hardwareProcess.on("error", () => {
|
|
|
+ console.log(`hardware machine error!`);
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ reject(error);
|
|
|
+ }
|
|
|
+ // 检测工具是个持续进程,调用时无法获取进程开启成功信息。
|
|
|
+ // 目前延时2000ms处理。
|
|
|
+ setTimeout(() => {
|
|
|
+ resolve(true);
|
|
|
+ }, 2000);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const startHardwareCheckTool = async () => {
|
|
|
+ const res = await checkToolIsStart().catch(error => {
|
|
|
+ console.error(error);
|
|
|
+ });
|
|
|
+ if (!res) return Promise.reject("error");
|
|
|
+
|
|
|
+ if (res === "none") {
|
|
|
+ return startHardwareCheckProcess();
|
|
|
+ }
|
|
|
+ return Promise.resolve(true);
|
|
|
+};
|
|
|
+
|
|
|
+// tcp
|
|
|
+async function startConnect(resultCallback) {
|
|
|
+ // if (isDevelopment) {
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
+ const res = await startHardwareCheckTool().catch(error => {
|
|
|
+ console.error(error);
|
|
|
+ resultCallback({ success: false, type: "start", error });
|
|
|
+ });
|
|
|
+ if (!res) return;
|
|
|
+
|
|
|
+ stopConnect();
|
|
|
+
|
|
|
+ tcpClient = net.createConnection({
|
|
|
+ host: HOST,
|
|
|
+ port: PORT
|
|
|
+ });
|
|
|
+
|
|
|
+ tcpClient.on("connect", () => {
|
|
|
+ tcpClient.write(Buffer.from("efef", "hex"));
|
|
|
+ resultCallback({ success: true, type: "connect" });
|
|
|
+ });
|
|
|
+
|
|
|
+ tcpClient.on("data", data => {
|
|
|
+ if (!needStartHardware) return;
|
|
|
+
|
|
|
+ const content = data.toString("hex");
|
|
|
+ // console.log(content);
|
|
|
+ const codes = parseHardwareCode(content);
|
|
|
+ if (!checkTimestamp(codes.timestamp)) {
|
|
|
+ console.error("time error");
|
|
|
+ resultCallback({ success: false, type: "data", error: "超时" });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const answerCode = getAnswerCode(codes);
|
|
|
+ // console.log(answerCode);
|
|
|
+ tcpClient.write(Buffer.from(answerCode, "hex"));
|
|
|
+ resultCallback({ success: true, type: "data" });
|
|
|
+ });
|
|
|
+
|
|
|
+ tcpClient.on("error", error => {
|
|
|
+ console.error(error);
|
|
|
+ resultCallback({ success: false, type: "connect", error });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function stopConnect() {
|
|
|
+ if (!tcpClient) return;
|
|
|
+ if (hardwareProcess) hardwareProcess.kill();
|
|
|
+ tcpClient.pause();
|
|
|
+ tcpClient.destroy();
|
|
|
+ tcpClient = null;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 控制硬件设备开关
|
|
|
+ * @param {Boolean} open 是否开启设备
|
|
|
+ */
|
|
|
+function swithHardware(open) {
|
|
|
+ needStartHardware = !!open;
|
|
|
+}
|
|
|
+
|
|
|
+function testParse(content) {
|
|
|
+ console.log(content);
|
|
|
+ // const customCrc16 = createCrc16(splitStr(content).slice(0, -2));
|
|
|
+ // console.log(customCrc16);
|
|
|
+ // const codes = parseHardwareCode(content);
|
|
|
+ const codes = parseHardwareCode(content);
|
|
|
+ const answerCode = getAnswerCode(codes);
|
|
|
+ console.log(answerCode);
|
|
|
+}
|
|
|
+
|
|
|
+export { startConnect, stopConnect, swithHardware, testParse };
|