Skip to content

Commit 0c13bfa

Browse files
upcyantraeagent
andcommitted
feat: 推送本地开发到仓库
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
1 parent 6b194aa commit 0c13bfa

2 files changed

Lines changed: 161 additions & 91 deletions

File tree

aircat-server-lite.py

Lines changed: 84 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
time_sleep = 5 # 采集间隔(秒)
1212
SOCKET_PORT = 9000 # 监听端口
1313
BUFFER_SIZE = 4096 # 接收缓冲区(增大以处理更大数据包)
14-
RECV_TIMEOUT = 10 # 单次接收超时时间(秒),缩短以快速检测网络问题
15-
MAX_RETRY = 3 # 超时最大重试次数,超过则断开连接重连
14+
RECV_TIMEOUT = 15 # 单次接收超时时间(秒),给 IoT 设备足够的响应时间
15+
MAX_RETRY = 10 # 超时最大重试次数,IoT 设备低功耗/网络波动较频繁,放宽阈值
16+
SEND_TIMEOUT = 10 # 发送阶段超时时间(秒)
17+
CHUNK_TIMEOUT = 3 # 分片读取额外数据的超时(秒),缩短以避免阻塞主循环
1618

1719
# M1设备查询指令(保持原样)
1820
GET_MSG = b'\xaaO\x01%F\x119\x8f\x0b\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xf8\x93\x11dR\x007\x00\x00\x02{"type":5,"status":1}\xff#END#'
@@ -164,27 +166,51 @@ def start(self):
164166
def _handle_client(self, conn, addr):
165167
"""处理单个客户端连接"""
166168
_log(f"New connection from {addr}", 0)
167-
169+
168170
conn.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
169-
conn.settimeout(RECV_TIMEOUT)
170-
171+
# 启用 TCP keepalive 参数,更快检测死连接
172+
try:
173+
if hasattr(socket, 'TCP_KEEPIDLE'):
174+
conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) # 60s 空闲开始探测
175+
if hasattr(socket, 'TCP_KEEPINTVL'):
176+
conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) # 每 10s 探测一次
177+
if hasattr(socket, 'TCP_KEEPCNT'):
178+
conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) # 5 次失败判定断开
179+
except Exception:
180+
pass
181+
171182
consecutive_timeout = 0
172183
total_data_count = 0
173-
184+
174185
try:
175186
while self.running:
187+
# ---------- 阶段 1:发送查询指令 ----------
176188
try:
189+
conn.settimeout(SEND_TIMEOUT)
177190
_log(f"Client {addr} sending query...", 3)
178191
conn.sendall(GET_MSG)
179192
_log(f"Client {addr} query sent, waiting for response...", 3)
180-
193+
except (socket.timeout, OSError, ConnectionError) as e:
194+
_log(f"Client {addr} send query failed: {e}", 1)
195+
# 发送失败直接计入超时,不给太多重试机会(通常意味着连接已断)
196+
consecutive_timeout += 2
197+
if consecutive_timeout >= MAX_RETRY:
198+
_log(f"Client {addr} consecutive send failures, closing connection", 2)
199+
break
200+
time.sleep(2)
201+
continue
202+
203+
# ---------- 阶段 2:接收首包数据 ----------
204+
try:
205+
conn.settimeout(RECV_TIMEOUT)
181206
data = conn.recv(BUFFER_SIZE)
182-
207+
183208
if not data:
184209
_log(f"Client {addr} closed connection (empty recv)", 0)
185210
break
186-
187-
conn.settimeout(2)
211+
212+
# ---------- 阶段 3:接收剩余分片 ----------
213+
conn.settimeout(CHUNK_TIMEOUT)
188214
while True:
189215
try:
190216
chunk = conn.recv(BUFFER_SIZE)
@@ -193,41 +219,24 @@ def _handle_client(self, conn, addr):
193219
data += chunk
194220
except socket.timeout:
195221
break
196-
197-
conn.settimeout(RECV_TIMEOUT)
198-
199-
json_data = self._parse_data(data)
200-
if json_data:
201-
self._process_data(json_data, addr)
202-
total_data_count += 1
203-
consecutive_timeout = 0
204-
else:
205-
_log(f"Client {addr} received data but no valid JSON (len={len(data)})", 1)
206222

207-
# 处理完数据后,检查亮度控制
208-
brightness = get_current_brightness()
209-
if brightness >= 0 and data and len(data) >= 23:
210-
try:
211-
brightness_json = json.dumps({"brightness": brightness})
212-
brightness_msg = data[:23] + b'\x00\x18\x00\x00\x02' + brightness_json.encode('utf-8') + b'\xff#END#'
213-
conn.sendall(brightness_msg)
214-
_log(f"Sent brightness control: {brightness} to {addr}", 3)
215-
except Exception as e:
216-
_log(f"Brightness control error: {e}", 1)
217-
218-
time.sleep(time_sleep)
219-
220223
except socket.timeout:
221224
consecutive_timeout += 1
222-
_log(f"Client {addr} recv timeout ({consecutive_timeout}/{MAX_RETRY}), data received: {total_data_count}", 1)
223-
225+
_log(
226+
f"Client {addr} recv timeout ({consecutive_timeout}/{MAX_RETRY}), "
227+
f"data packets received: {total_data_count}",
228+
1 if consecutive_timeout < MAX_RETRY // 2 else 2
229+
)
224230
if consecutive_timeout >= MAX_RETRY:
225-
_log(f"Client {addr} max recv timeout reached ({MAX_RETRY}), closing connection", 2)
231+
_log(
232+
f"Client {addr} max recv timeout reached ({MAX_RETRY}), "
233+
f"closing connection (total packets: {total_data_count})",
234+
2
235+
)
226236
break
227-
228237
time.sleep(1)
229238
continue
230-
239+
231240
except ConnectionResetError:
232241
_log(f"Client {addr} reset connection (ConnectionResetError)", 1)
233242
break
@@ -238,24 +247,50 @@ def _handle_client(self, conn, addr):
238247
_log(f"Client {addr} connection aborted", 1)
239248
break
240249
except OSError as e:
241-
_log(f"Client {addr} OS error: {e}", 2)
250+
_log(f"Client {addr} OS error on recv: {e}", 2)
242251
break
243-
except Exception as e:
244-
_log(f"Client {addr} unexpected error: {e}", 2)
245-
consecutive_timeout += 1
246-
if consecutive_timeout >= MAX_RETRY:
247-
break
248-
time.sleep(1)
249-
continue
250-
252+
253+
# ---------- 阶段 4:解析与处理数据 ----------
254+
# 数据接收成功,重置超时计数器
255+
consecutive_timeout = 0
256+
257+
json_data = self._parse_data(data)
258+
if json_data:
259+
self._process_data(json_data, addr)
260+
total_data_count += 1
261+
262+
# 处理完数据后,检查亮度控制
263+
brightness = get_current_brightness()
264+
if brightness >= 0 and data and len(data) >= 23:
265+
try:
266+
conn.settimeout(SEND_TIMEOUT)
267+
brightness_json = json.dumps({"brightness": brightness})
268+
brightness_msg = data[:23] + b'\x00\x18\x00\x00\x02' + brightness_json.encode('utf-8') + b'\xff#END#'
269+
conn.sendall(brightness_msg)
270+
_log(f"Sent brightness control: {brightness} to {addr}", 3)
271+
except Exception as e:
272+
_log(f"Brightness control error: {e}", 1)
273+
else:
274+
_log(f"Client {addr} received data but no valid JSON (len={len(data)})", 1)
275+
276+
# 等待下一次采集
277+
try:
278+
conn.settimeout(None) # sleep 期间不需要超时
279+
except Exception:
280+
pass
281+
time.sleep(time_sleep)
282+
251283
except Exception as e:
252284
_log(f"Client {addr} fatal error: {e}", 2)
253285
finally:
254286
try:
255287
conn.shutdown(socket.SHUT_RDWR)
256-
except:
288+
except Exception:
289+
pass
290+
try:
291+
conn.close()
292+
except Exception:
257293
pass
258-
conn.close()
259294
_log(f"Connection closed: {addr}, total data packets: {total_data_count}", 0)
260295

261296
def _parse_data(self, data):

aircat-server-web.py

Lines changed: 77 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
time_sleep = 5 # 采集间隔(秒)
1616
SOCKET_PORT = 9000 # 监听端口
1717
BUFFER_SIZE = 4096 # 接收缓冲区(增大以处理更大数据包)
18-
RECV_TIMEOUT = 10 # 单次接收超时时间(秒),缩短以快速检测网络问题
19-
MAX_RETRY = 3 # 超时最大重试次数,超过则断开连接重连
18+
RECV_TIMEOUT = 15 # 单次接收超时时间(秒),给 IoT 设备足够的响应时间
19+
MAX_RETRY = 10 # 超时最大重试次数,IoT 设备低功耗/网络波动较频繁,放宽阈值
20+
SEND_TIMEOUT = 10 # 发送阶段超时时间(秒)
21+
CHUNK_TIMEOUT = 3 # 分片读取额外数据的超时(秒),缩短以避免阻塞主循环
2022

2123
# M1设备查询指令(保持原样)
2224
GET_MSG = b'\xaaO\x01%F\x119\x8f\x0b\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xf8\x93\x11dR\x007\x00\x00\x02{"type":5,"status":1}\xff#END#'
@@ -714,25 +716,49 @@ def _handle_client(self, conn, addr):
714716
_log(f"New connection from {addr}", 0)
715717

716718
conn.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
717-
conn.settimeout(RECV_TIMEOUT)
719+
# 启用 TCP keepalive 参数,更快检测死连接
720+
try:
721+
if hasattr(socket, 'TCP_KEEPIDLE'):
722+
conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) # 60s 空闲开始探测
723+
if hasattr(socket, 'TCP_KEEPINTVL'):
724+
conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) # 每 10s 探测一次
725+
if hasattr(socket, 'TCP_KEEPCNT'):
726+
conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) # 5 次失败判定断开
727+
except Exception:
728+
pass
718729

719730
consecutive_timeout = 0
720731
total_data_count = 0
721732

722733
try:
723734
while self.running:
735+
# ---------- 阶段 1:发送查询指令 ----------
724736
try:
737+
conn.settimeout(SEND_TIMEOUT)
725738
_log(f"Client {addr} sending query...", 3)
726739
conn.sendall(GET_MSG)
727740
_log(f"Client {addr} query sent, waiting for response...", 3)
741+
except (socket.timeout, OSError, ConnectionError) as e:
742+
_log(f"Client {addr} send query failed: {e}", 1)
743+
# 发送失败直接计入超时,不给太多重试机会(通常意味着连接已断)
744+
consecutive_timeout += 2
745+
if consecutive_timeout >= MAX_RETRY:
746+
_log(f"Client {addr} consecutive send failures, closing connection", 2)
747+
break
748+
time.sleep(2)
749+
continue
728750

751+
# ---------- 阶段 2:接收首包数据 ----------
752+
try:
753+
conn.settimeout(RECV_TIMEOUT)
729754
data = conn.recv(BUFFER_SIZE)
730755

731756
if not data:
732757
_log(f"Client {addr} closed connection (empty recv)", 0)
733758
break
734759

735-
conn.settimeout(2)
760+
# ---------- 阶段 3:接收剩余分片 ----------
761+
conn.settimeout(CHUNK_TIMEOUT)
736762
while True:
737763
try:
738764
chunk = conn.recv(BUFFER_SIZE)
@@ -742,38 +768,20 @@ def _handle_client(self, conn, addr):
742768
except socket.timeout:
743769
break
744770

745-
conn.settimeout(RECV_TIMEOUT)
746-
747-
json_data = self._parse_data(data)
748-
if json_data:
749-
self._process_data(json_data, addr)
750-
total_data_count += 1
751-
consecutive_timeout = 0
752-
753-
# 亮度控制
754-
if db_manager and data and len(data) >= 23:
755-
brightness = get_current_brightness(db_manager)
756-
if brightness >= 0:
757-
try:
758-
brightness_json = json.dumps({"brightness": brightness})
759-
brightness_msg = data[:23] + b'\x00\x18\x00\x00\x02' + brightness_json.encode('utf-8') + b'\xff#END#'
760-
conn.sendall(brightness_msg)
761-
_log(f"Sent brightness control: {brightness} to {addr}", 3)
762-
except Exception as e:
763-
_log(f"Brightness control error: {e}", 1)
764-
else:
765-
_log(f"Client {addr} received data but no valid JSON (len={len(data)})", 1)
766-
767-
time.sleep(time_sleep)
768-
769771
except socket.timeout:
770772
consecutive_timeout += 1
771-
_log(f"Client {addr} recv timeout ({consecutive_timeout}/{MAX_RETRY}), data received: {total_data_count}", 1)
772-
773+
_log(
774+
f"Client {addr} recv timeout ({consecutive_timeout}/{MAX_RETRY}), "
775+
f"data packets received: {total_data_count}",
776+
1 if consecutive_timeout < MAX_RETRY // 2 else 2
777+
)
773778
if consecutive_timeout >= MAX_RETRY:
774-
_log(f"Client {addr} max recv timeout reached ({MAX_RETRY}), closing connection", 2)
779+
_log(
780+
f"Client {addr} max recv timeout reached ({MAX_RETRY}), "
781+
f"closing connection (total packets: {total_data_count})",
782+
2
783+
)
775784
break
776-
777785
time.sleep(1)
778786
continue
779787

@@ -787,24 +795,51 @@ def _handle_client(self, conn, addr):
787795
_log(f"Client {addr} connection aborted", 1)
788796
break
789797
except OSError as e:
790-
_log(f"Client {addr} OS error: {e}", 2)
798+
_log(f"Client {addr} OS error on recv: {e}", 2)
791799
break
792-
except Exception as e:
793-
_log(f"Client {addr} unexpected error: {e}", 2)
794-
consecutive_timeout += 1
795-
if consecutive_timeout >= MAX_RETRY:
796-
break
797-
time.sleep(1)
798-
continue
800+
801+
# ---------- 阶段 4:解析与处理数据 ----------
802+
# 数据接收成功,重置超时计数器
803+
consecutive_timeout = 0
804+
805+
json_data = self._parse_data(data)
806+
if json_data:
807+
self._process_data(json_data, addr)
808+
total_data_count += 1
809+
810+
# 亮度控制
811+
if db_manager and data and len(data) >= 23:
812+
brightness = get_current_brightness(db_manager)
813+
if brightness >= 0:
814+
try:
815+
conn.settimeout(SEND_TIMEOUT)
816+
brightness_json = json.dumps({"brightness": brightness})
817+
brightness_msg = data[:23] + b'\x00\x18\x00\x00\x02' + brightness_json.encode('utf-8') + b'\xff#END#'
818+
conn.sendall(brightness_msg)
819+
_log(f"Sent brightness control: {brightness} to {addr}", 3)
820+
except Exception as e:
821+
_log(f"Brightness control error: {e}", 1)
822+
else:
823+
_log(f"Client {addr} received data but no valid JSON (len={len(data)})", 1)
824+
825+
# 等待下一次采集
826+
try:
827+
conn.settimeout(None) # sleep 期间不需要超时
828+
except Exception:
829+
pass
830+
time.sleep(time_sleep)
799831

800832
except Exception as e:
801833
_log(f"Client {addr} fatal error: {e}", 2)
802834
finally:
803835
try:
804836
conn.shutdown(socket.SHUT_RDWR)
805-
except:
837+
except Exception:
838+
pass
839+
try:
840+
conn.close()
841+
except Exception:
806842
pass
807-
conn.close()
808843
_log(f"Connection closed: {addr}, total data packets: {total_data_count}", 0)
809844

810845
def _parse_data(self, data):

0 commit comments

Comments
 (0)