外观
嵌入式软件开发
项目简介🪄
项目介绍
本项目基于搭载双核RISC处理器的【立创开发板 ESP32S3R8N8】构建,是一个支持500万像素的卡片照相机,支持局域网相册管理,采用模块化CMOS传感器,可自由选择OV2640/OV5640模块。贴片阻容采用0805封装,适合初学者焊接。
开发环境
- 软件环境:VSCode+PlatformIO
- 开发语言:C/C++
依赖库
通过以下开源库协助本项目开发 - TFT_eSPI:用于屏幕显示驱动
- ArduinoJson:解析Json数据
- ESPAsyncWebServer-esphome:构建异步WEB服务
main 主进程
本项目的嵌入式软件功能比较简单,只需要接收/api路径下数据并解析显示至ST7789屏幕,所以这里直接用裸机开发实现,而不使用OS,UI部分则是由TFT-eSPI实现,逻辑默认仅当数据变化下才更新,避免频繁刷屏导致频闪。 Markdown 语法解析会导致部分内容消失,详细源码建议查看附件
#include
#include
#include
#include
#include
#include
#include
#include "image.cpp"
AsyncWebServer server(80);// 设置服务器端口
TFT_eSPI tft = TFT_eSPI();
const char *ssidFile = "/ssid.json"; // 配置存储文件
String ipAddress = "Not Connected";
String lastMode = "";
String lastprimitiveType = "";
String lastIP = "";
String lastname1 = "";
String currentname1 = "--";
String lastdata1 = "";
String currentdata1 = "--";
String lastname2 = "";
String currentname2 = "--";
String lastdata2 = "";
String currentdata2 = "--";
String lastname3 = "";
String currentname3 = "--";
String lastdata3 = "";
String currentdata3 = "--";
String lastname4 = "";
String currentname4 = "--";
String lastdata4 = "";
String currentdata4 = "--";
// AP 默认热点配置
const char *ap_ssid = "EDA-InfoHelper";
const char *ap_password = "";
String currentMode = "--";
String currentprimitiveType = "--";
bool wifiBlink = true;
int temp = 26;
int humi = 45;
// ================== UI 提示函数 ==================
void showMessage(String msg, uint16_t color = TFT_WHITE) {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(color, TFT_BLACK);
tft.drawCentreString(msg, 160, 120, 4);
}
// ================== 原始 UI 元素 ==================
void drawHeader() {
tft.fillRect(0, 0, 320, 20, TFT_NAVY);
tft.setTextColor(TFT_WHITE, TFT_NAVY);
tft.drawString("EDA-InfoHelper", 5, 5, 2);
if (WiFi.status() == WL_CONNECTED) {
tft.setTextColor(TFT_GREEN, TFT_NAVY);
tft.drawRightString(ipAddress, 230, 5, 2);
} else {
tft.setTextColor(TFT_GREEN, TFT_NAVY);
tft.drawRightString(ipAddress, 230, 5, 2);
if (wifiBlink) {
tft.fillRect(290, 5, 5, 14, TFT_CYAN);
tft.fillRect(300, 9, 5, 10, TFT_CYAN);
tft.fillRect(310, 13, 5, 6, TFT_CYAN);
} else {
tft.fillRect(280, 0, 40, 20, TFT_NAVY);
}
}
}
void drawCard(int x, int y,uint16_t color,String mode) {
tft.fillRoundRect(x, y, 80, 80, 8, color);
if(mode == "PCB")
{
/* code */
tft.pushImage(x+3, y+3, 75, 75, pcb);
}
else if(mode == "SCH")
{
/* code */
tft.pushImage(x+3, y+3, 75, 75, sch);
}else{
tft.pushImage(x+3, y+3, 75, 75, eda);
}
}
void drawTextCard(int x, int y, String title, String value, uint16_t color) {
tft.fillRoundRect(x, y, 210, 35, 8, color);
tft.drawRoundRect(x, y, 210, 35, 8, TFT_WHITE);
tft.setTextColor(TFT_WHITE, color);
tft.drawString(title, x + 8, y + 6, 2);
tft.setTextColor(TFT_YELLOW, color);
tft.drawRightString(value, x + 200, y + 5, 4);
}
void drawTextCard2(int x, int y, String title, String value, uint16_t color) {
tft.fillRoundRect(x, y, 145, 50, 8, color);
tft.drawRoundRect(x, y, 145, 50, 8, TFT_WHITE);
tft.setTextColor(TFT_WHITE, color);
tft.drawString(title, x + 8, y + 6, 2);
tft.setTextColor(TFT_YELLOW, color);
tft.drawRightString(value, x + 130, y + 25, 2);
}
// ================== 保存 WiFi 配置 ==================
void saveWiFiConfig(String ssid, String pass) {
DynamicJsonDocument doc(256);
doc["ssid"] = ssid;
doc["pass"] = pass;
fs::File file = SPIFFS.open(ssidFile, "w");
if (file) {
serializeJson(doc, file);
file.close();
}
}
// ================== 加载 WiFi 配置 ==================
bool loadWiFiConfig(String &ssid, String &pass) {
if (!SPIFFS.exists(ssidFile)) return false;
fs::File file = SPIFFS.open(ssidFile, "r");
if (!file) return false;
DynamicJsonDocument doc(256);
if (deserializeJson(doc, file)) return false;
ssid = doc["ssid"].as();
pass = doc["pass"].as();
return true;
}
// ================== WiFi STA 连接函数 ==================
bool connectSTA(String ssid, String pass, int timeout = 10000) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), pass.c_str());
showMessage("Wait connect WIFI...", TFT_YELLOW);
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED && millis() - start < timeout) {
delay(3000);
}
if (WiFi.status() == WL_CONNECTED) {
ipAddress = WiFi.localIP().toString();
return true;
} else {
return false;
}
}
// ================== AP 模式启动 ==================
void startAP() {
WiFi.mode(WIFI_AP);
WiFi.softAP(ap_ssid, ap_password);
showMessage("Wait connect WIFI...", TFT_CYAN);
}
// ================== 路由配置 ==================
void handleWiFiConfig() {
server.on("/api", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("mode", true) && request->hasParam("primitiveType", true)) {
currentMode = request->getParam("mode", true)->value();
currentprimitiveType = request->getParam("primitiveType", true)->value();
currentname1 = request->getParam("name1", true)->value();
currentdata1 = request->getParam("data1", true)->value();
currentname2= request->getParam("name2", true)->value();
currentdata2 = request->getParam("data2", true)->value();
currentname3 = request->getParam("name3", true)->value();
currentdata3 = request->getParam("data3", true)->value();
currentname4 = request->getParam("name4", true)->value();
currentdata4 = request->getParam("data4", true)->value();
request->send(200, "application/json", "{\"status\":\"ok\"}");
} else {
request->send(400, "application/json", "{\"error\":\"missing parameters\"}");
}
});
server.on("/connect", HTTP_POST, [](AsyncWebServerRequest *request) {
// 获取POST参数:ssid、pass、city、api
String ssid = request->getParam("ssid", true)->value();
String pass = request->getParam("pass", true)->value();
// 打印接收到的参数
Serial.println(ssid);
Serial.println(pass);
// 保存WiFi信息到JSON文件
DynamicJsonDocument doc(1024);
doc["ssid"] = ssid;
doc["pass"] = pass;
fs::File file = SPIFFS.open(ssidFile, "w"); // 打开文件进行写入
if (file) {
serializeJson(doc, file); // 将JSON内容写入文件
file.close(); // 关闭文件
Serial.println("WiFi信息保存成功,即将重启...");
} else {
Serial.println("无法打开文件保存WiFi信息!");
request->send(500, "application/json", "{\"error\":\"无法保存WiFi信息\"}");
return;
}
// 发送响应告知用户即将重启
request->send(200, "text/html; charset=UTF-8",
""
""
""
" "
" 状态"
""
""
" <h1>WiFi配置已保存,设备正在重启...</h1>"
""
""
);
delay(500); // 确保响应发送出去
ESP.restart(); // 重启设备
});
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
// 检查SPIFFS文件系统中是否存在index.html文件
if (SPIFFS.exists("/index.html")) {
fs::File file = SPIFFS.open("/index.html", "r"); // 打开index.html文件
if (file) {
size_t fileSize = file.size(); // 获取文件大小
String fileContent;
// 逐字节读取文件内容
while (file.available()) {
fileContent += (char)file.read();
}
file.close(); // 关闭文件
// 返回HTML内容
request->send(200, "text/html", fileContent);
return;
}
}
// 如果文件不存在,返回404错误
request->send(404, "text/plain", "File Not Found"); });
server.on("/setting.html", HTTP_GET, [](AsyncWebServerRequest *request)
{
// 检查SPIFFS文件系统中是否存在index.html文件
if (SPIFFS.exists("/setting.html")) {
fs::File file = SPIFFS.open("/setting.html", "r"); // 打开index.html文件
if (file) {
size_t fileSize = file.size(); // 获取文件大小
String fileContent;
// 逐字节读取文件内容
while (file.available()) {
fileContent += (char)file.read();
}
file.close(); // 关闭文件
// 返回HTML内容
request->send(200, "text/html", fileContent);
return;
}
}
// 如果文件不存在,返回404错误
request->send(404, "text/plain", "File Not Found"); });
// 启动服务器
server.begin();
}
// ================== 主流程 ==================
void setup() {
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
Serial.begin(115200);
SPIFFS.begin();
String ssid, pass;
if (loadWiFiConfig(ssid, pass)) {
if (!connectSTA(ssid, pass)) {
startAP();
}
} else {
startAP();
}
handleWiFiConfig();
if (WiFi.status() == WL_CONNECTED) {
tft.fillScreen(TFT_BLACK);
drawHeader();
drawCard(10, 30, TFT_WHITE,"EDA");
drawTextCard(100, 30, "mode", "JLC_EDA", TFT_GREEN);
drawTextCard(100, 75, "primitiveType", "ABC", TFT_SKYBLUE);
drawTextCard2(10, 120, "name1", "data1", TFT_ORANGE);
drawTextCard2(165, 120, "name2", "data2", TFT_ORANGE);
drawTextCard2(10, 180, "name3", "data3", TFT_MAGENTA);
drawTextCard2(165, 180, "name4", "data4", TFT_MAGENTA);
}
}
void loop() {
// WiFi 状态刷新(顶部 header)
static unsigned long lastBlink = 0;
if (millis() - lastBlink > 500) {
wifiBlink = !wifiBlink;
String currentIP = WiFi.localIP().toString();
// 仅当IP发生变化或wifiBlink状态改变时才刷新header
if (currentIP != lastIP) {
ipAddress = currentIP;
drawHeader();
lastIP = currentIP;
} else if (wifiBlink) {
drawHeader(); // 保持闪烁动画
}
lastBlink = millis();
}
// 实时更新 mode/data(仅当发生变化时)
if (WiFi.status() == WL_CONNECTED) {
if (currentMode != lastMode) {
drawTextCard(100, 30, "mode", currentMode, TFT_GREEN);
drawCard(10, 30, TFT_WHITE,currentMode);
lastMode = currentMode;
}
if (currentprimitiveType != lastprimitiveType) {
drawTextCard(100, 75, "primitiveType", currentprimitiveType, TFT_SKYBLUE);
lastprimitiveType = currentprimitiveType;
}
if (currentdata1 != lastdata1) {
if (currentdata1.length() > 16) {
currentdata1 = currentdata1.substring(0, 16);
}
drawTextCard2(10, 120, currentname1, currentdata1, TFT_ORANGE);
lastname1 = currentname1;
lastdata1 = currentdata1;
}
if (currentdata2 != lastdata2) {
if (currentdata2.length() > 16) {
currentdata2 = currentdata2.substring(0, 16);
}
drawTextCard2(165, 120,currentname2,currentdata2, TFT_ORANGE);
lastname2 = currentname2;
lastdata2 = currentdata2;
}
if (currentdata3 != lastdata3) {
if (currentdata3.length() > 16) {
currentdata3 = currentdata3.substring(0, 16);
}
drawTextCard2(10, 180, currentname3, currentdata3, TFT_MAGENTA);
lastname3 = currentname3;
lastdata3 = currentdata3;
}
if (currentdata4 != lastdata4) {
if (currentdata4.length() > 16) {
currentdata4 = currentdata4.substring(0, 16);
}
drawTextCard2(165, 180, currentname4, currentdata4, TFT_MAGENTA);
lastname4 = currentname4;
lastdata4 = currentdata4;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
上位机客户端软件开发
开发环境
- 软件环境:VSCode
- 开发语言:Python
依赖库
通过以下开源库协助本项目开发 - Flask:用来快速搭建 HTTP 接口或 Web 应用
- requests:用来发送 GET/POST 等请求
- flask_cors (CORS):用来处理跨域请求问题
- Pillow (PIL):托盘图标绘制
- pystray:用于在系统托盘创建图标和菜单
Python主进程
因为只是转发服务,所以不需要什么UI,就选择Python快速开发,应用会在后台运行,可在系统托盘中关闭中转转发服务,通过5012端口/proxy/api路径的数据监听,把数据转发给传入的IP地址/api路径下。 import threading
from flask import Flask, request, jsonify
import requests
from flask_cors import CORS
from PIL import Image, ImageDraw
import pystray
import sys
import tkinter as tk
from tkinter import messagebox
app = Flask(__name__)
CORS(app)
# --- Flask 路由 ---
@app.route("/proxy/api", methods=["POST"])
def proxy_api():
try:
ip = request.form.get('ip', '127.0.0.1')
mode = request.form.get('mode')
primitiveType = request.form.get('primitiveType')
name1 = request.form.get('name1')
data1 = request.form.get('data1')
name2 = request.form.get('name2')
data2 = request.form.get('data2')
name3 = request.form.get('name3')
data3 = request.form.get('data3')
name4 = request.form.get('name4')
data4 = request.form.get('data4')
if not all([ip, mode, primitiveType,name1,data1,name2,data2,name3,data3,name4,data4]):
return {"success": False, "error": "缺少必要参数"}, 400
target_url = f"http://{ip}/api"
resp = requests.post(target_url,
data={"mode": mode, "primitiveType": primitiveType,
"name1":name1,"data1":data1,
"name2":name2,"data2":data2,
"name3":name3,"data3":data3,
"name4":name4,"data4":data4},
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=5)
resp.raise_for_status()
return {"success": True, "esp_response": resp.text}
except Exception as e:
return {"success": False, "error": str(e)}, 500
# --- 托盘相关 ---
def create_image():
img = Image.new('RGB', (64, 64), color='white')
d = ImageDraw.Draw(img)
d.rectangle([16, 16, 48, 48], fill='black')
return img
def on_quit(icon, item):
icon.stop()
sys.exit(0)
def run_tray():
icon = pystray.Icon("EDA-InfoHelper")
icon.icon = create_image()
icon.title = "EDA-InfoHelper"
icon.menu = pystray.Menu(pystray.MenuItem("EDA-InfoHelper正在运行",None),pystray.MenuItem("退出", on_quit))
icon.run()
# --- 弹窗提示 ---
def show_startup_message():
root = tk.Tk()
root.withdraw() # 隐藏主窗口
messagebox.showinfo("提示", "EDA-InfoHelper 已启动并隐藏到托盘")
root.destroy()
# --- 启动 Flask 线程 ---
def run_flask():
print("EDA-InfoHelper 转发服务已启动,端口号5012")
app.run(host="0.0.0.0", port=5012, debug=False)
if __name__ == "__main__":
# 弹窗提示
show_startup_message()
# 启动 Flask 后台线程
threading.Thread(target=run_flask, daemon=True).start()
# 显示托盘图标
run_tray()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
嘉立创EDA插件开发
开发环境
- 软件环境:VSCode
- 开发语言:Html/CSS/TypeScript
index.ts
index.ts是功能入口,创建的状态栏列表下按键通过此文件入口进入,这里我们通过定时器实现实时检测,同时将定时器id记录并存储,方便结束定时器。
import * as extensionConfig from '../extension.json';
import * as func from './function';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function activate(status?: 'onStartupFinished', arg?: string): void {}
export function about(): void {
eda.sys_Dialog.showInformationMessage(
eda.sys_I18n.text('EasyEDA extension SDK v', undefined, undefined, extensionConfig.version),
eda.sys_I18n.text('About'),
);
}
let timerId: ReturnType | null = null;
// 封装定时器函数
// 启动定时器
export async function start(): Promise {
const configs = await eda.sys_Storage.getExtensionAllUserConfigs();
if (configs.timerId) {
console.log('定时器已存在:', configs.timerId);
return;
}
const id = func.createDocumentTimer();
// 保存到扩展配置
await eda.sys_Storage.setExtensionAllUserConfigs({
...configs,
timerId: id,
});
console.log('start timerId =', id);
}
// 停止定时器
export async function stopa(): Promise {
console.log('stop');
const configs = eda.sys_Storage.getExtensionAllUserConfigs();
const id = configs.timerId;
console.log('stop timerId =', id);
if (id !== undefined) {
clearInterval(id);
delete configs.timerId;
await eda.sys_Storage.setExtensionAllUserConfigs(configs);
console.log('循环已停止');
}
}
export async function setting(): Promise {
eda.sys_IFrame.openIFrame('/iframe/index.html', 450, 500);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
function.ts
function.ts是存放的是功能实现,用于逻辑处理及在index.ts入门中调用实现的功能。 export function createDocumentTimer(): number {
return setInterval(() => {
eda.dmt_SelectControl
.getCurrentDocumentInfo()
.then(async (doc) => {
if (doc) {
console.log('用户当前在:' + doc.documentType);
if (doc.documentType === 3) {
// PCB
const items = await eda.pcb_SelectControl.getSelectedPrimitives();
if (items && items.length > 0) {
items.forEach((i) => {
const dataValue = i.pcbItemPrimitiveType ?? 0; // 如果 undefined 就用 0
switch (dataValue) {
case "Component":
const name = i.device.name?? 0;
const part = i.device.attrs["Supplier Part"]?? 0;
const footprint = i.attrsMap["Footprint"] ?? 0;
const globalIndex = i.globalIndex ?? 0;
sendData("PCB", dataValue,"name",name,"part",part,"footprint",footprint,"globalIndex",globalIndex);
break;
case "Pad":
const net = i.net ?? 0;
const num = i.num ?? 0;
const layerId = i.layerId ?? 0;
const globalIndex2 = i.globalIndex ?? 0;
sendData("PCB", dataValue,"net",net,"num",num,"layerId",layerId,"globalIndex",globalIndex2);
break;
case "Text":
const formulaName = i.formulaName ?? 0;
const fontSize = i.fontSize ?? 0;
const key = i.key ?? 0;
const globalIndex3 = i.globalIndex ?? 0;
sendData("PCB", dataValue,"formulaName", formulaName,"fontSize",fontSize,"key",key,"globalIndex",globalIndex3);
break;
case "Rect":
const height = i.height ?? 0;
const width = i.width ?? 0;
const type = i.type ?? 0;
const globalIndex4 = i.globalIndex ?? 0;
sendData("PCB", dataValue,"height",height,"width",width,"type",type,"globalIndex",globalIndex4);
break;
case "Via":
const net2 = i.net ?? 0;
const holeRadius = i.holeRadius ?? 0;
const radius = i.radius ?? 0;
const globalIndex5 = i.globalIndex ?? 0;
sendData("PCB", dataValue,"net",net2,"holeRadius",holeRadius,"radius",radius,"globalIndex",globalIndex5);
break;
case "Polygon":
const strokeWidth = i.strokeWidth ?? 0;
const type2 = i.type ?? 0;
const layerId2 = i.layerId ?? 0;
const globalIndex6 = i.globalIndex ?? 0;
sendData("PCB", dataValue,"strokeWidth",strokeWidth,"type",type2,"layerId",layerId2,"globalIndex",globalIndex6);
break;
default:
break;
}
});
}
} else if (doc.documentType === 1) {
// SCH
const items = await eda.sch_SelectControl.getSelectedPrimitives();
if (items && items.length > 0) {
items.forEach((i) => {
const dataValue = i.primitiveType ?? 0; // 防止 undefined
switch (dataValue) {
case "Component":
const designator = i.param.designator?? 0;
const uniqueId = i.param.uniqueIdt?? 0;
const id = i.id ?? 0;
sendData("SCH", dataValue,"designator",designator,"uniqueId",uniqueId,"id",id,"---","---");
break;
case "Net Port":
case "Wire":
case "Net Flag":
const net = i.param.net ?? 0;
const id2 = i.id ?? 0;
sendData("SCH", dataValue,"net",net,"id",id2,"---","---","---","---");
break;
case "Property":
case "Text":
const id3 = i.id ?? 0;
sendData("SCH", dataValue,"id",id3,"---","---","---","---","---","---");
break;
case "Name":
const id4 = i.id ?? 0;
sendData("SCH", dataValue,"id",id4,"---","---","---","---","---","---");
break;
default:
break;
}
});
}
}
} else {
console.log('当前没有选中文档');
}
})
.catch((err) => {
console.error('获取文档信息失败:', err);
});
}, 1000);
}
function sendData(mode: String, primitiveType: String,name1:String,data1:String,name2:String,data2:String,name3:String,data3:String,name4:String,data4:String) {
const params = new URLSearchParams();
// 获取已存储的IP地址
const configs = eda.sys_Storage.getExtensionAllUserConfigs();
const ip = configs.ip || '127.0.0.1';
params.append('ip', ip); // 新增IP参数
params.append('mode', mode.toString());
params.append('primitiveType', primitiveType.toString());
params.append('name1', name1.toString());
params.append('data1', data1.toString());
params.append('name2', name2.toString());
params.append('data2', data2.toString());
params.append('name3', name3.toString());
params.append('data3', data3.toString());
params.append('name4', name4.toString());
params.append('data4', data4.toString());
eda.sys_ClientUrl
.request(
'http://127.0.0.1:5012/proxy/api',
'POST',
params.toString(),
{
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
},
(res) => {
console.log('数据发送成功', res);
},
)
.catch((err) => {
console.error('请求失败:', err);
});
}
declare global {
interface ExtensionConfigs {
timerId?: number;
ip?: string; // 新增IP字段
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
index.html
index.html是设置页面的前端展示,用于用户下载中转服务及输入目标硬件设备的IP,IP会存储至插件内部存储,便于API请求,避免重复配置。
<div>
<div>
<h3>EDA-InfoHelperServer中转服务工具</h3>
<p>在使用本插件的同时,您还需下载并启动 EDA-InfoHelperServer中转服务工具,通过本工具把POST数据传输至硬件设备。</p>
下载中转服务工具
</div>
<div>
<h3>绑定EDA-InfoHelper的IP地址</h3>
<p>中转服务工具启动后,还需在此填入您的EDA-InfoHelper设备IP地址,您的硬件联网后硬件设备IP地址会显示在硬件设备的顶部导航栏上。</p>
<input type="text" id="ipAddress" />
保存设置
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Markdown 语法解析会导致部分内容消失,详细内容建议看附件文件