Commit 26939a27 authored by lyong's avatar lyong

feat:增加自动化测试

parent bad876fe
// globalThis.cslog = //console.log
// //console.log = function () {
console.log = function () {
// }
// console.trace = function () {
}
console.trace = function () {
// }
}
console.debug = function (text) {
var date = new Date()
text = `========= [${date}] ${text}`
......@@ -60,6 +60,8 @@ function onCreate(uri) {
console.log(times);
gc();
}
require("autoFactoryTest.js").autoFactoryTestTimer();
}
// function onKeyEvent(code) {
......
<html>
<div class="screen">
<div id="id_color"></div>
<image style="left:0;top:0;" id="id_testPng" :src="gifIconSrc">{{gifIconSrc}}</image>
<text class="page" style="color: #f11515; font-size: 88;">auto Test</text>
<text class="testReturn" id="id_ng" onpress="touchedScreen" onclick="touch1Button" style="top: 300;left:22 ;color: #F44236;background-color: #858282;">点击1</text>
<text class="testReturn" id="id_back" onpress="touchedScreen" onclick="touched2Button" style="top: 300;left:174;color: #000000;background-color: #858282;">点击2</text>
<text class="testReturn" id="id_yes" onpress="touchedScreen" onclick="touch3Button" style="top: 300;left:326;color: #000000;background-color: #FF9700;">点击3</text>
</div>
</html>
<script>
var router = require("@system.router");
var lvgl = require("@native.lvgl7");
var autoUtil = require("autoFactoryTestUtil.js");
var manual = require("autoFactoryTest.js");
var idTimeOut = null;
export default {
data: {
colorBash: [
{
"val": 0xffffff,//纯白
"text": "纯白:#ffffff"//纯白
},
{
"val": 0xff0000,//纯红
"text": "纯红:#ff0000",//纯红
},
{
"val": 0x00ff00,//纯绿
"text": "纯绿:#00ff00",//纯绿
},
{
"val": 0x0000ff,//纯蓝
"text": "纯蓝:#0000ff",//纯蓝
},
{
"val": 0x000000,//纯黑
"text": "纯黑:#000000",//纯黑
},
],
colorBashPng: [
{
"val": "./../../../../../mnt/app/quicknode/tests/re860_factory/images/factory/factoryGradientColor.png",
"text": "渐变:png1",
},
{
"val": "./../../../../../mnt/app/quicknode/tests/re860_factory/images/factory/factoryWhileToBlack.png",
"text": "渐变:png2",//纯黑
},
],
imgCnt: 0,
pngCnt: 0,
gifIconSrc: "",
},
onshwKeyBuntton: function () {
console.log("!!onshwKeyBuntton")
this.checkFlagBash(0)
},
exitLedTest: function () {
console.log("!!exitLedTest")
this.clearTimer();
},
/*************** struct ***************/
onInit: function () {
//this.$setImageForceUpdate(true);
},
onShow: function () {
//console.log(this.$uri + "===========onShow=============")
this.$hide("id_testPng")
this.imgCnt = 0
this.pngCnt = 0
this.checkFlagBash(this.imgCnt)
this.imgCnt++
this.gifIconSrc = " ";
this.$hide("id_testPng")
this.checkFlagBash(0)
this.$show("id_yes")
this.$show("id_ng")
this.$show("id_back")
autoUtil.initPage(this)
},
onHide: function () {
//console.log(this.$uri + "===========onHide=============")
this.clearTimer()
},
onQuit: function () {
//console.log(this.$uri + "===========onQuit=============")
this.clearTimer()
},
resetLcd: function () {
if (this.imgCnt >= this.colorBash.length &&
this.pngCnt >= this.colorBashPng.length) {
this.imgCnt = 0;
this.pngCnt = 0;
this.gifIconSrc = " ";
this.$hide("id_testPng")
}
},
/*************** timer ***************/
ledtimerSet: function () {
this.resetLcd();
this.clearTimer();
idTimeOut = this.$setInterval(this.normalTimerOut, 300, [], this);
// this.$setTimeout(this.LedTimerOut, 5000, [], this);
},
resetUI: function () {
this.gifIconSrc = " ";
this.$hide("id_testPng")
this.checkFlagBash(0)
this.$hide("id_yes")
this.$hide("id_ng")
this.$hide("id_back")
},
normalTimerOut: function () {
this.resetLcd();
this.touchedScreen();
},
clearTimer: function () {
if (idTimeOut != null) {
this.$clearInterval(idTimeOut);
idTimeOut = null;
}
},
/*************** connect ***************/
/*************** event ***************/
shortDelay: function () {
this.$setX("id_ng", 800);
this.$setY("id_ng", 340);
//console.log("=====show id_ng successful!")
this.$clearTimeout(this.shortDelayId);
},
touchedScreen: function () {
if (this.imgCnt < this.colorBash.length) {
this.checkFlagBash(this.imgCnt)
this.imgCnt++
} else {
if (this.pngCnt < this.colorBashPng.length) {
this.checkFlagBash2(this.pngCnt)
this.pngCnt++
if (this.pngCnt == this.colorBashPng.length) {
//console.log("=====show id_yes successful!")
}
}
}
},
/*************** gesture ***************/
onGestureEvent: function (dir, x, y) {
//console.log(dir + " " + x + " " + y);
},
/*************** work ***************/
checkFlagBash: function (val) {
var temp = this.colorBash[val].val
//console.log("tempType = " + typeof temp)
//console.log("tempValue = " + temp)
this.$setBackgroundColor("id_color", temp)
},
checkFlagBash2: function (val) {
var temp = this.colorBashPng[val].val
//console.log("tempValue = " + temp)
this.gifIconSrc = temp;
this.$show("id_testPng")
},
touch1Button: function (node, topNode, x, y) {
autoUtil.ciclkAutoFactory(1)
manual.manualKeyBz("TOUCH1")
},
touched2Button: function (node, topNode, x, y) {
autoUtil.ciclkAutoFactory(2)
manual.manualKeyBz("TOUCH2")
},
touch3Button: function (node, topNode, x, y) {
autoUtil.ciclkAutoFactory(3)
manual.manualKeyBz("TOUCH3")
},
}
</script>
<style>
.screen {
left: 0px;
top: 0px;
width: 1280;
height: 1280;
background-color: gray;
}
/*********************************/
#id_color {
width: 1280;
height: 1280;
background-color: black;
}
#reminderText {
top: 40;
width: 500;
height: 100;
border-radius: 10;
border-width: 2;
border-color: white;
text-align: left;
/* text-overflow: ellipsis; */
font-size: 60;
color: white;
background-color: transparent;
}
.class_msg {
top: 240;
width: 200;
height: 100;
border-radius: 10;
border-width: 2;
border-color: white;
text-align: center;
text-overflow: ellipsis;
font-size: 60;
color: red;
background-color: gainsboro;
}
#id_yes {
bottom: 20px;
left: 20px;
}
#id_ng {
bottom: 20px;
right: 20px;
}
#id_back {
bottom: 20px;
right: 20px;
}
.testReturn {
position: absolute;
width: 130;
height: 130;
text-align: center;
text-overflow: ellipsis;
font-size: 40;
}
.page {
position: absolute;
top: 0;
width: 480;
height: 200;
text-align: center;
text-overflow: ellipsis;
font-size: 66;
}
</style>
\ No newline at end of file
var idtimerAutoFactory = null;
var continueFlag = true; //
var fd;
var fdCw;
var outTime = 0; //自动推出计数器
var touchFlag = 0;
var uart = require("uartUtil.js");
var _userdata = require("userdata.js");
var userData = _userdata.userData;
var autoUtil = require("autoFactoryTestUtil.js");
var uartMode = 2 //0:uart 1:uart_carrie_wave
/* ================= 1. 路由表:关键字 → 处理函数 ================= */
const cmdTable = [
{ kw: 'TEST_START', fn: doStart },
{ kw: 'VERSION', fn: doVersion },
{ kw: 'DUT_START=1', fn: doDutStart },
{ kw: 'SCAN', fn: doScan },
{ kw: 'OTG_TEST', fn: doOtg },
{ kw: 'LCD', fn: doLcd },
{ kw: 'TOUCHKEY', fn: doKeyBz },
{ kw: 'TEMPATURE', fn: doTempHum },
{ kw: 'WRITE_MAC', fn: doWriteMac },
{ kw: 'GET_MAC', fn: doGetMac },
{ kw: 'WRITE_SN', fn: doWriteSn },
{ kw: 'GET_SN', fn: doGetSn },
// { kw: 'WRITE_LIC', fn: doWriteLic },
{ kw: 'GET_CRC', fn: doGetCrc },
{ kw: 'TEST_END', fn: doEnd },
{ kw: 'RTC', fn: doGetRtc },
];
const cmdTableHex = [
{ kw: '57524954455F4C4943', fn: doWriteLic }, //WRITE_LIC
];
/* ================= 2. 统一入口:模糊匹配================= */
function event(fd, recvBuf) {
console.log('RX:' + recvBuf);
const hit = cmdTable.find(item => recvBuf.includes(item.kw));
if (hit) return hit.fn(fd, recvBuf);
}
function eventHex(fd, recvBuf) {
console.log('RXHEX:' + recvBuf);
const hit = cmdTableHex.find(item => recvBuf.includes(item.kw));
if (hit) return hit.fn(fd, recvBuf);
}
/* ================= 3. 处理函数 ================= */
function doStart(fd) {
setCountdownStatus(false); //关闭倒计时直到接到通知关闭
send(fd, '+START');
autoUtil.setAutoModeUI("INIT");
}
function doGetRtc(fd) {
var time = "+" + autoUtil.getRtcTimer()
console.log('RTC:' + time);
send(fd, time);
}
function doVersion(fd) {
var version = "+" + userData.softwareVersion
// var version = "+" + "V0.0.112"
console.log("version: " + version);
send(fd, version);
}
function doDutStart(fd) {
console.log("doDutStart");
autoUtil.startWifiScan();
send(fd, '+OK');
}
function doScan(fd) {
var value = autoUtil.getWifiScan();
if (value != "null") {
var scanList = "+" + autoUtil.getWifiScan();
console.log("scanList: " + scanList);
send(fd, scanList);
}
}
function doOtg(fd) { send(fd, '+OTG_OK'); }
var cntLcd = 0;
function doLcd(fd) {
cntLcd += 1;
if (cntLcd > 1) {
autoUtil.ledTimerOut();
manualLcdBz();
} else {
// autoUtil.setAutoModeUI("INIT");
autoUtil.setAutoModeUI("LCD");
}
}
function doTempHum(fd) {
var tempHum = "+" + autoUtil.getTempHum();
console.log("tempHum: " + tempHum);
send(fd, tempHum);
}
function doGetMac(fd) { send(fd, '+ab:cd:ef:12:34:56'); }
function doGetSn(fd) {
var sn = "+" + autoUtil.getSn();
console.log('get sn: ' + sn);
send(fd, sn);
}
function doGetCrc(fd) {
// var crc = "+" + autoUtil.getCrc();
var crc = autoUtil.testLicParce();
if (crc != "null") {
var crcVal = "+" + crc
console.log('crcVal: ' + crcVal);
send(fd, crcVal);
}
}
function doEnd(fd) {
send(fd, '+OK');
autoUtil.exitAutoFactoryPage();
exitAutoFactory();
}
function doWriteMac(fd, recvBuf) {
// const mac = recvBuf.split('=')[1].trim();
console.log('write mac:' + mac);
send(fd, '+OK');
}
function doWriteSn(fd, recvBuf) {
const sn = recvBuf.split('=')[1].trim();
console.log('write sn:' + sn);
autoUtil.writeSn(sn);
send(fd, '+OK');
}
function doWriteLic(fd, recvBuf) {
console.log("recvBuf:" + recvBuf)
var ret = autoUtil.licParce(recvBuf);
if (ret == 1) {
send(fd, '+OK');
} else if (ret == -2) {
if (uartMode == 1) {
restartUart();
} else if (uartMode == 2) {
restartUartCarrieWave();
}
}
// console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
// console.log('write lic:' + recvBuf);
// var hex = autoUtil.formatHexWithSpace(recvBuf)
// console.log('hex:' + hex);
// send(fd, '+OK');
}
function doKeyBz(fd, recvBuf) {
/* 人工弹窗可接 UI,这里直接返回 OK */
autoUtil.setAutoModeUI("INIT");
autoUtil.setAutoModeUI("TOUCH");
touchFlag = 1;
// send(fd, '+OK');
}
// 想加 TOUCH4、5…直接往后写
// 3 键就是 0b111
let collectMask = 0;
function manualKeyBz(buf) {
const KEYS = ['TOUCH1', 'TOUCH2', 'TOUCH3'];
const TARGET_MASK = (1 << KEYS.length) - 1; // 0b111
const idx = KEYS.indexOf(buf);
if (idx === -1) return;
collectMask |= 1 << idx;
if (collectMask === TARGET_MASK) {
collectMask = 0;
if (touchFlag == 1) {
send(getFd(), '+OK');
touchFlag = 0;
}
}
}
function manualLcdBz() {
send(getFd(), '+OK');
}
function initEvent(fd) {
autoUtil.startWifiScan();
setCountdownStatus(true);
}
function exitAutoFactory() {
if (idtimerAutoFactory != null) {
clearInterval(idtimerAutoFactory);
idtimerAutoFactory = null;
}
setCountdownStatus(true);
close(fd);
}
function uart() {
if (idtimerAutoFactory != null) {
clearInterval(idtimerAutoFactory);
idtimerAutoFactory = null;
}
fd = init();
if (fd < 0) {
console.log("!!!uart_init error");
return;
}
initEvent();
console.log("uart_init ok");
idtimerAutoFactory = setInterval(function () {
uartFd(fd);
// testUartFd(fd);
console.log("!!!auto ing :" + outTime);
//cnt30s(fd);
}, 1000)
}
function uartCarrieWave() {
if (idtimerAutoFactory != null) {
clearInterval(idtimerAutoFactory);
idtimerAutoFactory = null;
}
const baud_str = "4800";
const port = pickUartDev();
if (!port) return -1;
var setTimerOutStartUart = kill_tty(port);
setTimeout(() => {
// fd = init();
fd = initCarrieWave(port, baud_str)
if (fd < 0) {
console.log("!!!uart_init error");
return;
}
uart_set_mode(fd,"raw")
initEvent(fd);
console.log("uart_init ok");
idtimerAutoFactory = setInterval(function () {
uartFd(fd);
// testUartFd(fd);
console.log("!!!auto ing :" + outTime);
//cnt30s(fd);
}, 1000)
}, setTimerOutStartUart);
}
function autoFactoryTestTimer() {
if (uartMode == 1) {
uart();
}
else if (uartMode == 2) {
uartCarrieWave();
}
}
function testUartFd(fd) {
var recvBuf = recv(fd);
console.log("recv:" + recvBuf);
// event(fd, recvBuf)
var recvHexBuf = recvHex(fd)
console.log("recvHex:" + recvHexBuf);
send(fd, '+TESTSEND');
}
function uartFd(fd) {
var recvBuf = recv(fd);
console.log("recv:" + recvBuf);
event(fd, recvBuf)
var recvHexBuf = recvHex(fd)
// console.log("recvHex:" + recvHexBuf);
eventHex(fd, recvHexBuf)
}
function cnt30s(fd) {
if (outTime > 60) {
console.log("!!!outTime > 60");
exitAutoFactory();
return;
}
if (getCountdownStatus() == true)
++outTime;
}
function setCountdownStatus(sta) {
continueFlag = sta;
}
function getCountdownStatus() {
return continueFlag
}
function send(fd, cmd) {
var cmdBuf = cmd + "\r\n";
var len = cmdBuf.length; // 返回 8
console.log("TX:" + cmdBuf + "(len):" + cmdBuf.length);
return uart.uart_send(fd, cmdBuf, len);
}
function recv(fd) {
return uart.uart_recv(fd) || "";
}
function recvHex(fd) {
return uart.uart_recv_hex(fd) || "";
}
function recvBin(fd) {
return uart.uart_recv_bin(fd) || "";
}
function init() {
var port = "/dev/ttyS4";
var baud_str = "115200";
return uart.uart_init(port, baud_str);
}
function getUartConfig() {
return {
ln_sPort: "/dev/ttyProto",
old_port: "/dev/ttyS0",
baud_str: "115200"
}
}
// 统一找串口设备,返回选中的设备路径;失败返回 null
function pickUartDev() {
const { ln_sPort, old_port } = getUartConfig();
if (autoUtil.devExist(ln_sPort)) return ln_sPort;
console.log(`!!!${ln_sPort} not exist`);
if (autoUtil.devExist(old_port)) return old_port;
console.log(`!!!${old_port} not exist`);
return null;
}
function kill_tty(port) {
var setTimerOutStartUart = 0; // 无可用设备
var killed = uart.kill_tty_user(port);
if (killed > 0) {
console.log("Killed " + killed + " process(es) using " + port + ", waiting 200 ms...");
setTimerOutStartUart = 300
} else {
setTimerOutStartUart = 30
}
return setTimerOutStartUart
}
function initCarrieWave(port, baud_str) {
console.log("!!initCarrieWave");
uart.pwm_uart_init()
return uart.uart_init(port, baud_str);
}
function close(fd) {
if (fd >= 0) {
uart.uart_close(fd);
fd = -1;
}
}
function setFd(fdvar) {
fd = fdvar;
}
function getFd() {
return fd;
}
/* ========== 新增:重启串口 ========== */
function restartUart() {
console.log("!!=== restart Uart restart Uart restart Uart===");
/* 1. 如果之前已打开,先关闭 */
if (fd >= 0) {
uart.uart_close(fd);
fd = -1;
}
/* 2. 重新初始化 */
fd = init(); // 沿用原 init() 配置
if (fd < 0) {
console.log("reset usart error");
return -1;
}
console.log("reset usart success,fd=" + fd);
return fd;
}
/* ========== 新增:重启串口 ========== */
function restartUartCarrieWave() {
console.log("!!=== restart Uart restart Uart restart Uart===");
/* 1. 如果之前已打开,先关闭 */
if (fd >= 0) {
uart.uart_close(fd);
fd = -1;
}
/* 2. 重新初始化 */
const baud_str = "4800";
const port = pickUartDev();
if (!port) {
console.log("!!!get pickUartDev error");
return -1;
}
fd = initCarrieWave(port, baud_str)
if (fd < 0) {
console.log("reset usart error");
return -1;
}
uart_set_mode(fd,"raw")
console.log("reset usart success,fd=" + fd);
return fd;
}
function uart_set_mode(fd, mode){
switch(mode){
case "canonical": uart.uart_set_canonical(fd); break;
case "raw": uart.uart_set_raw(fd); break;
default: uart.uart_set_canonical(fd);
}
}
module.exports = {
autoFactoryTestTimer: autoFactoryTestTimer,
manualKeyBz: manualKeyBz,
manualLcdBz: manualLcdBz,
restartUart: restartUart
}
var systemCtrl = require("systemCtrl.js");
const fs = require("@system.fs")
var _userdata = require("userdata.js");
var userData = _userdata.userData;
var handleSNDataBinFile = require("handleSNDataBinFile.js");
var router = require("@system.router");
var bglight = require("bglight.js");
var envTemp = require("environmentTemperature.js");
var resultsWifiList = "null";
var SECURE_APP_PATH = "/mnt/app/eOpteeCtrl"
var that;
var scanFlag = true;
var idScanTimerOut = null;
var fullLic;
function parseWifiScanResults(output) {
const results = [];
/* 去掉文件头两行 */
const lines = output.split('\n').slice(2);
lines.forEach((raw, idx) => {
const l = raw.trim();
if (!l) return;
/* 原始行:02:b1:f7:70:1d:a2 2412 -37 [WPA2-PSK-CCMP][ESS] Waterworld-A8 */
const cols = l.split(/\t|\s{2,}/); // 按 Tab 或两个以上空格切
if (cols.length < 5) return;
const mac = cols[0]; // bssid
const rssi = parseInt(cols[2], 10); // signal level
const ssid = cols.slice(4).join(' ').trim(); // ssid 可能带空格
/* 拼成目标格式:01,name:xxx,rssi:-50,mac:xx-xx-xx-xx-xx-xx */
results.push({
idx: String(idx + 1).padStart(2, '0'), // 01 02 03 ...
name: ssid || '',
rssi: rssi,
mac: mac.split(':').join('-') // 统一成横杠
});
});
return results;
}
function enableScan() {
console.log("!!!!!!!!!!!!Is @@@@@@@@@@@@@@@@@@@@@@@@@@#enableScan")
var process = require("@system.process")
if (process.platform == "win32") {
console.log("its win32.")
return;
}
console.log("resultsWifiList:")
// if (resultsWifiList && oneFlag) {
// scanRefreshWifiList(resultsWifiList)
// oneFlag = 0;
// }
if (scanFlag == false) {
return;
}
system("rm -fr /tmp/scan_FactoryResult.txt")
system("wpa_cli -p /tmp/wpa_supplicant/sockets scan")
scanFlag = false;
if (idScanTimerOut != null) {
clearTimeout(idScanTimerOut);
idScanTimerOut = null;
}
idScanTimerOut = setTimeout(function () {
system("wpa_cli -p /tmp/wpa_supplicant/sockets scan_results > /tmp/scan_FactoryResult.txt")
var content = fs.read("/tmp/scan_FactoryResult.txt", "r")
if (content) {
console.log("content: " + content)
var listScanValue = parseWifiScanResults(content)
console.log("listScanValue------------" + JSON.stringify(listScanValue))
if (listScanValue.length > 0) {
resultsWifiList = listScanValue;
console.log(JSON.stringify(resultsWifiList))
}
}
scanFlag = true;
}, 5000)
}
function startWifiScan() {
enableScan()
}
function getWifiScan() {
console.log("getWifiScan")
if (resultsWifiList == "null" || resultsWifiList.length == 0)
return "null";
console.log("getWifiScan--------2")
var result = JSON.stringify(resultsWifiList);
console.log(result)
var rel = resultsWifiList.map(ap => `${ap.idx},name:${ap.name},rssi:${ap.rssi},mac:${ap.mac}`)
.join(';');
console.log(rel)
return rel;
}
function writeSn(sn) {
userData.aboutSNValue = sn;
handleSNDataBinFile.writeSNDataToBinFile(userData.aboutSNValue);
}
function getSn() {
return userData.aboutSNValue;
}
function getCrc() {
system(SECURE_APP_PATH + " -l > /tmp/crc.txt");
const txt = fs.read("/tmp/crc.txt", "r");
if (!txt) return "NA";
// 全局替换所有空白、冒号、回车、换行为空串
return txt.replace(/[\s:\r\n]+/g, '');
}
function setBright(vlaue) {
var valueB = Math.round(vlaue * 255 / 100);
bglight.ctrl(valueB);
}
function getRtcTimer() {
system('hwclock > /tmp/hwRtc.txt');
const raw = fs.read('/tmp/hwRtc.txt');
if (!raw) return 'NA';
/* 典型行:Thu Jan 1 18:22:21 1970 0.000000 seconds
只处理前 4 段:星期 月 日 时间 年 */
var p = 0;
while (p < raw.length && raw[p] != ' ') p++; // 跳过星期
while (p < raw.length && raw[p] == ' ') p++;
var monSt = p;
while (p < raw.length && raw[p] != ' ') p++; // 月份
var mon = raw.slice(monSt, p);
while (p < raw.length && raw[p] == ' ') p++;
var daySt = p;
while (p < raw.length && raw[p] != ' ') p++; // 日
var day = raw.slice(daySt, p);
while (p < raw.length && raw[p] == ' ') p++;
var timeSt = p;
while (p < raw.length && raw[p] != ' ') p++; // 时间 HH:MM:SS
var time = raw.slice(timeSt, p);
while (p < raw.length && raw[p] == ' ') p++;
var yearSt = p;
while (p < raw.length && raw[p] != ' ') p++; // 年
var year = raw.slice(yearSt, p);
/* 月份英文→数字 */
var monTbl = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var mo = 1;
for (var i = 0; i < 12; i++) {
if (mon === monTbl[i]) { mo = i + 1; break; }
}
/* 去掉前导空格并拼成目标格式 */
day = day.trim();
return year + '-' + mo + '-' + day + ' ' + time;
}
// value == 1,2,3
function ciclkAutoFactory(value) {
var arr = [0, 50, 100]
setBright(arr[value - 1])
console.log("!!ciclkAutoFactory:" + arr[value - 1])
}
function getTemp() {
return envTemp.getTemper();
}
function getHumid() {
return envTemp.getHumid();
}
function getTempHum() {
return getTemp() + "," + getHumid();
}
function writeLic(lic) {
handleSNDataBinFile.writeLicenseToBinFile(lic)
handleSNDataBinFile.writeSNDataToBinFile(lic)
handleSNDataBinFile.writeInnerSNDataToBinFile(lic)
}
/**
* 将十六进制字符串按每 2 字符插入一个空格
* @param {string} hexStr - 仅含 0-9A-Fa-f 的十六进制字符串
* @returns {string} - 带空格的格式,例如 "41 54 2B ..."
*/
function formatHexWithSpace(hexStr) {
// 去除可能存在的 0x 前缀、空白及非法字符
const clean = hexStr.replace(/^(0x)?/i, '').replace(/\s+/g, '').toUpperCase();
// 按 2 字符切割后拼接
return clean.match(/.{1,2}/g)?.join(' ') || '';
}
var autoMode = "";
function getAutoStartMode() {
console.log("getAutoStartMode")
return autoMode
}
function initPage(id) {
console.log("!!init id:"+ id)
that = id;
}
function ledTimerOut() {
that.clearTimer()
that.resetUI();
}
function setAutoModeUI(mode) {
console.log("!!that:"+ that)
console.log("!!setAutoModeUI: " + mode)
switch (mode) {
case "LCD":
that.ledtimerSet()
that.$hide("id_yes")
that.$hide("id_ng")
that.$hide("id_back")
break;
case "TOUCH":
console.log("111")
that.exitLedTest();
that.onshwKeyBuntton();
console.
break;
case "INIT":
autoMode = mode;
router.push({
path: "autoFactory"
});
require("autoOffScreen.js").onOffAtuoOffScreen();
setBright(100)
var soundLevelText = "tinymix set 16 7"
system(soundLevelText);
break;
default:
break;
}
}
function exitAutoFactoryPage() {
// router.push({
// path: "getStart"
// });
system("reboot 1")
}
// 41542B57524954455F4C49433D32312F333232298B6D8E21B368DBD9FE04DADE18BE74CF0BF181510CE7A3AE5C059A7FA2F990DF6DD94932D5C269EFD96337776AF6088DAC20D5F4F3E57A521803F3F0B9EC0D0D0A0A
// formatHexWithSpace -- 41 54 2B 57 52 49 54 45 5F 4C 49 43 3D 32 31 2F 33 32 32 29 8B 6D 8E 21 B3 68 DB D9 FE 04 DA DE 18 BE 74 CF 0B F1 81 51 0C E7 A3 AE 5C 05 9A 7F A2 F9 90 DF 6D D9 49 32 D5 C2 69 EF D9 63 37 77 6A F6 08 8D AC 20 D5 F4 F3 E5 7A 52 18 03 F3 F0 B9 EC 0D 0D 0A 0A
// 41542B57524954455F4C49433D header
// 41542B57524954455F4C49433D 332F3332 //332F3332 // 2F后面为总段(固定32段),前面为当前段 //3332 - 为总段
// 1、分割字符串 模糊匹配57524954455F4C4943 分割3D后面的字符串
// 2、获取段数和总段 如332F3332 在F3332前面的字符串在进行分割 得到数据段32298B6D8E21B368DBD9FE04DADE18BE74CF0BF181510CE7A3AE5C059A7FA2F990DF6DD94932D5C269EFD96337776AF6088DAC20D5F4F3E57A521803F3F0B9EC0D0D0A0A
// 3、删除结尾0D0D0A0A 得到完整数据段
// 4、使用formatHexWithSpace函数转换为16进制格式字符串(放在前面执行)
// 5、将当前段数存储到对应的数组中
// 6、当段数等于总段时候将数组所有数据拼接成字符串
// 7、将数据通过参数方式传入autoUtil.writeLic(lic);
function hexToAscii(hexStr) {
let result = '';
for (let i = 0; i < hexStr.length; i += 2) {
const byte = hexStr.substr(i, 2);
const charCode = parseInt(byte, 16);
result += String.fromCharCode(charCode);
}
return result;
}
/* ========== 全局 ========== */
const licSegArray = []; // 下标 = 段号-1
function licParce(recvBuf) {
var comallArrLicVal = '4954455F4C4943';
var allArrLicVal = '3332'; // 外部可改,总段号(16 进制字符串)
var equal = '3D';
// console.log('licParce recv:' + recvBuf);
var buf = recvBuf;
var titleBuf = recvBuf;
/* 1. 以 comallArrLicVal 为分隔符劈成两段 */
const pos = buf.indexOf(comallArrLicVal);
// console.log("pos:" + pos)
if (pos === -1) return -1;
let core = buf.slice(pos + comallArrLicVal.length + 12); // 后半段
// console.log("!!!!1_ core:" + core)
// console.log("1--core:" + core);
/* 2. 去尾 D0D0A0A */
// if (core.endsWith('D0D0A0A'))
core = core.slice(0, -8);
console.log("recv:" + core);
const expectedHexLen = 128; // 64 字节 = 128 个十六进制字符
if (core.length !== expectedHexLen) {
console.log(`长度错误:期望 ${expectedHexLen},实际 ${core.length}`);
return -2;
} else {
console.log(`长度正确:期望 ${expectedHexLen},实际 ${core.length}`);
}
const posT = titleBuf.indexOf(equal);
let coreT = titleBuf.slice(posT + equal.length);
// console.log("coreT:" + coreT);
const slashPos = coreT.indexOf('2F');
if (slashPos === -1) { return -1; }
const segHex = coreT.slice(0, slashPos); // 例如 "31"
// Hex 字符串当成 ASCII 解码
// Hex → 字节 → ASCII → 数字
// const segChar = String.fromCharCode(parseInt(segHex, 16)); // '1'
const curSeg = hexToAscii(segHex);
const totSeg = hexToAscii(allArrLicVal);
console.log("recv index:" + curSeg);
console.log("all index:" + totSeg);
/* 4. 格式化并缓存 */
// const spaced = formatHexWithSpace(core);
const spaced = core;
licSegArray[curSeg - 1] = spaced;
console.log(`seg=${curSeg}/${totSeg} data=${spaced}`);
// /* 5. 末段到齐 → 拼接写入 */
if (curSeg === totSeg && licSegArray.every(v => v)) {
fullLic = licSegArray.join('').replace(/\s+/g, '');
// const fullLic = licSegArray.join('').replace(/\s+/g, '');
writeLic(fullLic);
console.log('Full license written:' + fullLic);
licSegArray.length = 0;
}
return 1;
}
function testLicParce() {
// var fullLic = "202C3AE80810DA0000000000000000005F93DC7F18DDC42C761F2EADA48223F1D966861A2AC39B28F7C267BB282775556A57312196FC891E77BC3423E9685DD3CBE6DE5637B4D4BA26994AE0C94B103E3CE725C09825E2BCF2D0FBF5A\
// 4A72250EA508EA8CA3450BB82EA7972C3B621134714E7485299C095D05AB13BC8A9A8BC952AFBD0F003F72C29C5B6B3EC95EC12D3EA67CDDF0CA5324AC7C15A69D0D72B0F7951FDB05D6D6C5196AD1FD27074D08E13A5088B8B2C8C65A5EB21AE379E313F78E15\
// 6ACB89E4216AAAB3C2374CCF9BD64829FB55C83E98883FC385B34AEBD2C39BE5AE52A7EA41F1DA928859CE10BA28C6F42073792CD083D060DF501677D818ED55AF9CCCBFE7F0B8DD58C720F52A08B53049E9C0B88CD85BB4BC8D389C635F3A7815D38CF8A4DAA3\
// E75D27A45CD7F677728E86E8FA02392A9245CBE0C723D6C400483244A2AA3C78FD27692833BF6B4FCE9CF469DAF22A4AFEFAC0F1E04AC8A7A35931DC5A253A3BC35677F561CA128A28B5A3C2F76D6B283A84A14F60AB1B905CBDB9CDA45B585ECDE579834D8540\
// 2EEA279CBB75F4A7585AF311EE856A41E4A1174C0A9835C24B8AD55EE94E1F51F25E03AB3AE4DEC4153909F30D4A263961275F82C7599128450C3937068BCF2D8A6217DC7ED515AFB8FA5D19A4C7E97D7D43A54D128CBFE49AA5A06B189D59BB3AEC89525C4C3E\
// 09244CD6E8D8F94A055E00C16B6A3AFEF08860ECD9F83FC6D9549DBE54D8C0703C5052741C4C8791CD0B3CCB7C01F0114F340DC5778B6659515E8117A3720336DB4C8AD9388937107925BE11D69C0052EE614B989450E8E59108483E1AFFEA3004D05E13F27DBE\
// 37D31B2DC6F5D05A417E802D226F2A200021D906EFBE492E3507C22EF3C12427625E696847104EEAD6DBBC4256ABCA03C8A0330D23F9D412798F1AC7DAEA0DD4848C77EE97391CFD9C0DC6262DAE0CF09806CE17A17D8FB90AEA8FA1BFB9DE32ED05C1C3FB817E\
// 657F320466748643E3A7B5748982F080EF83B9E61063F4A7AA65BC16CA3A0713FF4C522F8C5CF5044B5074FFA9D0901837B83FEFE6C4E101EB4B8BED41B078F044071FCE51798B41905AA5A8C7AD83110F57303B9CE9685FB507F8912A12576968138B3C2191A9\
// E23BE7F7EB74E40EFAEDD84891C9990F44D1003B9AD75462DB7E1388097D36BC7460BAF70FAA748A68BFBB6DB4062504548F99703A7BD0220582A54913A9556DF9E75B5B49FC2A6868A05FC0FD9E9A6AFE11E2FA6BE61B63ECA9B824F5FEFAED65EE6E717DF77F\
// 0AA7EA169BC592DFE9B259F631BDF500913675EC3B6C9EC590713E55FFDFC0755D59BF061E24DA89EA57043BF7C336A0C6194BF9858D7384E68B90E3FE6977E873FA274FE087079D33003537B5748982F080EF83B9E61063F4A7AA675D0DB5DFCC4FE811ED5783\
// B7224413642146428096DA280CEA85F5378279C3F5794DCCFA3579127BF0BD098B127099889F0170E39920C1B19C9042BB28ECD2C656FF58FA4C48A7DC463F04E4AFE977096E6B7BF282B5545D54090A7E3C1BF49B154DD733C2617A33AB7CE39446C398E0A6F9\
// 0EA70526A299B8FEBFFCEB3A791FEA790CAA610E878DFE6540F6909B9C07DDB22072BA29F88462049F4EB6337764CCA84ADE9F9C07FE097C27E15B5D35BA03F9EC1829C73ED33C2297915EC047BE6E617A5B251BF0B79206FE7BDF694AAE408DC705DB7918081E\
// E71EBC5276B22B6F817AC47DC8D44EC363A849CA57BA736AA18A5110FE6BC60490165B17A7CDA7B5748982F080EF83B9E61063F4A7AA632298B6D8E21B368DBD9FE04DADE18BE74CF0BF181510CE7A3AE5C059A7FA2F990DF6DD94932D5C269EFD96337776AF60\
// 88DAC20D5F4F3E57A521803F3F0B9EC740B79EAF795A3A44F54F9528DF80B7B10C80830C248C0DA0A0390C8A413A3C38BB7CF499B93B2E3787AAEB70276EB6B87255B8CA730F64EABD9D908422C10762AD22E0B869BDA06198E50F5B21A7D8F002A38537B03D45\
// FCADD5DC61AC5BD01FD29A924C67B38C32DDA61EB15CD5542C6E44790CD77A1ACF499855426A086F464767F0E366FD4C952A0C5532A3E2481FF9097C1674B49F3BEE0B290E321AE8EFF623D08E8413FF0866B6785DFCD770886DBB220648812D5330788301F8AF\
// 3F07B5748982F080EF83B9E61063F4A7AA6720A13ECEF43B746BDE51C48F760503D7A9399EF5DD8F5FA7F09321BBAA0945B233C6469B520F357A6C9DDA6BCCE46CC958178FCD173DB951D15559F1147BB40897A06C2B27D7EEC04AAF191A38B297AF5CEE70BCB0\
// CBF6AE6139BF31F619FBB67B7381761C6B45ED8CA07B43DB8A72D7F1F8E94ECA1916249B4D0338BC1BB17627FBC2DAAD8145329492721B8698475415278BA579696419F378CB387A811D2B8E678A7E5154DD5692D61632ABB7A51B5F03BCB17B9A971C39E1C2C9\
// 2266F8433901D31261F04FB8C6C3EAF59BCAB4022079AA2A651C95199371A3465A5E98F052B959D669B89C114A3BDD69E973598FF907C495ABFFD326BB66A6503F697245F93DC7F18DDC42C761F2EADA48223F1D966861A2AC39B28F7C267BB28277555C80FC78\
// 8423B3E667442E56A6AEA5D4CD0707E03103DF2AF339F2AD67F4AAEBF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\
// 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BBE7700481CE78FC040BB1DDE4D8FE55985227C9A42401FA7D33DD09E5CD83A4"
if (!fullLic || fullLic == undefined) {
console.log("fullLic error")
return "null";
}
// console
const cksum = figureChecksum(fullLic, 2048);
// console.log('checksum = 0x' + cksum.toString(16).padStart(2, '0'));
return "0X" + cksum.toString(16).padStart(2, '0').toUpperCase();
}
/**
* 累加和校验(8 位无符号)
* @param {String} hexStr 连续 HEX 字符串,不限长度
* @param {Number} size 要校验的字节数(<= 字节长度)
* @returns {Number} 0-255 的校验和
*/
function figureChecksum(hexStr, size = 2048) {
const bytes = hexStr.match(/.{2}/g).map(b => parseInt(b, 16));
const len = Math.min(size, bytes.length);
let checksum = 0;
for (let i = 0; i < len; i++) {
checksum = (checksum + bytes[i]) & 0xFF; // 强制 8 位
}
return checksum;
}
function initUartPWM() {
system("/mnt/app/quicknode/shell/uartPwmInit.sh &");
}
function hexStringToBytes(hexStr) {
const bytes = new Uint8Array(hexStr.length / 2);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(hexStr.substr(i * 2, 2), 16);
}
return bytes;
}
// 入参:设备路径 返回:1 存在,0 不存在
function devExist(devPath) {
const tmpFlag = "/tmp/dev_exist.flag";
system(
`[ -e '${devPath}' ] && echo 1 > ${tmpFlag} || echo 0 > ${tmpFlag}`
);
const ret = fs.read(tmpFlag, "r");
return (ret && ret.trim() === "1") ? 1 : 0;
}
function system(cmd) {
systemCtrl.executeSystemCommand(cmd)
}
module.exports = {
startWifiScan: startWifiScan,
getWifiScan: getWifiScan,
writeSn: writeSn,
getSn: getSn,
getCrc: getCrc,
getAutoStartMode: getAutoStartMode,
setBright: setBright,
ciclkAutoFactory: ciclkAutoFactory,
getTempHum: getTempHum,
writeLic: writeLic,
formatHexWithSpace: formatHexWithSpace,
initPage: initPage,
setAutoModeUI: setAutoModeUI,
ledTimerOut: ledTimerOut,
exitAutoFactoryPage: exitAutoFactoryPage,
licParce: licParce,
figureChecksum: figureChecksum,
testLicParce: testLicParce,
initUartPWM: initUartPWM,
devExist: devExist
}
var dlfcn = require("dlfcna.js");
console.log(dlfcn)
var funcs = [
["uart_init", "iss"],
["uart_close", "vi"],
["uart_send", "iisi"],
["uart_recv", "si"],
["uart_recv_hex", "si"],
["kill_tty_user", "is"],
["pwm_uart_init", "vv"],
["uart_set_canonical", "vi"],
["uart_set_raw", "vi"]
];
console.log("in uartUtil.js")
function register() {
console.log("will register uartUtil.so")
var isSuccess = dlfcn.register("@native.uartUtil", "/mnt/app/quicknode/tests/re860_factory/lib/factory/uartUtil.so", funcs);
if (isSuccess) {
console.log("load so successfully")
var uartUtil = require("@native.uartUtil");
return uartUtil
} else {
console.log("load so failed")
}
return undefined;
}
var uartUtil = register();
function uart_init(port, baud_str) {
// int uart_init(const char *port, const char *baud_str);
return uartUtil.uart_init(port, baud_str);
}
function uart_close(fd) {
// void uart_close(int fd);
uartUtil.uart_close(fd);
}
function uart_send(fd, data, len) {
// int uart_send(int fd, const void *data, size_t len);
return uartUtil.uart_send(fd, data, len);
}
function uart_recv(fd) {
// char *uart_recv(int fd)
return uartUtil.uart_recv(fd);
}
function uart_recv_hex(fd) {
console.log("!!uart_recv_hex:" + fd);
// char *uart_recv(int fd)
// console.log("uart_recv_hex:" + fd);
return uartUtil.uart_recv_hex(fd);
}
function kill_tty_user(port) {
console.log("!!kill_tty_user:" + port);
// const char *port = "/dev/ttyS0";
// int killed = kill_tty_user(port);
return uartUtil.kill_tty_user(port);
}
function pwm_uart_init() {
console.log("!!pwm_uart_init");
uartUtil.pwm_uart_init();
}
function uart_set_canonical(fd) {
console.log("!!!!!! Please use C code to modify rx_thread !!!!!");
return;
console.log("!!uart_set_canonical:" + fd);
// 串口设为 canonical 模式 //
uartUtil.uart_set_canonical(fd);
}
function uart_set_raw(fd) {
console.log("!!uart_set_raw:" + fd);
// 串口设为 raw 模式
uartUtil.uart_set_raw(fd);
}
module.exports = {
uart_init: uart_init,
uart_close: uart_close,
uart_send: uart_send,
uart_recv: uart_recv,
uart_recv_hex: uart_recv_hex,
kill_tty_user: kill_tty_user,
pwm_uart_init: pwm_uart_init,
uart_set_canonical: uart_set_canonical,
uart_set_raw: uart_set_raw
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <signal.h>
#include <sys/stat.h> // 放在现有 #include 区域任意位置
#include <stdint.h> // 就有 uint8_t
#define DEBUG 0 /* 1=打开调试 0=关闭打印 */
#if DEBUG
#define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define DBG(fmt, ...) \
do \
{ \
} while (0)
#endif
#define BUFFER_SIZE 4096
#define SEND_SIZE 1024
static char g_rxbuf[BUFFER_SIZE] = {0}; /* 最新一帧数据(普通文本) */
static char g_rxbuf_hex[BUFFER_SIZE] = {0}; /* 最新一帧数据(16进制) */
static int g_rxlen = 0; /* 有效长度 */
static int g_rxHexlen = 0; /* 有效长度 */
static pthread_mutex_t g_rx_mutex = PTHREAD_MUTEX_INITIALIZER; /* 互斥锁 */
/* ---------------- 串口底层 ---------------- */
/**
* @brief 接收数据
* @param fd 串口描述符
* @return 接收到的数据
*/
char *uart_recv(int fd)
{
static char ret[BUFFER_SIZE];
pthread_mutex_lock(&g_rx_mutex);
if (g_rxlen > 0)
{
memcpy(ret, g_rxbuf, g_rxlen + 1);
g_rxlen = 0; /* 消费掉 */
}
else
{
ret[0] = '\0'; /* 暂无数据 */
}
// DBG("Received Text Data: %s\n", ret); // 打印接收到的普通文本数据
pthread_mutex_unlock(&g_rx_mutex);
return ret;
}
/**
* @brief 以16进制形式读取数据
* @param fd 串口描述符
* @return 16进制字符串
*/
char *uart_recv_hex(int fd)
{
static char ret[BUFFER_SIZE * 3 + 1]; // 每个字节需要2个字符表示16进制,加上空字符
pthread_mutex_lock(&g_rx_mutex);
if (g_rxHexlen > 0)
{
for (int i = 0; i < g_rxHexlen; i++)
{
sprintf(&ret[i * 2], "%02X", (unsigned char)g_rxbuf_hex[i]);
}
ret[g_rxHexlen * 2] = '\0'; // 添加空字符
g_rxHexlen = 0; /* 消费掉 */
// DBG("Received Hex Data: %s\n", ret); // 打印接收到的16进制数据
}
else
{
ret[0] = '\0'; /* 暂无数据 */
}
pthread_mutex_unlock(&g_rx_mutex);
return ret;
}
/**
* @brief 接收线程函数
* @param arg 串口文件描述符
* @return NULL
*/
// static void *rx_thread(void *arg)
// {
// int fd = (int)(long)arg;
// char tmp[BUFFER_SIZE];
// int n;
// while ((n = read(fd, tmp, sizeof(tmp))) > 0) {
// pthread_mutex_lock(&g_rx_mutex);
// if (n > BUFFER_SIZE - 1) n = BUFFER_SIZE - 1;
// tmp[n] = '\0';
// DBG("Received Text Data: %s\n", tmp);
// g_rxlen = n;
// g_rxHexlen = n;
// memcpy(g_rxbuf, tmp, n + 1);
// memcpy(g_rxbuf_hex, tmp, n + 1); /* 16进制 */
// pthread_mutex_unlock(&g_rx_mutex);
// }
// return NULL;
// }
// 4800 -- 115200 通信优化
/* 在 rx_thread.c 里增加 */
static void submit_frame_with_fix(const unsigned char *line, int n)
{
pthread_mutex_lock(&g_rx_mutex);
/* 先原样放进缓冲区 */
int len = n;
memcpy(g_rxbuf, line, len);
/* 修复帧尾:单 0D0A → 双 0D0D0A0A */
if (len >= 2 &&
g_rxbuf[len - 2] == 0x0D &&
g_rxbuf[len - 1] == 0x0A)
{
g_rxbuf[len - 2] = 0x0D;
g_rxbuf[len - 1] = 0x0D;
g_rxbuf[len] = 0x0A;
g_rxbuf[len + 1] = 0x0A;
len += 2;
}
/* 统一更新长度和三份缓冲区 */
g_rxlen = len;
g_rxHexlen = len;
memcpy(g_rxbuf_hex, g_rxbuf, len); /* 现在长度已 fix */
pthread_mutex_unlock(&g_rx_mutex);
}
// 4800 -- 115200 通信优化 canonical
// static void *rx_thread(void *arg)
// {
// int fd = (int)(long)arg;
// char line[BUFFER_SIZE];
// for (;;)
// {
// /* canonical 下 read 会一次性返回一行(含 \n) */
// ssize_t n = read(fd, line, sizeof(line) - 1);
// if (n <= 0) continue;
// line[n] = '\0'; /* 给 DBG 用 */
// /* 简单校验:头俩字节 "AT",尾两字节 "\r\n" */
// if (n >= 4 &&
// line[0] == 'A' && line[1] == 'T' &&
// line[n-2] == '\r' && line[n-1] == '\n')
// {
// // pthread_mutex_lock(&g_rx_mutex);
// // g_rxlen = g_rxHexlen = n;
// // memcpy(g_rxbuf, line, n);
// // memcpy(g_rxbuf_hex, line, n);
// // pthread_mutex_unlock(&g_rx_mutex);
// submit_frame_with_fix(line, n); /* 修复+提交 */
// /* 日志 */
// DBG("[FRAME] %zd B: %s", n, line);
// }
// else
// {
// DBG("[SKIP] %zd B: %s", n, line);
// }
// }
// return NULL;
// }
// raw
static void *rx_thread(void *arg)
{
int fd = (int)(long)arg;
uint8_t fifo[BUFFER_SIZE];
int wp = 0;
for (;;)
{
uint8_t ch;
ssize_t n = read(fd, &ch, 1); /* 阻塞到 1 字节 */
if (n <= 0)
continue;
fifo[wp++] = ch; /* 原样保存,含 0x00 0x0D 0x7F … */
if (wp >= BUFFER_SIZE)
wp = 0; /* 简单回绕,防止溢出 */
DBG("[FIFO] len=%d: ", wp);
for (int i = 0; i < wp; ++i)
DBG("%02X ", fifo[i]);
DBG("\n");
/* 判帧尾:收到 0x0A 就认为一帧结束(可按需要改成 0x0D 0x0A) */
// if (ch == 0x0A) {
if (wp >= 2 &&
fifo[wp - 2] == 0x0D &&
fifo[wp - 1] == 0x0A)
{
submit_frame_with_fix((const unsigned char *)fifo, wp);
wp = 0; /* 准备收下一帧 */
}
}
return NULL;
}
// static void *rx_thread(void *arg)
// {
// int fd = (int)(long)arg;
// uint8_t fifo[BUFFER_SIZE];
// int wp = 0;
// /* 帧尾模板:0D 0D 0A 0A */
// static const uint8_t tail[4] = {0x0D, 0x0D, 0x0A, 0x0A};
// for (;;) {
// uint8_t ch;
// ssize_t n = read(fd, &ch, 1);
// if (n <= 0) continue;
// fifo[wp++] = ch;
// if (wp >= BUFFER_SIZE) wp = 0; /* 回绕保护 */
// /* 只有末尾 4 字节完全匹配时才提交 */
// DBG("[FIFO] len=%d: ", wp);
// for (int i = 0; i < wp; ++i)
// DBG("%02X ", fifo[i]);
// DBG("\n");
// if (wp >= 4 &&
// fifo[wp - 4] == tail[0] &&
// fifo[wp - 3] == tail[1] &&
// fifo[wp - 2] == tail[2] &&
// fifo[wp - 1] == tail[3])
// {
// submit_frame_with_fix(fifo, wp); /* 此时长度已是 128+2=130 */
// wp = 0; /* 准备下一帧 */
// }
// }
// return NULL;
// }
/**
* @brief 配置已打开串口的波特率及 8N1+无流控+阻塞超时参数
* @param[in] fd : 串口文件描述符(已被 open)
* @param[in] baudrate : speed_t 波特率,如 B115200
* @return 0 成功,-1 失败
*/
int init_serial(int fd, speed_t baudrate)
{
struct termios tty;
if (tcgetattr(fd, &tty) != 0)
return -1;
cfmakeraw(&tty);
cfsetispeed(&tty, baudrate);
cfsetospeed(&tty, baudrate);
tty.c_cflag &= ~PARENB; /* 8N1 */
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8 | CREAD | CLOCAL;
tty.c_cflag &= ~CRTSCTS; /* 无流控 */
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tty.c_oflag &= ~OPOST;
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 50;
return tcsetattr(fd, TCSANOW, &tty);
}
/**
* @brief 把字符串数字波特率转成 speed_t
* @param str 例如 "115200"
* @return speed_t,非法返回 B0
*/
static speed_t baud_str2speed(const char *str)
{
unsigned long b = strtoul(str, NULL, 10);
switch (b)
{
case 4800: /* ← 新增 */
return B4800;
case 9600:
return B9600;
case 19200:
return B19200;
case 38400:
return B38400;
case 57600:
return B57600;
case 115200:
return B115200;
#ifdef B230400
case 230400:
return B230400;
#endif
#ifdef B460800
case 460800:
return B460800;
#endif
default:
return B0; /* 非法 */
}
}
/**
* @brief 打开并初始化串口(阻塞模式)
* @param port 设备路径,如 "/dev/ttyS1"
* @param baud_str 波特率字符串,如 "115200"
* @return >=0 成功返回 fd;<0 失败
*/
int uart_init(const char *port, const char *baud_str)
{
speed_t spd = baud_str2speed(baud_str);
if (spd == B0)
return -1;
int fd = open(port, O_RDWR | O_NOCTTY);
if (fd < 0)
return -1;
if (init_serial(fd, spd) != 0)
{
close(fd);
return -1;
}
/* 1. 丢弃上下电残留字节
2. 清空内核接收/发送队列
3. 等待硬件 FIFO 排空 */
tcflush(fd, TCIOFLUSH);
tcdrain(fd);
pthread_t tid;
pthread_create(&tid, NULL, rx_thread, (void *)(long)fd);
pthread_detach(tid); /* 自生自灭 */
return fd;
}
/**
* @brief 关闭串口
* @param fd 串口描述符
*/
void uart_close(int fd)
{
if (fd >= 0)
close(fd);
}
/**
* @brief 发送数据
* @param fd 串口描述符
* @param data 数据指针
* @param len 数据长度
* @return 成功发送的字节数
*/
int uart_send(int fd, const char *data, int len)
{
const char *p = data;
int n = 0;
while (n < len)
{
ssize_t r = write(fd, p + n, len - n);
if (r <= 0)
return -1;
n += r;
}
tcdrain(fd);
// DBG("TX:%.*s (%d bytes)\n", len, p, len);
return 1;
}
/* 新增:释放指定 TTY 的占用进程 */
int kill_tty_user(const char *tty)
{
DBG("kill_tty_user: %s\n", tty);
DIR *dir;
struct dirent *ent;
char path[64], link[256];
int killed = 0;
if (!(dir = opendir("/proc")))
return 0;
while ((ent = readdir(dir)))
{
if (ent->d_name[0] < '0' || ent->d_name[0] > '9')
continue;
snprintf(path, sizeof(path), "/proc/%s/fd", ent->d_name);
DIR *fd_dir = opendir(path);
if (!fd_dir)
continue;
struct dirent *fd_ent;
while ((fd_ent = readdir(fd_dir)))
{
if (fd_ent->d_name[0] == '.')
continue;
snprintf(link, sizeof(link), "%s/%s", path, fd_ent->d_name);
char real[256];
ssize_t r = readlink(link, real, sizeof(real) - 1);
if (r > 0)
{
real[r] = 0;
if (strcmp(real, tty) == 0)
{
pid_t pid = atoi(ent->d_name);
if (pid > 1)
{
kill(pid, SIGKILL);
killed++;
}
}
}
}
closedir(fd_dir);
}
closedir(dir);
return killed;
}
void pwm_uart_init(void)
{
struct stat st;
char pwm_path[128];
snprintf(pwm_path, sizeof(pwm_path), "/sys/class/pwm/pwmchip0/pwm2");
if (stat(pwm_path, &st) != 0)
{
system("echo 2 > /sys/class/pwm/pwmchip0/export");
}
system("echo 5000 > /sys/class/pwm/pwmchip0/pwm2/period");
system("echo 2500 > /sys/class/pwm/pwmchip0/pwm2/duty_cycle");
system("echo normal > /sys/class/pwm/pwmchip0/pwm2/polarity");
system("echo 1 > /sys/class/pwm/pwmchip0/pwm2/enable");
}
// 4800 -- 115200 通信优化
/*
* 把已打开的串口设为 canonical 模式(行缓冲),
* 行结束符='\n',保持 4800 8N1 不变
*/
void uart_set_canonical(int fd)
{
DBG("[CANON] uart_set_canonical\n");
struct termios t;
if (tcgetattr(fd, &t) < 0)
{
perror("tcgetattr");
return;
}
/* 打开 canonical 模式,让驱动拼到 \n 再一次性返回 */
t.c_lflag |= ICANON; /* 规范输入 */
t.c_cc[VEOL] = '\n'; /* 附加行结束符(可省,默认就是 \n) */
t.c_cc[VEOF] = 0; /* 禁用 EOF 字符,防止提前返回 */
t.c_cc[VMIN] = 0; /* canonical 下 VM IN/VTIME 被忽略 */
t.c_cc[VTIME] = 0;
/* 保持 raw 输出,避免回显、信号等干扰 */
t.c_lflag &= ~(ECHO | ECHONL | ISIG);
t.c_oflag &= ~OPOST;
if (tcsetattr(fd, TCSANOW, &t) < 0)
perror("tcsetattr canonical");
else
DBG("[CANON] 4800 8N1 canonical mode enabled\n");
}
void uart_set_raw(int fd)
{
DBG("[RAW] uart_set_raw\n");
struct termios t;
if (tcgetattr(fd, &t) < 0)
{
perror("tcgetattr");
return;
}
cfmakeraw(&t); /* 已经帮我们关掉 ICANON ECHO … 所有标志 */
t.c_cflag &= ~CRTSCTS; /* 保持无流控 */
t.c_iflag &= ~(IXON | IXOFF | IXANY);
t.c_cc[VMIN] = 1; /* 收到 1 字节就返回 */
t.c_cc[VTIME] = 0; /* 不设超时 */
if (tcsetattr(fd, TCSANOW, &t) < 0)
perror("tcsetattr raw");
else
DBG("[RAW] 4800 8N1 raw mode enabled\n");
}
int main(void)
{
const char *port = "/dev/ttyS0";
/* 1. 先释放占用(不改动任何原有函数) */
int killed = kill_tty_user(port);
if (killed > 0)
{
DBG("Killed %d process(es) using %s, waiting 200 ms...\n", killed, port);
usleep(200000);
}
pwm_uart_init();
// int killed = kill_tty_user(port);
// if (killed > 0) {
// DBG("Killed %d process(es) using %s, waiting 200 ms...\n", killed, port);/* */
// usleep(200000);
// }
int fd = uart_init(port, "4800");
if (fd < 0)
{
perror("uart_init");
return -1;
}
/* ******** 在这里切 canonical ******** */
// uart_set_canonical(fd);
uart_set_raw(fd);
for (;;)
{
sleep(1);
char *txt = uart_recv(fd);
if (txt[0])
{
DBG("TXT: %s\n", txt);
}
char *txthex = uart_recv_hex(fd);
if (txthex[0])
{
DBG("TXTHEX : %s\n", txthex);
}
if (strstr(txt, "TEST_START"))
uart_send(fd, "+START\r\n", 8); /* 回 AT */
DBG("TX:+START\r\n");
// fflush(stdout); /* ← 只加这一行 */
}
uart_close(fd);
return 0;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment