From 987e62692aafee4860a54efc7e2f7e8f7f721ded Mon Sep 17 00:00:00 2001
From: wu_xinjun <bent20140204@outlook.com>
Date: 星期二, 15 二月 2022 16:46:24 +0800
Subject: [PATCH] add microwave&env

---
 .gitignore                                   |   19 
 src/engine/MessageQueueThread.py             |  108 +++
 src/Microwave/demo/MicroWave2Ply.py          |   35 +
 src/protos/compile.bat                       |   11 
 src/Microwave/parsers.py                     |   23 
 src/engine/ControllThread.py                 |   86 ++
 src/engine/DeviceManager.py                  |   40 +
 src/Microwave/servers.py                     |  146 ++++
 src/Client.py                                |   32 +
 src/Microwave/demo/udp_test_get.py           |   13 
 src/Server.py                                |   95 +++
 src/config.json                              |   82 ++
 src/Microwave/demo/udp_test_send.py          |   20 
 startClient.bat                              |   16 
 env/ExportEnv.bat                            |   10 
 src/requirements.yaml                        |  149 ++++
 src/Microwave/demo/MicroWaveUDPServerTest.py |   33 +
 src/engine/DustCleaner.py                    |  108 +++
 src/engine/DustCleanerThread.py              |   67 ++
 src/engine/MicroWaveUDPThread.py             |   22 
 env/SettingEnv.bat                           |   19 
 src/Microwave/demo/MicroWaveReceiverTest.py  |   30 
 env/JustDoubleClickMe.bat                    |    4 
 src/engine/AcquisitionMain.py                |  257 ++++++++
 src/Microwave/constants.py                   |    2 
 README.md                                    |   21 
 /dev/null                                    |   15 
 src/protos/aerial.proto                      |   31 +
 startServer.bat                              |   16 
 src/Microwave/__init__.py                    |    4 
 src/engine/CaptureThread.py                  |   70 ++
 src/Microwave/demo/polty.ipynb               |   64 ++
 src/engine/__init__.py                       |    0 
 src/Microwave/components.py                  |  172 +++++
 34 files changed, 1,778 insertions(+), 42 deletions(-)

diff --git a/.gitignore b/.gitignore
index 0268744..5c8a381 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,14 +104,13 @@
 # SageMath parsed files
 *.sage.py
 
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
+# # Environments
+# .env
+# .venv
+# venv/
+# ENV/
+# env.bak/
+# venv.bak/
 
 # Spyder project settings
 .spyderproject
@@ -140,3 +139,7 @@
 
 .vs
 src/protos/aerial_pb2.py
+src/microwave/demo/*Data/
+
+env/aerial_deploy
+env/*.gz
diff --git a/MicroWaveData/packet0.udp b/MicroWaveData/packet0.udp
deleted file mode 100644
index 45ec6af..0000000
--- a/MicroWaveData/packet0.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet1.udp b/MicroWaveData/packet1.udp
deleted file mode 100644
index bdcd817..0000000
--- a/MicroWaveData/packet1.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet10.udp b/MicroWaveData/packet10.udp
deleted file mode 100644
index cbdae1b..0000000
--- a/MicroWaveData/packet10.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet11.udp b/MicroWaveData/packet11.udp
deleted file mode 100644
index 47e9442..0000000
--- a/MicroWaveData/packet11.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet12.udp b/MicroWaveData/packet12.udp
deleted file mode 100644
index 15ae507..0000000
--- a/MicroWaveData/packet12.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet13.udp b/MicroWaveData/packet13.udp
deleted file mode 100644
index c6b1347..0000000
--- a/MicroWaveData/packet13.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet14.udp b/MicroWaveData/packet14.udp
deleted file mode 100644
index 276ad97..0000000
--- a/MicroWaveData/packet14.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet15.udp b/MicroWaveData/packet15.udp
deleted file mode 100644
index f1edce3..0000000
--- a/MicroWaveData/packet15.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet16.udp b/MicroWaveData/packet16.udp
deleted file mode 100644
index 3ecac02..0000000
--- a/MicroWaveData/packet16.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet17.udp b/MicroWaveData/packet17.udp
deleted file mode 100644
index 6c01a95..0000000
--- a/MicroWaveData/packet17.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet18.udp b/MicroWaveData/packet18.udp
deleted file mode 100644
index 6f5d34b..0000000
--- a/MicroWaveData/packet18.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet19.udp b/MicroWaveData/packet19.udp
deleted file mode 100644
index 979e72a..0000000
--- a/MicroWaveData/packet19.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet2.udp b/MicroWaveData/packet2.udp
deleted file mode 100644
index c1b9840..0000000
--- a/MicroWaveData/packet2.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet20.udp b/MicroWaveData/packet20.udp
deleted file mode 100644
index 90b9361..0000000
--- a/MicroWaveData/packet20.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet21.udp b/MicroWaveData/packet21.udp
deleted file mode 100644
index 1c42e28..0000000
--- a/MicroWaveData/packet21.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet22.udp b/MicroWaveData/packet22.udp
deleted file mode 100644
index 144c58f..0000000
--- a/MicroWaveData/packet22.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet23.udp b/MicroWaveData/packet23.udp
deleted file mode 100644
index 3955833..0000000
--- a/MicroWaveData/packet23.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet24.udp b/MicroWaveData/packet24.udp
deleted file mode 100644
index 6ca2a5b..0000000
--- a/MicroWaveData/packet24.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet25.udp b/MicroWaveData/packet25.udp
deleted file mode 100644
index ba36f9e..0000000
--- a/MicroWaveData/packet25.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet26.udp b/MicroWaveData/packet26.udp
deleted file mode 100644
index e919a43..0000000
--- a/MicroWaveData/packet26.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet27.udp b/MicroWaveData/packet27.udp
deleted file mode 100644
index 2075503..0000000
--- a/MicroWaveData/packet27.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet28.udp b/MicroWaveData/packet28.udp
deleted file mode 100644
index 16544f4..0000000
--- a/MicroWaveData/packet28.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet29.udp b/MicroWaveData/packet29.udp
deleted file mode 100644
index dd261f7..0000000
--- a/MicroWaveData/packet29.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet3.udp b/MicroWaveData/packet3.udp
deleted file mode 100644
index 4648123..0000000
--- a/MicroWaveData/packet3.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet30.udp b/MicroWaveData/packet30.udp
deleted file mode 100644
index b8179c4..0000000
--- a/MicroWaveData/packet30.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet31.udp b/MicroWaveData/packet31.udp
deleted file mode 100644
index 9772cb9..0000000
--- a/MicroWaveData/packet31.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet32.udp b/MicroWaveData/packet32.udp
deleted file mode 100644
index c64b0af..0000000
--- a/MicroWaveData/packet32.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet33.udp b/MicroWaveData/packet33.udp
deleted file mode 100644
index 702ac41..0000000
--- a/MicroWaveData/packet33.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet34.udp b/MicroWaveData/packet34.udp
deleted file mode 100644
index 79dccdc..0000000
--- a/MicroWaveData/packet34.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet35.udp b/MicroWaveData/packet35.udp
deleted file mode 100644
index eab9861..0000000
--- a/MicroWaveData/packet35.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet36.udp b/MicroWaveData/packet36.udp
deleted file mode 100644
index 9ac7d57..0000000
--- a/MicroWaveData/packet36.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet37.udp b/MicroWaveData/packet37.udp
deleted file mode 100644
index e851049..0000000
--- a/MicroWaveData/packet37.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet38.udp b/MicroWaveData/packet38.udp
deleted file mode 100644
index 8f3f3de..0000000
--- a/MicroWaveData/packet38.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet39.udp b/MicroWaveData/packet39.udp
deleted file mode 100644
index 218e0c0..0000000
--- a/MicroWaveData/packet39.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet4.udp b/MicroWaveData/packet4.udp
deleted file mode 100644
index 8fbfa0e..0000000
--- a/MicroWaveData/packet4.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet40.udp b/MicroWaveData/packet40.udp
deleted file mode 100644
index 30da04c..0000000
--- a/MicroWaveData/packet40.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet41.udp b/MicroWaveData/packet41.udp
deleted file mode 100644
index 427cb47..0000000
--- a/MicroWaveData/packet41.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet42.udp b/MicroWaveData/packet42.udp
deleted file mode 100644
index 580cebd..0000000
--- a/MicroWaveData/packet42.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet43.udp b/MicroWaveData/packet43.udp
deleted file mode 100644
index efbccb5..0000000
--- a/MicroWaveData/packet43.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet44.udp b/MicroWaveData/packet44.udp
deleted file mode 100644
index 93722e8..0000000
--- a/MicroWaveData/packet44.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet45.udp b/MicroWaveData/packet45.udp
deleted file mode 100644
index ac7f7a1..0000000
--- a/MicroWaveData/packet45.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet46.udp b/MicroWaveData/packet46.udp
deleted file mode 100644
index 7571a79..0000000
--- a/MicroWaveData/packet46.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet47.udp b/MicroWaveData/packet47.udp
deleted file mode 100644
index 022174c..0000000
--- a/MicroWaveData/packet47.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet48.udp b/MicroWaveData/packet48.udp
deleted file mode 100644
index 028df1f..0000000
--- a/MicroWaveData/packet48.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet49.udp b/MicroWaveData/packet49.udp
deleted file mode 100644
index fd55199..0000000
--- a/MicroWaveData/packet49.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet5.udp b/MicroWaveData/packet5.udp
deleted file mode 100644
index b1be8fa..0000000
--- a/MicroWaveData/packet5.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet50.udp b/MicroWaveData/packet50.udp
deleted file mode 100644
index 63a569c..0000000
--- a/MicroWaveData/packet50.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet51.udp b/MicroWaveData/packet51.udp
deleted file mode 100644
index 2592ed5..0000000
--- a/MicroWaveData/packet51.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet52.udp b/MicroWaveData/packet52.udp
deleted file mode 100644
index af91f8a..0000000
--- a/MicroWaveData/packet52.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet53.udp b/MicroWaveData/packet53.udp
deleted file mode 100644
index aa07cce..0000000
--- a/MicroWaveData/packet53.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet54.udp b/MicroWaveData/packet54.udp
deleted file mode 100644
index a3ecd0a..0000000
--- a/MicroWaveData/packet54.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet55.udp b/MicroWaveData/packet55.udp
deleted file mode 100644
index b02a1f3..0000000
--- a/MicroWaveData/packet55.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet56.udp b/MicroWaveData/packet56.udp
deleted file mode 100644
index 94ab22f..0000000
--- a/MicroWaveData/packet56.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet57.udp b/MicroWaveData/packet57.udp
deleted file mode 100644
index 8f7132c..0000000
--- a/MicroWaveData/packet57.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet58.udp b/MicroWaveData/packet58.udp
deleted file mode 100644
index b78bcd5..0000000
--- a/MicroWaveData/packet58.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet59.udp b/MicroWaveData/packet59.udp
deleted file mode 100644
index c91c8a7..0000000
--- a/MicroWaveData/packet59.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet6.udp b/MicroWaveData/packet6.udp
deleted file mode 100644
index fde0113..0000000
--- a/MicroWaveData/packet6.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet60.udp b/MicroWaveData/packet60.udp
deleted file mode 100644
index 2f5012a..0000000
--- a/MicroWaveData/packet60.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet61.udp b/MicroWaveData/packet61.udp
deleted file mode 100644
index efb3fd9..0000000
--- a/MicroWaveData/packet61.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet62.udp b/MicroWaveData/packet62.udp
deleted file mode 100644
index 1dc13e5..0000000
--- a/MicroWaveData/packet62.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet63.udp b/MicroWaveData/packet63.udp
deleted file mode 100644
index 39fb456..0000000
--- a/MicroWaveData/packet63.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet64.udp b/MicroWaveData/packet64.udp
deleted file mode 100644
index 2b31395..0000000
--- a/MicroWaveData/packet64.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet65.udp b/MicroWaveData/packet65.udp
deleted file mode 100644
index cc20dd3..0000000
--- a/MicroWaveData/packet65.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet66.udp b/MicroWaveData/packet66.udp
deleted file mode 100644
index cc9834a..0000000
--- a/MicroWaveData/packet66.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet67.udp b/MicroWaveData/packet67.udp
deleted file mode 100644
index 7bd1728..0000000
--- a/MicroWaveData/packet67.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet68.udp b/MicroWaveData/packet68.udp
deleted file mode 100644
index cbdae1b..0000000
--- a/MicroWaveData/packet68.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet69.udp b/MicroWaveData/packet69.udp
deleted file mode 100644
index 09bbe10..0000000
--- a/MicroWaveData/packet69.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet7.udp b/MicroWaveData/packet7.udp
deleted file mode 100644
index f39b222..0000000
--- a/MicroWaveData/packet7.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet70.udp b/MicroWaveData/packet70.udp
deleted file mode 100644
index f0f22f1..0000000
--- a/MicroWaveData/packet70.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet71.udp b/MicroWaveData/packet71.udp
deleted file mode 100644
index 4a6e31c..0000000
--- a/MicroWaveData/packet71.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet72.udp b/MicroWaveData/packet72.udp
deleted file mode 100644
index 6aa2d01..0000000
--- a/MicroWaveData/packet72.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet73.udp b/MicroWaveData/packet73.udp
deleted file mode 100644
index ef5e1e2..0000000
--- a/MicroWaveData/packet73.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet74.udp b/MicroWaveData/packet74.udp
deleted file mode 100644
index abad527..0000000
--- a/MicroWaveData/packet74.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet75.udp b/MicroWaveData/packet75.udp
deleted file mode 100644
index 84cf797..0000000
--- a/MicroWaveData/packet75.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet76.udp b/MicroWaveData/packet76.udp
deleted file mode 100644
index 88b736a..0000000
--- a/MicroWaveData/packet76.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet77.udp b/MicroWaveData/packet77.udp
deleted file mode 100644
index 0b3ff02..0000000
--- a/MicroWaveData/packet77.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet78.udp b/MicroWaveData/packet78.udp
deleted file mode 100644
index 1be3d16..0000000
--- a/MicroWaveData/packet78.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet79.udp b/MicroWaveData/packet79.udp
deleted file mode 100644
index 78066c9..0000000
--- a/MicroWaveData/packet79.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet8.udp b/MicroWaveData/packet8.udp
deleted file mode 100644
index e253b20..0000000
--- a/MicroWaveData/packet8.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet80.udp b/MicroWaveData/packet80.udp
deleted file mode 100644
index ac896f2..0000000
--- a/MicroWaveData/packet80.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet81.udp b/MicroWaveData/packet81.udp
deleted file mode 100644
index 2b31395..0000000
--- a/MicroWaveData/packet81.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet82.udp b/MicroWaveData/packet82.udp
deleted file mode 100644
index 81c14a1..0000000
--- a/MicroWaveData/packet82.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet83.udp b/MicroWaveData/packet83.udp
deleted file mode 100644
index c8a9c4d..0000000
--- a/MicroWaveData/packet83.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet84.udp b/MicroWaveData/packet84.udp
deleted file mode 100644
index 7fa678f..0000000
--- a/MicroWaveData/packet84.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet85.udp b/MicroWaveData/packet85.udp
deleted file mode 100644
index a0ec621..0000000
--- a/MicroWaveData/packet85.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet86.udp b/MicroWaveData/packet86.udp
deleted file mode 100644
index 9c707b5..0000000
--- a/MicroWaveData/packet86.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet87.udp b/MicroWaveData/packet87.udp
deleted file mode 100644
index 8d608a5..0000000
--- a/MicroWaveData/packet87.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet88.udp b/MicroWaveData/packet88.udp
deleted file mode 100644
index db31558..0000000
--- a/MicroWaveData/packet88.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet89.udp b/MicroWaveData/packet89.udp
deleted file mode 100644
index 1077acf..0000000
--- a/MicroWaveData/packet89.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet9.udp b/MicroWaveData/packet9.udp
deleted file mode 100644
index 411ee5c..0000000
--- a/MicroWaveData/packet9.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet90.udp b/MicroWaveData/packet90.udp
deleted file mode 100644
index 68a44a8..0000000
--- a/MicroWaveData/packet90.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet91.udp b/MicroWaveData/packet91.udp
deleted file mode 100644
index aa7fac1..0000000
--- a/MicroWaveData/packet91.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet92.udp b/MicroWaveData/packet92.udp
deleted file mode 100644
index 5d4b12f..0000000
--- a/MicroWaveData/packet92.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet93.udp b/MicroWaveData/packet93.udp
deleted file mode 100644
index 273e0bf..0000000
--- a/MicroWaveData/packet93.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet94.udp b/MicroWaveData/packet94.udp
deleted file mode 100644
index c519c4e..0000000
--- a/MicroWaveData/packet94.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet95.udp b/MicroWaveData/packet95.udp
deleted file mode 100644
index f72c0fb..0000000
--- a/MicroWaveData/packet95.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet96.udp b/MicroWaveData/packet96.udp
deleted file mode 100644
index a7614ef..0000000
--- a/MicroWaveData/packet96.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet97.udp b/MicroWaveData/packet97.udp
deleted file mode 100644
index cd53dd6..0000000
--- a/MicroWaveData/packet97.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet98.udp b/MicroWaveData/packet98.udp
deleted file mode 100644
index 427d702..0000000
--- a/MicroWaveData/packet98.udp
+++ /dev/null
Binary files differ
diff --git a/MicroWaveData/packet99.udp b/MicroWaveData/packet99.udp
deleted file mode 100644
index 6ccc285..0000000
--- a/MicroWaveData/packet99.udp
+++ /dev/null
Binary files differ
diff --git a/README.md b/README.md
index d017b81..ff25d9a 100644
--- a/README.md
+++ b/README.md
@@ -7,25 +7,12 @@
 
 ## 鍒涘缓鐜
 
-```
-    conda create -n aerial python=3.9
-    conda install --name aerial flask pika protobuf requests
-    conda activate aerial
-    pip install pika opencv-contrib-python
+Just click ```JustDoubleClickMe.bat``` file to auto setiing the env, which is located in the ```env``` folder.
 
+## 杩愯閲囬泦绔�  
 
-```
-
-## 杩愯閲囬泦绔�
-
-```
-    cd src/Client
-    python Client.py
-```
+Double click to run  ```startClient.bat```
 
 ## 杩愯鏈嶅姟绔�
 
-```
-    cd src/Server
-    python Server.py
-```
+Double click to run  ```startServer.bat```
diff --git a/env/ExportEnv.bat b/env/ExportEnv.bat
new file mode 100644
index 0000000..69604f1
--- /dev/null
+++ b/env/ExportEnv.bat
@@ -0,0 +1,10 @@
+@echo off
+
+@REM change to the filepath
+cd %~dp0
+echo change to path: %cd%
+
+echo Exporting env ...
+conda pack -o aerial.tar.gz
+
+echo Done!
diff --git a/env/JustDoubleClickMe.bat b/env/JustDoubleClickMe.bat
new file mode 100644
index 0000000..65863eb
--- /dev/null
+++ b/env/JustDoubleClickMe.bat
@@ -0,0 +1,4 @@
+cd %~dp0
+SettingEnv.bat
+echo Press any key to exit this windiow.
+pause
\ No newline at end of file
diff --git a/env/SettingEnv.bat b/env/SettingEnv.bat
new file mode 100644
index 0000000..c6640ae
--- /dev/null
+++ b/env/SettingEnv.bat
@@ -0,0 +1,19 @@
+@echo off
+
+@REM change to the filepath
+cd %~dp0
+echo change to path: %cd%
+if exist aerial_deploy (
+    echo the folder aerial_deploy is exited, removing it ...
+    rmdir /S /Q aerial_deploy
+    echo remove done!
+) 
+
+mkdir aerial_deploy
+echo setting the env ...
+tar -xzf ./aerial.tar.gz -C %cd%/aerial_deploy
+echo ALL DONE!
+
+
+
+
diff --git a/proto/compile.bat b/proto/compile.bat
deleted file mode 100644
index 16b6b26..0000000
--- a/proto/compile.bat
+++ /dev/null
@@ -1 +0,0 @@
-protoc aerial.proto --python_out=../src/protos
\ No newline at end of file
diff --git a/src/CameraTest/CameraTest.py b/src/CameraTest/CameraTest.py
deleted file mode 100644
index 23fe756..0000000
--- a/src/CameraTest/CameraTest.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from concurrent.futures import thread
-from itertools import count
-import threading
-import cv2
-import sched
-import time
-
-IMAGE_WIDTH = 3840
-IMAGE_HEIGHT = 2880
-
-class CameraTest:
-
-    def __init__(self, interval: int):
-        self.last_frame = None
-        self.count = 0
-        self.last_time = 0
-        self.interval = interval
-        self.next_time = 0
-        self.cameras = []
-
-    def get_image(self) -> None:
-        # cv2.namedWindow("preview", cv2.WINDOW_NORMAL)
-        # cv2.namedWindow("diff", cv2.WINDOW_NORMAL)
-        while True:
-            this_time = time.time()
-            if this_time > self.next_time:
-                for i, camera in enumerate(self.cameras):
-                    ret, frame = camera.read()
-                    fn = "./Images/{}-{}.jpg".format(i, self.count)
-                    # print(fn, type(frame), frame.shape)
-                    encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 95]
-                    result, encimg = cv2.imencode('.jpg', frame, encode_param)
-                    print(fn, type(encimg), encimg.shape)
-                    # cv2.imshow("preview", frame)
-                    # encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 95]
-                #cv2.imwrite(fn, frame, encode_param)
-                # if not self.last_frame is None:
-                #     diff = cv2.absdiff(frame, self.last_frame)
-                #     print(self.count, this_time, diff.sum())
-                #     cv2.imshow("diff", diff)
-                # self.last_frame = frame1
-                self.count += 1
-                self.next_time += self.interval
-                print("frame ", self.count)
-            # time.sleep(0.1)
-            if cv2.waitKey(1) & 0xFF == ord('q'):
-                break
-            if self.count > 50:
-                break
-        for camera in self.cameras:
-            camera.release()
-        # cv2.destroyAllWindows()
-
-    def open_cameras(self) -> bool:
-        """
-        Test the ports and returns a tuple with the available ports and the ones that are working.
-        """
-        for i in range(0,4):
-            camera = cv2.VideoCapture(i, cv2.CAP_DSHOW)
-            #camera = cv2.VideoCapture(i, cv2.CAP_DSHOW)
-            # 璁剧疆鎽勫儚澶磋澶囧垎杈ㄧ巼
-            # camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter.fourcc('M', 'J', 'P', 'G'))
-            camera.set(cv2.CAP_PROP_FRAME_WIDTH, IMAGE_WIDTH)
-            camera.set(cv2.CAP_PROP_FRAME_HEIGHT, IMAGE_HEIGHT)
-            camera.set(cv2.CAP_PROP_FPS, 1)
-            if camera.isOpened():
-                is_reading, img = camera.read()
-                w = camera.get(3)
-                h = camera.get(4)
-                if is_reading:
-                    print("Port %s is working and reads images (%s x %s)" % (i, w, h))
-                    self.cameras.append(camera)
-
-    def start_capture(self):
-        self.next_time = time.time() + self.interval
-        threading.Thread(target=self.get_image).start()
-
-if __name__ == '__main__':
-    image = {}
-    camera = CameraTest(1)
-    camera.open_cameras()
-    camera.start_capture()
-
diff --git a/src/CameraTest/Images/.gitignore b/src/CameraTest/Images/.gitignore
deleted file mode 100644
index 4bc4044..0000000
--- a/src/CameraTest/Images/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.jpg
\ No newline at end of file
diff --git a/src/Client.py b/src/Client.py
new file mode 100644
index 0000000..3e0b982
--- /dev/null
+++ b/src/Client.py
@@ -0,0 +1,32 @@
+import signal
+import os, sys
+
+batfile_dir = os.path.join(__file__, "..", "protos/compile.bat" )
+batfile_path = os.path.abspath(batfile_dir)
+os.system(batfile_path)
+
+from engine.AcquisitionMain import AcquisitionMain
+from engine.AcquisitionMain import Config
+
+def quit(signum, frame):
+    print('You choose to stop me.')
+    sys.exit()
+
+if __name__ == '__main__':
+    print("Start AcquisitionMain")
+    signal.signal(signal.SIGINT, quit)
+    signal.signal(signal.SIGTERM, quit)
+    # client = AcquisitionMain('192.168.3.54', 'chai',
+    #                       'password123', 'aerial_rpc', 'pod1', 'http://192.168.3.54:5000/api', 10)
+    config_dir = os.path.join(__file__, "..", "config.json" )
+    config_path = os.path.abspath(config_dir)
+    config = Config(config_path)
+    client = AcquisitionMain('localhost',
+                             'chai',
+                             'password123', 
+                             'aerial_rpc', 
+                             'pod1', 
+                             'http://localhost:5000/api', 
+                             'localhost', # udp server host, 192.168.2.65 for production
+                             config)
+    client.start()
diff --git a/src/Client/AerialClient.py b/src/Client/AerialClient.py
deleted file mode 100644
index 9a8db31..0000000
--- a/src/Client/AerialClient.py
+++ /dev/null
@@ -1,264 +0,0 @@
-from socketserver import BaseRequestHandler, UDPServer
-import sys  # NOQA: E402
-sys.path.append('../')  # NOQA: E402
-import time
-import sched
-import pika
-from protos import aerial_pb2 as pb
-import uuid
-import cv2
-import logging
-import numpy as np
-import requests
-import threading  # NOQA: E402
-import MicroWaveParser
-import queue
-
-IMAGE_WIDTH = 3840
-IMAGE_HEIGHT = 2880
-CAMERA_COUNT = 4
-
-micro_wave_parser = MicroWaveParser.MicroWaveParser()
-udp_queue = queue.Queue()
-
-class MicroWaveUDPServer(BaseRequestHandler):
-    def handle(self):
-        # Get message and client socket
-        global micro_wave_parser
-        msg, sock = self.request
-        print("received a udp packet, len={}".format(len(msg)))
-        micro_wave_parser.AddBuf(msg)
-        if micro_wave_parser.PointCloudReady:
-            udp_queue.put(micro_wave_parser.PointCloud)
-            micro_wave_parser.PointCloudReady = False
-
-
-class AerialClient:
-    def __init__(self, host: str, user: str, password: str, request_queue: str, device_id: str, api_url: str, interval) -> None:
-        self.host: str = host
-        self.user: str = user
-        self.password: str = password
-        self.request_queue: str = request_queue
-        self.device_id: str = device_id
-        self.message_send: int = 0
-        self.api_url: str = api_url
-        self.cameras: list = []
-        self._def_callback_routes()
-        self.next_time = 0
-        self.interval = interval
-
-    def _set_logging(self):
-        logging.basicConfig(level=logging.INFO,
-                            format='%(asctime)s %(levelname)s %(message)s',
-                            datefmt='%Y-%m-%d %H:%M:%S')
-        self.logger = logging.getLogger(__name__)
-
-    def _send_data(self, req: pb.RequestCommand) -> None:
-        req.device_id = self.device_id
-        body = req.SerializeToString()
-        self.channel.basic_publish(
-            exchange='',
-            routing_key=self.request_queue,
-            body=body)
-        self.message_send += 1
-        return
-
-    def _call(self, req: pb.RequestCommand, timeout: int = 10) -> pb.ResponseCommand:
-        req.device_id = self.device_id
-        self.response = None
-        self.corr_id = str(uuid.uuid4())  # 鐢熸垚涓�涓湇鍔$杩斿洖娑堟伅鐨勯槦鍒楀悕
-
-        body = req.SerializeToString()
-        response = requests.post(self.api_url, data=body, headers={
-                                 'Content-Type': 'application/x-protobuf'})
-        resp = pb.ResponseCommand()
-        resp.ParseFromString(response.content)
-        return resp
-
-    def _connect(self) -> None:
-        credentials = pika.PlainCredentials(self.user, self.password)
-        parameters = pika.ConnectionParameters(
-            credentials=credentials, host=self.host)
-
-        # 杩炴帴闃熷垪鏈嶅姟鍣�
-        self.connection = pika.BlockingConnection(parameters)
-        self.channel = self.connection.channel()
-
-        result = self.channel.queue_declare(queue='', exclusive=True)
-        self.callback_queue = result.method.queue
-        self.channel.basic_consume(
-            queue=self.callback_queue,
-            on_message_callback=self._on_response,
-            auto_ack=True)
-
-    def _on_response(self, ch, method, props, body) -> None:
-        if self.corr_id == props.correlation_id:
-            self.response = body
-
-    def _send_point_cloud_cmd(self, point_cloud):
-        req = pb.RequestCommand()
-        req.sys_command = pb.SysCommand.MICRO_WAVE_COMMAND
-        req.sub_command = pb.SubCommand.MICRO_WAVE_SUB_COMMAND
-        data1 = pb.MicroWaveMessage()
-        data1.length = len(point_cloud)
-        data1.data = bytes(point_cloud)
-        data1_str = data1.SerializeToString()
-        req.data1.length = len(data1_str)
-        req.data1.data = data1_str
-        self._send_data(req)
-
-    def _send_image_cmd(self):
-        data1 = pb.ImagesMessage()
-        for i in range(0, len(self.cameras)):
-            ret, frame = self.cameras[i].read()
-            encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 95]
-            result, encimg = cv2.imencode('.jpg', frame, encode_param)
-            if frame is not None:
-                np_array = np.asarray(encimg)  # frame 杞崲涓簄umpy鏁扮粍
-                byte_array = np_array.tobytes()  # numpy鏁扮粍杞崲涓篵yte鏁扮粍
-                if i == 0:
-                    data1.image1 = byte_array
-                elif i == 1:
-                    data1.image2 = byte_array
-                elif i == 2:
-                    data1.image3 = byte_array
-                elif i == 3:
-                    data1.image4 = byte_array
-                elif i == 4:
-                    data1.image5 = byte_array
-        data1_str = data1.SerializeToString()
-
-        req = pb.RequestCommand()
-        req.sys_command = pb.SysCommand.FIVE_G_COMMAND
-        req.sub_command = pb.SubCommand.FIVE_G_SEND_IMAGE_SUB_COMMAND
-        req.data1.length = len(data1_str)
-        # print(f'data length={req.data1.length}')
-        req.data1.data = data1_str
-        self._send_data(req)
-
-    def _send_power_on_message(self) -> pb.ResponseCommand:
-        print("send power on message")
-        req = pb.RequestCommand()
-        req.sys_command = pb.SysCommand.SYS_COMMAND
-        req.sub_command = pb.SubCommand.POWER_ON_SUB_COMMAND
-        return self._call(req)
-
-    def _send_power_off_message(self) -> pb.ResponseCommand:
-        print("send power off message")
-        req = pb.RequestCommand()
-        req.sys_command = pb.SysCommand.SYS_COMMAND
-        req.sub_command = pb.SubCommand.POWER_OFF_SUB_COMMAND
-        return self._call(req)
-
-    def _capture_loop(self) -> None:
-        while True:
-            this_time = time.time()
-            if this_time > self.next_time:
-                self._send_image_cmd()
-                print(f"send image message: {self.send_image_count}")
-                self.send_image_count += 1
-                self.next_time += self.interval
-
-            if not udp_queue.empty():
-                point_cloud = udp_queue.get()
-                self._send_point_cloud_cmd(point_cloud)
-            if cv2.waitKey(1) & 0xFF == ord('q'):
-                break
-        for camera in self.cameras:
-            camera.release()
-
-    def _close(self) -> None:
-        self.connection.close()
-
-    def _open_cameras(self):
-        """
-        Test the ports and returns a tuple with the available ports and the ones that are working.
-        """
-        self.cameras = []
-        self.working_ports = []
-        for dev_port in range(CAMERA_COUNT):
-            camera = cv2.VideoCapture(dev_port, cv2.CAP_DSHOW)
-            camera.set(cv2.CAP_PROP_FRAME_WIDTH, IMAGE_WIDTH)
-            camera.set(cv2.CAP_PROP_FRAME_HEIGHT, IMAGE_HEIGHT)
-            camera.set(cv2.CAP_PROP_FPS, 1)
-            if camera.isOpened():
-                is_reading, img = camera.read()
-                w = camera.get(3)
-                h = camera.get(4)
-                if is_reading:
-                    print("Port %s is working and reads images (%s x %s)" %
-                          (dev_port, h, w))
-                    self.working_ports.append(dev_port)
-                    self.cameras.append(camera)
-
-    def _on_sys_command(self, req: pb.RequestCommand):
-        print('syscommand received')
-        return
-
-    def _default(self, req: pb.RequestCommand):
-        print('default handler')
-        return
-
-    def _def_callback_routes(self):
-        self.routes = {
-            pb.SysCommand.SYS_COMMAND: self._on_sys_command,
-        }
-
-    def _on_callback_request(self, ch, method, properties, body):
-        print('callback received')
-        # req = pb.RequestCommand()
-        # req.ParseFromString(body)
-        # dispatcher = self.routes.get(req.sys_command, self._default)
-        # resp = pb.ResponseCommand()
-        # dispatcher(req)
-        ch.basic_ack(delivery_tag=method.delivery_tag)
-        return
-
-    def _init_callback(self, host: str, user: str, password: str, queue: str):
-        credentials = pika.PlainCredentials(user, password)
-        parameters = pika.ConnectionParameters(
-            credentials=credentials, host=host)
-        connection = pika.BlockingConnection(parameters)
-        self.callback_channel = connection.channel()
-        self.callback_channel.queue_declare(queue=queue, durable=False)
-        self.callback_channel.basic_qos(prefetch_count=1)
-        self.callback_channel.basic_consume(
-            on_message_callback=self._on_callback_request, queue=queue)
-
-    def callback_consuming(self):
-        self.callback_channel.start_consuming()
-
-    def _start_callback(self):
-        t = threading.Thread(target=self.callback_consuming)
-        t.start()
-
-    def start_capture_thread(self):
-        self.next_time = time.time() + self.interval
-        threading.Thread(target=self._capture_loop).start()
-
-    def _udp_loop(self):
-        host = ''
-        port = 9911
-        self.udpserver = UDPServer((host, port), MicroWaveUDPServer)
-        print(time.asctime(),'UDP server started on port', port)
-        self.udpserver.serve_forever()
-
-    def start_udp_thread(self):
-        threading.Thread(target=self._udp_loop).start()
-
-    def start(self) -> None:
-        self._open_cameras()
-        if len(self.cameras) == 0:
-            print("No camera available")
-            return
-        self._init_callback(self.host, self.user, self.password, self.device_id + "_callback")
-        self._start_callback()
-        self._connect()
-        resp = self._send_power_on_message()
-        if resp.state == pb.ResponseState.SUCCESS:
-            print("received power on response SUCCESS, start send images")
-            self.send_image_count = 0
-            self.start_capture_thread()
-            self.start_udp_thread()
-        else:
-            print("power on failed")
diff --git a/src/Client/Client.py b/src/Client/Client.py
deleted file mode 100644
index 8a9cde1..0000000
--- a/src/Client/Client.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import sys  # NOQA: E402
-sys.path.append('../')  # NOQA: E402
-from AerialClient import AerialClient
-from protos import aerial_pb2 as pb
-
-
-if __name__ == '__main__':
-
-    print("Start AerialClient")
-    client = AerialClient('192.168.3.54', 'chai',
-                          'password123', 'aerial_rpc', 'pod1', 'http://192.168.3.54:5000/api', 10)
-    client.start()
diff --git a/src/Client/MicroWaveParser.py b/src/Client/MicroWaveParser.py
deleted file mode 100644
index 955e8e5..0000000
--- a/src/Client/MicroWaveParser.py
+++ /dev/null
@@ -1,210 +0,0 @@
-from ctypes import Structure, c_byte, c_char, c_int16, c_ubyte, c_uint32, c_uint16, sizeof, string_at
-from sys import argv
-import os 
-
-UDP_STATE_IDLE = 0
-UDP_STATE_HAND_SHAKE = 1
-UDP_STATE_DATA = 2
-UDP_STATE_FOOTER  = 3
-
-HAND_SHAKE_MAGIC = b"\x01\x09\x08\x09\x01\x00\x02\x02" 
-HEADER_MAGIC = b"\x02\x01\x04\x03\x06\x05\x08\x07" 
-
-class Handshake(Structure):
-    _pack_ = 1
-    _fields_=[
-        ('Magic', c_ubyte*8),
-        ('FrameDataLength', c_uint32),
-        ('Reserved', c_ubyte * 12)
-    ]
-
-class HeaderBlock(Structure):
-    _pack_ = 1
-    _fields_ = [
-        ('Magic', c_ubyte * 8),             # [2 1 4 3 6 5 8 7]
-        ('FrameNumber', c_uint32),          # frame number
-        ('VersionNumber', c_uint32),        # Version number, uint32_t format: MMddhhmm
-        ('NumberOfDetection', c_uint16),    # Number of Detection in the frame
-        ('NumberOfTrack', c_uint16),        # Number of Detection in the frame
-        ('HostSpeed', c_uint16),            # divide by 100 to get ego speed in m/s (e.g. 6453 = 64.53 m/s, e.g. -2456 = -24.56 m/s)
-        ('HostAngle', c_uint16),            # divide by 100 to get host angle in degree. Clockwise rotation corresponds to positive rotation.(e.g. 1400 = 14 deg)
-        ('Reserved', c_ubyte * 8),
-        ('RangeAccuracyIdx', c_uint16),     # divide by 10000 to get value in m
-        ('DopplerAccuracyIdx', c_uint16),   # divide by 10000 to get value in m/s
-        ('AzimuthAccuracyIdx', c_uint16),   # divide by 10000 to get value in degree
-        ('ElevationAccuracyIdx', c_uint16), # divide by 10000 to get value in degree
-        ('DspWorkload', c_ubyte),
-        ('ARMWorkload', c_ubyte),
-        ('Reserved2', c_ubyte * 6),
-    ]
-
-class FooterBlock(Structure):
-    _pack_ = 1
-    _fields_ = [
-        ('Reserved1', c_ubyte * 8),
-        ('RangeAccuracyIdx', c_uint16),         # divide by 10000 to get value in m
-        ('DopplerAccuracyIdx', c_uint16),       # divide by 10000 to get value in m
-        ('AzimuthAccuracyIdx', c_uint16),       # divide by 10000 to get value in degree
-        ('ElevationAccuracyIdx', c_uint16),     # divide by 10000 to get value in degree
-        ('Reserved2', c_ubyte * 16),
-    ]
-
-class DetectionBlock(Structure):
-    _pack_ = 1
-    _fields_ = [
-        ('data', c_ubyte * 8),
-        # ('Reserved1', c_ubyte, 1),
-        # ('Flag', c_ubyte, 1),                   # used for decoding. Source code provided in the SDK. 
-        # ('Reserved2', c_ubyte, 6),
-        # ('PowerValue', c_uint16, 16),           # 0 to 65535, Power value is in dB scale and represents the, signal to noise ratio of the detection. Divide by 100 to decode power. (e.g. 208 = 2.08dB)
-        # ('BetaIndex', c_int16, 10),             # multiply with Elevation Accuracy value to get Elevation (beta) value in degree. 
-        # ('AzimuthIndex', c_int16, 10),          # multiply with Doppler Accuracy value to get Doppler value in m/s.
-        # ('DropplerIndex', c_int16, 10),         # multiply with Doppler Accuracy value to get Doppler value in m/s.
-        # ('RangeIndex', c_uint16, 10),           # multiply with Range Accuracy value to get Range value in m.
-    ]
-
-
-
-    # XYZ: The XYZ coordinates can be retrieved using the following equation:
-    # X = Range * Sin(Alpha) * Cos(Beta)
-    # Y = Range * Sin(Beta)
-    # Z = Range * Cos(Alpha) * Cos(Beta)
-
-class TrackerBlock(Structure):
-    _pack_ = 1
-    _fields_ = [
-        ('TrackID', c_uint32),      # 0 to 4294967296 Track ID
-        ('XPos', c_int16),          # -32768 to 32767, divide by 100 to get x in m (e.g. 6453 = 64.53 m, e.g.: -2456 = -24.56 m)
-        ('YPos', c_int16),          # -32768 to 32767, divide by 100 to get y in m (e.g. 6453 = 64.53 m, e.g. -2456 = -24.56 m)
-        ('ZPos', c_uint16),         # 0 to 65535, divide by 100 to get z in m (e.g. 12654 = 126.54 m)
-        ('XDot', c_int16),          # -32768 to 32767, divide by 100 to get speed in the x direction in m/s (e.g. 6453 = 64.53 m/s, e.g. -2456 = -24.56 m/s)
-        ('YDot', c_int16),          # -32768 to 32767, divide by 100 to get speed in the y direction in m/s (e.g. 6453 = 64.53 m/s, e.g. -2456 = -24.56 m/s)
-        ('ZDot', c_int16),          # -32768 to 32767, divide by 100 to get speed in the z direction in m/s (e.g. 6453 = 64.53 m/s, e.g. -2456 = -24.56 m/s)
-        ('Res1', c_uint16),
-        ('Res2', c_uint16),
-        ('Res3', c_uint16),
-        ('Flag', c_uint16),         # 0 to 65535. 16 1-bit flags, the flag definitions are internal to Oculii.
-                                    # Bit 3 鈥� Bit 15 : Reserved
-                                    # Bit 0, Bit 1, Bit 2 : Track Quality. Currently only values 1 and 2 are used. Filter to use only value 鈥�2鈥�.
-        ('Class', c_uint16),        # 0 to 5. Reserved for future.  
-                                    # 0: Unknown Class
-                                    # 1: Pedestrian
-                                    # 2: Motorcycle/Bike
-                                    # 3: Vehicle and SUV
-                                    # 4: Bus and Truck
-                                    # 5: Background
-        ('Conf', c_uint16),
-        ('Res4', c_uint16),
-        ('Res5', c_uint16),
-    ]
-
-
-def bytes_hex(bytes):
-    return ' '.join(["%02x" % b for b in bytes])
-
-
-class MicroWaveParser:
-    def __init__(self):
-        self.state = UDP_STATE_IDLE
-        self.N = 0
-        self.Nt = 0
-        self.PointCount = 0
-        self.TrackCount = 0
-        self.PointCloudReady = False
-        self.PointCloud = bytearray() 
-        
-    def _receive_data(self, data) -> None:
-        self.data_buf.extend(data)
-        self.ReceivedDataLength += len(data)
-        if self.ReceivedDataLength == self.DataLength:
-            self.state = UDP_STATE_FOOTER
-        return
-
-    def _receive_footer(self, data) -> None:
-        self.footer_buf.extend(data)
-        self.ReceivedFooterLength += len(data)
-        if self.ReceivedFooterLength == sizeof(FooterBlock):
-            footer = FooterBlock.from_buffer_copy(self.footer_buf)
-            self.PointCloudReady = True
-            self.state = UDP_STATE_IDLE
-        return
-    
-    def AddBuf(self, buf: bytes) -> None:
-        buf_size = len(buf)
-        if self.state == UDP_STATE_IDLE:
-            if buf_size == sizeof(Handshake):
-                handshake = Handshake.from_buffer_copy(buf)
-                if HAND_SHAKE_MAGIC == string_at(handshake.Magic, 8):
-                    self.PointCloudReady = False
-                    self.state = UDP_STATE_HAND_SHAKE
-                    self.PointCloud = bytearray(buf)
-        elif self.state == UDP_STATE_HAND_SHAKE:
-            self.PointCloud.extend(buf)
-            header = HeaderBlock.from_buffer_copy(buf[:sizeof(HeaderBlock)])
-            if HEADER_MAGIC == string_at(header.Magic, 8):
-                self.N = header.NumberOfDetection
-                self.Nt = header.NumberOfTrack
-                self.PointCount = 0
-                self.TrackCount = 0
-                self.DataLength = self.N * 8 + self.Nt * sizeof(TrackerBlock)
-                self.RemainLength = self.DataLength + sizeof(FooterBlock)
-                self.data_buf = bytearray()
-                self.footer_buf = bytearray()
-                self.Points = []
-                self.ReceivedDataLength = 0
-                self.ReceivedFooterLength = 0
-                self.state = UDP_STATE_DATA
-
-                header_size = sizeof(HeaderBlock)
-                # 澶勭悊绗竴涓暟鎹寘涓殑鏁版嵁
-                data_size = min(buf_size - header_size, self.DataLength)
-                self._receive_data(buf[header_size: header_size + data_size])
-                
-                # 鍒ゆ柇绗竴涓暟鎹寘涓槸鍚﹀寘鍚獸ooter Block
-                if (header_size + data_size) < buf_size:
-                    self._receive_footer(buf[header_size + data_size])
-        elif self.state == UDP_STATE_DATA:
-            self.PointCloud.extend(buf)
-            remain_data_size = self.DataLength - self.ReceivedDataLength
-            data_size = min(buf_size, remain_data_size)
-            self._receive_data(buf[: data_size])
-            if data_size < buf_size:
-                self._receive_footer(buf[data_size: ])
-        elif self.state == UDP_STATE_FOOTER:
-            self.PointCloud.extend(buf)
-            self._receive_footer(buf)
-
-if __name__ == '__main__':
-    if len(argv) != 2:
-        print('Usage: python parser.py [filename]')
-        exit(1)
-
-    print(sizeof(Handshake))
-    filename = argv[1]
-    file_size = os.path.getsize(filename)
-    with open(filename, "rb") as udp_file:
-        if file_size == 24:
-            handshake = Handshake.from_buffer_copy(udp_file.read(sizeof(Handshake)))
-            udp_file.close()
-            print('Magic: ', bytes_hex(handshake.Magic))
-            print('FrameDataLength: ', handshake.FrameDataLength)
-            print('Reserved: ', bytes_hex(handshake.Reserved))
-        elif file_size == 256:
-            header = HeaderBlock.from_buffer_copy(udp_file.read(sizeof(HeaderBlock)))
-            udp_file.close()
-            print('Magic: ', bytes_hex(header.Magic))
-            print('FrameNumber: ', header.FrameNumber)
-            print('VersionNumber: ', header.VersionNumber)
-            print('NumberOfDetection: ', header.NumberOfDetection)
-            print('NumberOfTrack: ', header.NumberOfTrack)
-            print('HostSpeed: ', header.HostSpeed)
-            print('HostAngle: ', header.HostAngle)
-            print('Reserved: ', bytes_hex(header.Reserved)) 
-            print('RangeAccuracyIdx: ', header.RangeAccuracyIdx)
-            print('DopplerAccuracyIdx: ', header.DopplerAccuracyIdx)
-            print('AzimuthAccuracyIdx: ', header.AzimuthAccuracyIdx)
-            print('ElevationAccuracyIdx: ', header.ElevationAccuracyIdx)
-            print('DspWorkload: ', header.DspWorkload)
-            print('ARMWorkload: ', header.ARMWorkload)
-            print('Reserved2: ', bytes_hex(header.Reserved2))
-        
\ No newline at end of file
diff --git a/src/Microwave/__init__.py b/src/Microwave/__init__.py
new file mode 100644
index 0000000..44c7895
--- /dev/null
+++ b/src/Microwave/__init__.py
@@ -0,0 +1,4 @@
+from .components import *
+from  .constants import *
+from  .parsers import *
+from  .servers import *
\ No newline at end of file
diff --git a/src/Microwave/components.py b/src/Microwave/components.py
new file mode 100644
index 0000000..2019759
--- /dev/null
+++ b/src/Microwave/components.py
@@ -0,0 +1,172 @@
+from ctypes import Structure, c_ubyte, c_uint16, c_uint32, c_int16
+from bitarray import bitarray, util
+import math
+
+"""
+    UDP packets 0  --> ... --> UDP packets n
+Handshake --> HeaderBlock --> DetectionBlock --> TrackerBlock --> FooterBlock
+
+"""
+
+
+class Handshake(Structure):
+    """
+    Handshake structure, 24 bytes
+    """
+    _pack_ = 1
+    _fields_ = [
+        ('Magic', c_ubyte*8),
+        ('FrameDataLength', c_uint32),
+        ('Reserved', c_ubyte * 12)
+        ]
+
+
+class HeaderBlock(Structure):
+    """
+    Header structure, 48 bytes
+    """
+    _pack_ = 1
+    _fields_ = [
+        ('Magic', c_ubyte * 8),             # [2 1 4 3 6 5 8 7]
+        ('FrameNumber', c_uint32),          # frame number
+        ('VersionNumber', c_uint32),        # Version number, uint32_t format: MMddhhmm
+        ('NumberOfDetection', c_uint16),    # Number of Detection in the frame
+        ('NumberOfTrack', c_uint16),        # Number of Detection in the frame
+        ('HostSpeed', c_uint16),            # divide by 100 to get ego speed in m/s (e.g. 6453 = 64.53 m/s, e.g. -2456 = -24.56 m/s)
+        ('HostAngle', c_uint16),            # divide by 100 to get host angle in degree. Clockwise rotation corresponds to positive rotation.(e.g. 1400 = 14 deg)
+        ('Reserved', c_ubyte * 8),
+        ('RangeAccuracyIdx', c_uint16),     # divide by 10000 to get value in m
+        ('DopplerAccuracyIdx', c_uint16),   # divide by 10000 to get value in m/s
+        ('AzimuthAccuracyIdx', c_uint16),   # divide by 10000 to get value in degree
+        ('ElevationAccuracyIdx', c_uint16), # divide by 10000 to get value in degree
+        ('DspWorkload', c_ubyte),
+        ('ARMWorkload', c_ubyte),
+        ('Reserved2', c_ubyte * 6),
+    ]
+    def __new__(cls, buf):
+        # new instance from buf
+        return cls.from_buffer_copy(buf)
+
+    def __init__(self, data):
+        # claculate parameters
+        self.RangeAccuracy = self.RangeAccuracyIdx / 10000.0
+        self.DopplerAccuracy = self.DopplerAccuracyIdx / 10000.0
+        self.AzimuthAccuracy = self.AzimuthAccuracyIdx / 10000.0
+        self.ElevationAccuracy = self.ElevationAccuracyIdx / 10000.0
+
+
+
+class FooterBlock(Structure):
+    """
+    Footer structure, 32 bytes
+    """
+    _pack_ = 1
+    _fields_ = [
+        ('Reserved1', c_ubyte * 8),
+        ('RangeAccuracyIdx', c_uint16),         # divide by 10000 to get value in m
+        ('DopplerAccuracyIdx', c_uint16),       # divide by 10000 to get value in m
+        ('AzimuthAccuracyIdx', c_uint16),       # divide by 10000 to get value in degree
+        ('ElevationAccuracyIdx', c_uint16),     # divide by 10000 to get value in degree
+        ('Reserved2', c_ubyte * 16),
+    ]
+    def __new__(cls, buf):
+        # new instance from buf
+        return cls.from_buffer_copy(buf)
+
+    def __init__(self, data):
+        # claculate parameters
+        self.RangeAccuracy = self.RangeAccuracyIdx / 10000.0
+        self.DopplerAccuracy = self.DopplerAccuracyIdx / 10000.0
+        self.AzimuthAccuracy = self.AzimuthAccuracyIdx / 10000.0
+        self.ElevationAccuracy = self.ElevationAccuracyIdx / 10000.0
+
+
+class DetectionBlock:
+    """
+    Detection structure
+    using bitarray parse structure because of ctypes can't parse compact bitfields
+    """
+
+    def __init__(self, header: HeaderBlock, footer: FooterBlock, data):
+        buf = bytearray(data)
+        buf.reverse()
+        bits = bitarray()
+        bits.frombytes(bytes(buf))
+        # print(bits)
+        # parse data structure
+        self.Flag = bits[1]
+        self.RangeIndex = util.ba2int(bits[54:64])
+        self.DropplerIndex = util.ba2int(bits[45:54])
+        if bits[44]:
+            self.DropplerIndex -= 512
+        self.AzimuthIndex = util.ba2int(bits[35:44])
+        if bits[34]:
+            self.AzimuthIndex -= 512
+        self.BetaIndex = util.ba2int(bits[25:34])
+        if bits[24]:
+            self.BetaIndex -= 512
+        self.PowerValue = util.ba2int(bits[8:24])
+
+        if not self.Flag:
+            # using header's parameters
+            RangeAccuracy = header.RangeAccuracy
+            DopplerAccuracy = header.DopplerAccuracy
+            AzimuthAccuracy = header.AzimuthAccuracy
+            ElevationAccuracy = header.ElevationAccuracy
+        else:
+            # using footer's parameters
+            RangeAccuracy = footer.RangeAccuracy
+            DopplerAccuracy = footer.DopplerAccuracy
+            AzimuthAccuracy = footer.AzimuthAccuracy
+            ElevationAccuracy = footer.ElevationAccuracy
+
+        # claculation
+        self.Power = self.PowerValue / 100.0
+        self.Range = self.RangeIndex * RangeAccuracy
+        self.Doppler = self.DropplerIndex * DopplerAccuracy
+        self.Beta = self.BetaIndex * ElevationAccuracy
+        self.Azimuth = self.AzimuthIndex * AzimuthAccuracy
+        # degrees to radians
+        alpha = math.radians(self.Azimuth)
+        beta = math.radians(self.Beta)
+        # XYZ: The XYZ coordinates can be retrieved using the following equation:
+        # X = Range * Sin(Alpha) * Cos(Beta)
+        # Y = Range * Sin(Beta)
+        # Z = Range * Cos(Alpha) * Cos(Beta)
+        # or
+        # Z = sqrt(Range^2 - X^2 - Y^2)
+        self.X = self.Range * math.sin(alpha) * math.cos(beta)
+        self.Y = self.Range * math.sin(beta)
+        self.Z = math.sqrt(self.Range * self.Range -
+                           self.X * self.X - self.Y * self.Y)
+
+class TrackerBlock(Structure):
+    """
+    Tracker structure
+    """
+    _pack_ = 1
+    _fields_ = [
+        ('TrackID', c_uint32),      # 0 to 4294967296 Track ID
+        ('XPos', c_int16),          # -32768 to 32767, divide by 100 to get x in m (e.g. 6453 = 64.53 m, e.g.: -2456 = -24.56 m)
+        ('YPos', c_int16),          # -32768 to 32767, divide by 100 to get y in m (e.g. 6453 = 64.53 m, e.g. -2456 = -24.56 m)
+        ('ZPos', c_uint16),         # 0 to 65535, divide by 100 to get z in m (e.g. 12654 = 126.54 m)
+        ('XDot', c_int16),          # -32768 to 32767, divide by 100 to get speed in the x direction in m/s (e.g. 6453 = 64.53 m/s, e.g. -2456 = -24.56 m/s)
+        ('YDot', c_int16),          # -32768 to 32767, divide by 100 to get speed in the y direction in m/s (e.g. 6453 = 64.53 m/s, e.g. -2456 = -24.56 m/s)
+        ('ZDot', c_int16),          # -32768 to 32767, divide by 100 to get speed in the z direction in m/s (e.g. 6453 = 64.53 m/s, e.g. -2456 = -24.56 m/s)
+        ('Res1', c_uint16),
+        ('Res2', c_uint16),
+        ('Res3', c_uint16),
+        ('Flag', c_uint16),         # 0 to 65535. 16 1-bit flags, the flag definitions are internal to Oculii.
+                                    # Bit 3 鈥� Bit 15 : Reserved
+                                    # Bit 0, Bit 1, Bit 2 : Track Quality. Currently only values 1 and 2 are used. Filter to use only value 鈥�2鈥�.
+        ('Class', c_uint16),        # 0 to 5. Reserved for future.  
+                                    # 0: Unknown Class
+                                    # 1: Pedestrian
+                                    # 2: Motorcycle/Bike
+                                    # 3: Vehicle and SUV
+                                    # 4: Bus and Truck
+                                    # 5: Background
+        ('Conf', c_uint16),
+        ('Res4', c_uint16),
+        ('Res5', c_uint16),
+    ]
diff --git a/src/Microwave/constants.py b/src/Microwave/constants.py
new file mode 100644
index 0000000..8346fa5
--- /dev/null
+++ b/src/Microwave/constants.py
@@ -0,0 +1,2 @@
+HAND_SHAKE_MAGIC = b"\x01\x09\x08\x09\x01\x00\x02\x02" 
+HEADER_MAGIC = b"\x02\x01\x04\x03\x06\x05\x08\x07" 
\ No newline at end of file
diff --git a/src/Microwave/demo/MicroWave2Ply.py b/src/Microwave/demo/MicroWave2Ply.py
new file mode 100644
index 0000000..7825244
--- /dev/null
+++ b/src/Microwave/demo/MicroWave2Ply.py
@@ -0,0 +1,35 @@
+import os
+import sys
+
+# add python path of microwave to sys.path
+microwave_path = os.path.join(__file__, *(['..'] * 3))
+microwave_path = os.path.abspath(microwave_path)
+sys.path.insert(0, microwave_path)
+
+from microwave.parsers import MicroWaveParser
+
+if __name__ == '__main__':
+    if len(sys.argv) != 2:
+        print('Usage: python MicroWave2Ply.py [filename.bin]')
+        exit(1)
+
+    filename = sys.argv[1]
+    (fn, ext) = os.path.splitext(filename)
+    with open(filename, "rb") as udp_file:
+        buf = udp_file.read()
+        point_cloud = MicroWaveParser.ParserFrame(buf)
+        with open(fn + ".ply", "w") as f:
+            f.write("ply\n")
+            f.write("format ascii 1.0\n")
+            f.write("comment MicroWave2Ply generated\n")
+            f.write("element vertex %d\n" % len(point_cloud))
+            f.write("property float x\n")
+            f.write("property float y\n")
+            f.write("property float z\n")
+            f.write("element face 0\n")
+            f.write("property list uchar int vertex_indices\n")
+            f.write("end_header\n")
+            for p in point_cloud:
+                x, y, z = p
+                f.write("{:.5f} {:.5f} {:.5f}\n".format(x, y, z))
+            print("Done!")
diff --git a/src/Microwave/demo/MicroWaveReceiverTest.py b/src/Microwave/demo/MicroWaveReceiverTest.py
new file mode 100644
index 0000000..56a3713
--- /dev/null
+++ b/src/Microwave/demo/MicroWaveReceiverTest.py
@@ -0,0 +1,30 @@
+import os
+from sys import argv
+import sys
+
+# add python path of microwave to sys.path
+microwave_path = os.path.join(__file__, *(['..'] * 3))
+microwave_path = os.path.abspath(microwave_path)
+sys.path.insert(0, microwave_path)
+
+from microwave.servers import MicroWaveReceiver
+
+
+if __name__ == '__main__':
+    if len(sys.argv) != 2:
+        print('Usage: python MicroWaveReceiverTest.py [folder]')
+        exit(1)
+    folder = sys.argv[1]
+    files = ["packet{}.udp".format(i) for i in range(100)]
+    receiver = MicroWaveReceiver()
+    for file in files:
+        filename = os.path.join(folder, file)
+        with open(filename, "rb") as f:
+            data = f.read()
+            receiver.AddBuf(data)
+            if receiver.DataFrameReady:
+                frame_number = receiver.Header.FrameNumber
+                bin_name = os.path.join(folder, "{}.bin".format(frame_number))
+                print("Frame {} ready".format(frame_number))
+                with open(bin_name, "wb") as bin_file:
+                    bin_file.write(receiver.DataFrame)
diff --git a/src/Microwave/demo/MicroWaveUDPServerTest.py b/src/Microwave/demo/MicroWaveUDPServerTest.py
new file mode 100644
index 0000000..e35b64e
--- /dev/null
+++ b/src/Microwave/demo/MicroWaveUDPServerTest.py
@@ -0,0 +1,33 @@
+import sys
+import time
+import os
+
+# add python path of microwave to sys.path
+microwave_path = os.path.join(__file__, *(['..'] * 3))
+microwave_path = os.path.abspath(microwave_path)
+sys.path.insert(0, microwave_path)
+
+from microwave.servers import MicroWaveUDPServer
+
+if __name__ == '__main__':
+    if len(sys.argv) == 2:
+        output_folder = sys.argv[1]
+    else:
+        output_folder = '.'
+
+    # host = '192.168.2.65'
+    host = ''
+    port = 9911
+    server = MicroWaveUDPServer((host, port), output_folder)
+    server.packed_count = 0
+    print(time.asctime(),'Server started on port', port)
+    print('....')
+    print('ctrl-c to quit server.')
+    try:
+        server.serve_forever()
+    except KeyboardInterrupt:
+        server.server_close()
+        print(time.asctime(),"Server Stopped")
+    except:
+        server.server_close()
+        print(time.asctime(),"Server Stopped")
\ No newline at end of file
diff --git a/src/Microwave/demo/polty.ipynb b/src/Microwave/demo/polty.ipynb
new file mode 100644
index 0000000..8cd999e
--- /dev/null
+++ b/src/Microwave/demo/polty.ipynb
@@ -0,0 +1,64 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[Open3D WARNING] [ViewControl] SetViewPoint() failed because window height and width are not set.\n"
+     ]
+    }
+   ],
+   "source": [
+    "import open3d as o3d\n",
+    "# visualization of point clouds.\n",
+    "# pcd = o3d.io.read_point_cloud('dense2_without_color.ply')\n",
+    "# show = [pcd]\n",
+    "pcd2522 = o3d.io.read_point_cloud('demoData/2522.ply')\n",
+    "pcd2523 = o3d.io.read_point_cloud('demoData2523.ply')\n",
+    "pcd2524 = o3d.io.read_point_cloud('demoData2524.ply')\n",
+    "pcd2525 = o3d.io.read_point_cloud('demoData2525.ply')\n",
+    "pcd2526 = o3d.io.read_point_cloud('demoData2526.ply')\n",
+    "pcd2527 = o3d.io.read_point_cloud('demoData2527.ply')\n",
+    "show = [pcd2522,pcd2523,pcd2524,pcd2525,pcd2526,pcd2527]\n",
+    "o3d.visualization.draw_geometries(show)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "interpreter": {
+   "hash": "35e65690039548a4ed71bb11d5d15cea8607c9411f67738c48033f1deba4df50"
+  },
+  "kernelspec": {
+   "display_name": "Python 3.9.10 ('huaneng_server')",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.10"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/src/udp_test/udp.py b/src/Microwave/demo/udp_test_get.py
similarity index 76%
rename from src/udp_test/udp.py
rename to src/Microwave/demo/udp_test_get.py
index 85a0c42..26b0c16 100644
--- a/src/udp_test/udp.py
+++ b/src/Microwave/demo/udp_test_get.py
@@ -1,5 +1,14 @@
 from socketserver import BaseRequestHandler, UDPServer
 import time
+import os
+
+
+cudir,_ = os.path.split(__file__)
+file_folder = os.path.join(cudir,"saveUDPData")
+
+if not os.path.exists(file_folder):
+    os.makedirs(file_folder)
+    print(f"make a new folder: {file_folder}")
 
 packed_count = 0
 
@@ -12,7 +21,7 @@
         print('Got connection from {}, {}'.format(self.client_address, packed_count))
         # Get message and client socket
         msg, sock = self.request
-        f = open('packet{}.udp'.format(packed_count), 'wb')
+        f = open('{}/packet{:000006}.udp'.format(file_folder,packed_count), 'wb')
         f.write(msg)
         f.close()
         packed_count += 1
@@ -23,8 +32,8 @@
 if __name__ == '__main__':
 
     global server
-    # host = '192.168.2.65'
     host = ''
+    # host = '192.168.2.65'
     port = 9911
     server =UDPServer((host, port), PointCloudUDPServer)
     print(time.asctime(),'Server started on port', port)
diff --git a/src/Microwave/demo/udp_test_send.py b/src/Microwave/demo/udp_test_send.py
new file mode 100644
index 0000000..9911fc2
--- /dev/null
+++ b/src/Microwave/demo/udp_test_send.py
@@ -0,0 +1,20 @@
+import time
+import socket
+import os, sys
+
+cudir,_ = os.path.split(__file__)
+file_folder = os.path.join(cudir,"demoData")
+
+packed_count = 0
+if __name__ == '__main__':
+
+    files = ["packet{}.udp".format(i) for i in range(0, 100)]
+    for file in files:
+        with open(os.path.join(file_folder,file), "rb") as f:
+            message = f.read()
+            client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+            client_socket.settimeout(1.0)
+            addr = ("127.0.0.1", 9911)
+            client_socket.sendto(message, addr)
+            time.sleep(0.1)
+    print('Done!')
diff --git a/src/Microwave/parsers.py b/src/Microwave/parsers.py
new file mode 100644
index 0000000..97c0b07
--- /dev/null
+++ b/src/Microwave/parsers.py
@@ -0,0 +1,23 @@
+from .components import *
+from .constants import *
+from ctypes import sizeof, string_at
+
+
+
+class MicroWaveParser:
+    @classmethod
+    def ParserFrame(cls, buffer: bytes) -> list:
+        """
+        瑙f瀽瀹屾暣鐨勪竴甯ф暟鎹紝鏁版嵁鍖呮嫭鏁版嵁澶达紝鏁版嵁浣擄紝鏁版嵁灏撅紝杩斿洖鐐逛簯鏁扮粍
+        """
+        header = HeaderBlock(buffer[:sizeof(HeaderBlock)])
+        if HEADER_MAGIC == string_at(header.Magic, 8):
+            footer = FooterBlock(buffer[-sizeof(FooterBlock):])
+            point_cloud = []
+            for i in range(header.NumberOfDetection):
+                detection = DetectionBlock(header, footer, buffer[i * 8 + sizeof(HeaderBlock): (i + 1) * 8 + sizeof(HeaderBlock)])
+                point = (detection.X, detection.Y, detection.Z)
+                point_cloud.append(point)
+            return point_cloud
+        else:
+            return []
\ No newline at end of file
diff --git a/src/Microwave/servers.py b/src/Microwave/servers.py
new file mode 100644
index 0000000..9ce2a7f
--- /dev/null
+++ b/src/Microwave/servers.py
@@ -0,0 +1,146 @@
+import os, sys
+from ctypes import sizeof, string_at
+from socketserver import BaseRequestHandler, UDPServer
+from .components import *
+from .constants import *
+
+# add python path of src to sys.path
+src_path = os.path.join(__file__, *(['..'] * 2))
+src_path = os.path.abspath(src_path)
+sys.path.insert(0, src_path)
+
+from protos import aerial_pb2 as pb
+
+
+
+UDP_STATE_IDLE = 0
+UDP_STATE_HAND_SHAKE = 1
+UDP_STATE_DATA = 2
+UDP_STATE_FOOTER  = 3
+
+class MicroWaveReceiver:
+    """
+    micro wave data receiver
+    use AddBuf method add udp packet to receiver
+    when DataFrameReady == True, you can get data frame
+    """
+    def __init__(self):
+        self.state = UDP_STATE_IDLE
+        self.PointCount = 0
+        self.TrackCount = 0
+        self.DataFrameReady = False
+        self.DataFrame = None 
+        self.Header = None
+        self.Footer = None
+    
+    def _receive_data(self, data) -> None:
+        """
+        receive data packet from udp
+        """
+        self.data_buf.extend(data)
+        self.ReceivedDataLength += len(data)
+        if self.ReceivedDataLength == self.DataLength:
+            # if received data length equal expected data length
+            # then we can get footer block
+            self.state = UDP_STATE_FOOTER
+        return
+
+    def _receive_footer(self, data) -> None:
+        # receive footer block
+        self.footer_buf.extend(data)
+        self.ReceivedFooterLength += len(data)
+        if self.ReceivedFooterLength == sizeof(FooterBlock):
+            self.Footer = FooterBlock.from_buffer_copy(self.footer_buf)
+            # after received footer block, the data frame is ready
+            self.DataFrameReady = True
+            # reset state to idle, ready for next data frame
+            self.state = UDP_STATE_IDLE
+        return
+    
+    def AddBuf(self, buf: bytes) -> None:
+        # add a udp packet to receiver
+        buf_size = len(buf)
+        if self.state == UDP_STATE_IDLE:
+            if buf_size == sizeof(Handshake):
+                handshake = Handshake.from_buffer_copy(buf)
+                if HAND_SHAKE_MAGIC == string_at(handshake.Magic, 8):
+                    self.DataFrameReady = False
+                    self.state = UDP_STATE_HAND_SHAKE
+                    self.DataFrame = None
+                    self.Header = None
+                    self.Footer = None
+        elif self.state == UDP_STATE_HAND_SHAKE:
+            # after received handshake packet, we can get header block 
+            self.Header = HeaderBlock.from_buffer_copy(buf[:sizeof(HeaderBlock)])
+            if HEADER_MAGIC == string_at(self.Header.Magic, 8):
+                self.DataFrame = bytearray(buf)
+                N = self.Header.NumberOfDetection
+                Nt = self.Header.NumberOfTrack
+                self.PointCount = 0
+                self.TrackCount = 0
+                self.DataLength = N * 8 + Nt * sizeof(TrackerBlock)
+                self.RemainLength = self.DataLength + sizeof(FooterBlock)
+                self.data_buf = bytearray()
+                self.footer_buf = bytearray()
+                self.Points = []
+                self.ReceivedDataLength = 0
+                self.ReceivedFooterLength = 0
+                # the next packet should be data block
+                self.state = UDP_STATE_DATA
+
+                header_size = sizeof(HeaderBlock)
+                # 澶勭悊绗竴涓暟鎹寘涓殑鏁版嵁
+                data_size = min(buf_size - header_size, self.DataLength)
+                self._receive_data(buf[header_size: header_size + data_size])
+                
+                # 鍒ゆ柇绗竴涓暟鎹寘涓槸鍚﹀寘鍚獸ooter Block
+                if (header_size + data_size) < buf_size:
+                    self._receive_footer(buf[header_size + data_size])
+        elif self.state == UDP_STATE_DATA:
+            # receive data block
+            self.DataFrame.extend(buf)
+            remain_data_size = self.DataLength - self.ReceivedDataLength
+            data_size = min(buf_size, remain_data_size)
+            self._receive_data(buf[: data_size])
+            if data_size < buf_size:
+                self._receive_footer(buf[data_size: ])
+        elif self.state == UDP_STATE_FOOTER:
+            # receive footer block
+            self.DataFrame.extend(buf)
+            self._receive_footer(buf)
+
+
+
+class MicroWaveRequestHandler(BaseRequestHandler):
+    """
+    micro wave udp request handler
+    """
+    def handle(self):
+        # Get message and client socket
+        buf, _ = self.request
+        receiver = self.server.Receiver
+        # add buffer to receiver
+        receiver.AddBuf(buf)
+        if receiver.DataFrameReady:
+            # dataframe ready, add to udp_queue
+            msg = pb.RequestCommand()
+            msg.sys_command = pb.SysCommand.MICRO_WAVE_COMMAND
+            msg.sub_command = pb.SubCommand.MICRO_WAVE_SUB_COMMAND
+            data1 = pb.MicroWaveMessage()
+            data1.length = len(receiver.DataFrame)
+            data1.data = bytes(receiver.DataFrame)
+            data1_str = data1.SerializeToString()
+            msg.data1.length = len(data1_str)
+            msg.data1.data = data1_str
+            self.msg_queue.put(msg)
+
+
+class MicroWaveUDPServer(UDPServer):
+    """
+    micro wave udp server
+    """
+    def __init__(self, server_address, msg_queue):
+        super().__init__(server_address, MicroWaveRequestHandler)
+        # create receiver instance
+        self.Receiver = MicroWaveReceiver()
+        self.msg_queue = msg_queue
\ No newline at end of file
diff --git a/src/Server.py b/src/Server.py
new file mode 100644
index 0000000..f0e8ca2
--- /dev/null
+++ b/src/Server.py
@@ -0,0 +1,95 @@
+from posixpath import abspath
+import sys              # NOQA: E402    
+import os
+from flask import Flask, request
+
+batfile_dir = os.path.join(__file__, "..", "protos/compile.bat" )
+batfile_path = os.path.abspath(batfile_dir)
+os.system(batfile_path)
+
+from engine.DeviceManager import DeviceManager
+from engine.MessageQueueThread import MessageQueueThread
+from protos import aerial_pb2 as pb
+
+
+
+class MyServer(Flask):
+    def __init__(self, host, user, password, queue, port):
+        Flask.__init__(self, __name__)
+        self.host = host
+        self.user = user
+        self.password = password
+        self.queue = queue
+        self.port = port
+
+        self.route('/api', methods=['POST'])(self.api)
+        self.route('/', methods=['GET'])(self.hello)
+        self._def_routes()
+        self._device_manager = DeviceManager(host, user, password)
+
+    def _def_routes(self):
+        self.routes = {
+            pb.SysCommand.SYS_COMMAND: self._on_sys_command,
+        }
+
+    def _on_sys_command(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
+        if pb.SubCommand.POWER_ON_SUB_COMMAND == req.sub_command:
+            print('power on message received')
+            self._device_manager.get_device(req.device_id)
+        elif pb.SubCommand.POWER_OFF_SUB_COMMAND == req.sub_command:
+            device = self._device_manager.get_device(req.device_id)
+            device['power_on'] = False
+            print('power off message received')
+        return
+
+    def _default(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
+        print('onknown command')
+
+    def api(self):
+        req = pb.RequestCommand()
+        req.ParseFromString(request.data)
+        dispatcher = self.routes.get(req.sys_command, self._default)
+        resp = pb.ResponseCommand()
+        dispatcher(req, resp)
+        resp.checksum = 12345
+        response = Flask.response_class(
+            resp.SerializeToString(),
+            mimetype='application/x-protobuf'
+        )
+        return response, 200
+
+    def hello(self):
+        return 'Hello, This is acquisition demo API!'
+
+    def run_rabbitmq(self, host: str, user: str, password: str, queue: str) -> None:
+        self._message_queue_thread = MessageQueueThread(
+            host, user, password, queue, self._device_manager)
+        self._message_queue_thread.start()
+
+    def run_all(self):
+        self.run_rabbitmq(self.host, self.user, self.password, self.queue)
+        self.run(debug=False, host="0.0.0.0", port=self.port)
+
+    def send_controll_message(self, device_id, msg):
+        device = self._device_manager.get_device(device_id)
+        if device is None:
+            channel = device.controll_channel
+            channel.basic_publish(
+                exchange='',
+                routing_key='',
+                body=msg.SerializeToString())
+
+    def send_dust_cleaner_cmd(self, device_id, sub_cmd, period):
+        msg = pb.ControllMessage()
+        msg.sys_command = pb.ControlSysCommand.DUST_CLEANER_COMMAND
+        msg.sub_command = sub_cmd
+        data_msg = pb.DustCleanerMessage()
+        data_msg.period = period
+        data1_str = data_msg.SerializeToString()
+        msg.data1.length = len(data1_str)
+        msg.data1.data = data1_str
+
+
+if __name__ == "__main__":
+    server = MyServer(host="localhost", user='chai', password='password123', queue='aerial_rpc', port=5000)
+    server.run_all()
diff --git a/src/Server/Server.py b/src/Server/Server.py
deleted file mode 100644
index 54eafaf..0000000
--- a/src/Server/Server.py
+++ /dev/null
@@ -1,157 +0,0 @@
-import datetime
-import os
-import sys
-import threading
-
-sys.path.append('../')  # NOQA: E402
-
-from protos import aerial_pb2 as pb
-from flask import Flask, request
-import pika
-
-
-class MyServer(Flask):
-    def __init__(self, *args, **kwargs):
-        Flask.__init__(self, *args, **kwargs)
-        self.message_received: int = 0
-        self._def_routes()
-        self.devices = {}
-        self.route('/api', methods=['POST'])(self.api)
-        self.route('/', methods=['GET'])(self.hello)
-
-    def init_rabbmitmq(self, host: str, user: str, password: str, queue: str):
-        credentials = pika.PlainCredentials(user, password)
-        parameters = pika.ConnectionParameters(
-            credentials=credentials, host=host)
-        connection = pika.BlockingConnection(parameters)
-        self.channel = connection.channel()
-        self.channel.queue_declare(queue=queue, durable=True)
-        self.channel.basic_qos(prefetch_count=1)
-        self.channel.basic_consume(
-            on_message_callback=self._on_request, queue=queue)
-
-    def _def_routes(self):
-        self.routes = {
-            pb.SysCommand.SYS_COMMAND: self._on_sys_command,
-            pb.SysCommand.SENSOR_COMMAND: self._on_sensor_command,
-            pb.SysCommand.FIVE_G_COMMAND: self._on_five_g_command,
-            pb.SysCommand.MICRO_WAVE_COMMAND: self._on_micro_wave_command,
-        }
-
-    def _get_device(self, device_id: str):
-        device = self.devices.get(device_id, None)
-        if device is None:
-            start = datetime.datetime.now()
-            if not os.path.exists('./Images'):
-                os.mkdir('./Images')
-            path = f"./Images/{device_id}-{start.strftime('%Y-%m-%d-%H-%M-%S')}"
-            if not os.path.exists(path):
-                os.mkdir(path)
-            device = dict(power_on=True, srart=start, path=path, images=0, micro_wave=0)
-            self.devices[device_id] = device
-        return device
-
-    def _on_sys_command(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
-        if pb.SubCommand.POWER_ON_SUB_COMMAND == req.sub_command:
-            print('power on message received')
-            self._get_device(req.device_id)
-        elif pb.SubCommand.POWER_OFF_SUB_COMMAND == req.sub_command:
-            device = self.devices[req.device_id]
-            device['power_on'] = False
-            print('power off message received')
-        return
-
-    def _on_sensor_command(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
-        print('sensor command')
-
-    def _write_image(self, device: dict, idx: int, image: bytes):
-        if (image is None) | (len(image) == 0):
-            return
-        filename = f'{device["path"]}/{idx}-{device["images"]}.jpg'
-        with open(filename, 'wb') as f:
-            f.write(image)
-
-    def _on_five_g_command(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
-        # print('five g command')
-        if pb.SubCommand.FIVE_G_SEND_IMAGE_SUB_COMMAND == req.sub_command:
-            print(
-                f'send image sub command received, length={req.data1.length}')
-            if req.data1.length > 0:
-                device = self._get_device(req.device_id)
-                img_msg = pb.ImagesMessage()
-                img_msg.ParseFromString(req.data1.data)
-                # print(img_msg.image1)
-                count = device["images"]
-                self._write_image(device, 1, img_msg.image1)
-                self._write_image(device, 2, img_msg.image2)
-                self._write_image(device, 3, img_msg.image3)
-                self._write_image(device, 4, img_msg.image4)
-                self._write_image(device, 5, img_msg.image5)
-                device['images'] += 1
-
-    def _on_micro_wave_command(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
-        print('micro wave command')
-        if pb.SubCommand.MICRO_WAVE_SUB_COMMAND == req.sub_command:
-            print('micro wave sub command received')
-            device = self._get_device(req.device_id)
-            micro_wave_msg = pb.MicroWaveMessage()
-            if (micro_wave_msg.length == len(micro_wave_msg.data)):
-                filename = f'{device["path"]}/{device["micro_wave"]}.micro_wave'
-                with open(filename, 'wb') as f:
-                    f.write(micro_wave_msg.data)
-                device['micro_wave'] += 1
-
-    def _default(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
-        print('onknown command')
-
-    def _on_request(self, ch, method, properties, body):
-        req = pb.RequestCommand()
-        req.ParseFromString(body)
-        dispatcher = self.routes.get(req.sys_command, self._default)
-        resp = pb.ResponseCommand()
-        dispatcher(req, resp)
-        resp.checksum = 12345
-        resp.state = pb.ResponseState.SUCCESS
-        if properties.reply_to != None:
-            ch.basic_publish(exchange='',
-                             routing_key=properties.reply_to,  # 鏀舵秷鎭殑闃熷垪
-                             properties=pika.BasicProperties(
-                                 correlation_id=properties.correlation_id),  # 杩斿洖娑堟伅鐨勯槦鍒�
-                             body=resp.SerializeToString())  # 杩斿洖缁撴灉鏁版嵁
-        ch.basic_ack(delivery_tag=method.delivery_tag)  # 纭繚娑堟伅琚� 瀹㈡埛绔帴鏀�
-        self.message_received += 1
-        return
-
-    def api(self):
-        req = pb.RequestCommand()
-        req.ParseFromString(request.data)
-        dispatcher = self.routes.get(req.sys_command, self._default)
-        resp = pb.ResponseCommand()
-        dispatcher(req, resp)
-        resp.checksum = 12345
-        response = Flask.response_class(
-            resp.SerializeToString(),
-            mimetype='application/x-protobuf'
-        )
-        return response, 200
-
-    def hello(self):
-        return 'Hello, World!'
-
-    def consuming(self):
-        print(' [*] RabbitMQ Server started. Waiting for messages.')
-        self.channel.start_consuming()
-
-    def run_rabbitmq(self):
-        t = threading.Thread(target=self.consuming)
-        t.start()
-
-    def run_all(self, host="localhost", port=5000):
-        self.run_rabbitmq()
-        self.run(debug=False, host=host, port=port)
-
-
-if __name__ == "__main__":
-    server = MyServer(__name__)
-    server.init_rabbmitmq(host='localhost', user='chai', password='password123', queue='aerial_rpc')
-    server.run_all(host="0.0.0.0", port=5000)
diff --git a/src/config.json b/src/config.json
new file mode 100644
index 0000000..c32234f
--- /dev/null
+++ b/src/config.json
@@ -0,0 +1,82 @@
+{
+  "Device_name": "瀹炴椂鍫嗕綋璇嗗埆",
+  "Device_ID": "e8d28684-c908-457e-ade9-381451d26b0c",
+  "Device_Network_para": {
+    "Network_SSID": "thisisaWiFI",
+    "Network_Password": "thisisaWiFIpassword",
+    "Network_mode": "5g",
+    "Network_MAC": "AA-BB-CC-DD-EE-FF",
+    "Network_IP": "192.168.99.99"
+  },
+  "Captue_Image_para": {
+    "brightness": 50,
+    "contrast": 50,
+    "hue": 50,		
+    "saturation": 50,	
+    "clarity": 50,	
+    "white_balance": 50,	
+    "frame_rate": 20,		
+    "capture_interval": 10,	
+    "dodec_type": "h264",	
+    "resolution": "4k",		
+    "IFrame_interval": 10,	
+    "overlap": 0.8,			
+    "level_angle": 2,		
+    "left_angle": -3,		
+    "right_angle": 1,		
+    "device_mid_x": 100,	
+    "device_mid_y": 50,	
+    "device_mid_z": 8000,	
+    "camera_1_x": -100,
+    "camera_1_y": -100,
+    "camera_1_z": -100,
+    "camera_2_x": -100,
+    "camera_2_y": -100,
+    "camera_2_z": -100,
+    "camera_3_x": -100,
+    "camera_3_y": -100,
+    "camera_3_z": -100,
+    "camera_4_x": -100,
+    "camera_4_y": -100,
+    "camera_4_z": -100,
+    "left_pupil_distance": 400,	
+    "right_pupil_distance": 800
+  },
+  "Millimeter_Wave_para": {
+    "level_angle": 2,
+    "left_angle": -3,
+    "right_angle": 1,
+    "MW_x": 100,		
+    "MW_y": 50,		 
+    "MW_z": 8000,		
+    "MW_IP": "192.168.168.9",
+    "MW_Port": 50055,
+    "protocol": "tcp",
+    "dest_IP": "192.168.168.100",	
+    "dest_port": 50056,			
+    "dest_protocol": "xieyi?TCP"
+  },
+  "Clean_Brush_para": {
+    "clean_lens_start_angle": 10,
+    "clean_lens_end_angle": 90,
+    "clean_lens_num": 2,
+    "clean_lens_period": 1200,
+    "clean_self_start_angle": 10,
+    "clean_self_end_angle": 55,
+    "clean_self_num": 2,
+    "clean_self_period": 2400
+  },
+  "High_Light_para": {
+    "open_hour": 18,	
+    "open_minute": 30,	 
+    "close_hour": 6,	
+    "close_minute": 30,	
+    "brightness": 30,	 
+    "color": "white"		
+  },
+  "AI_Algorithm_para": {
+    "threshold": 99.6,	
+    "para_1": 100,
+    "para_2": 1
+  }
+}
\ No newline at end of file
diff --git a/src/engine/AcquisitionMain.py b/src/engine/AcquisitionMain.py
new file mode 100644
index 0000000..24f9c01
--- /dev/null
+++ b/src/engine/AcquisitionMain.py
@@ -0,0 +1,257 @@
+import sys              # NOQA: E402
+import os
+import cv2
+import logging
+import json
+import pika
+import queue
+import requests
+import time
+
+from .CaptureThread import CaptureThread
+from .ControllThread import ControllThread
+from .MicroWaveUDPThread import MicroWaveUDPThread
+
+# add python path of src to sys.path
+src_path = os.path.join(__file__, *(['..'] * 2))
+src_path = os.path.abspath(src_path)
+sys.path.insert(0, src_path)
+
+from protos import aerial_pb2 as pb
+
+
+IMAGE_WIDTH = 3840
+IMAGE_HEIGHT = 2880
+CAMERA_COUNT = 4
+
+class Config:
+    """
+    config class
+    """
+    def __init__(self, config_file: str):
+        self.interval: float = 10
+        self.resolution = '4k'
+
+        self.clean_lens_start_angle = 10
+        self.clean_lens_end_angle = 90
+        self.clean_lens_num = 2
+        self.clean_lens_period = 1200
+        self.clean_self_start_angle = 10
+        self.clean_self_end_angle = 55
+        self.clean_self_num = 2
+        self.clean_self_period = 240
+
+        # load config file
+        with open(config_file, 'r') as f:
+            self.config = json.load(f)
+            self.device_id = self.config['Device_ID']
+            self.interval = self.config['Captue_Image_para']['capture_interval']
+            self.resolution = self.config['Captue_Image_para']['resolution']
+
+            Clean_Brush_para = self.config['Clean_Brush_para']
+            self.clean_lens_start_angle = Clean_Brush_para['clean_lens_start_angle']
+            self.clean_lens_end_angle = Clean_Brush_para['clean_lens_end_angle']
+            self.clean_lens_num = Clean_Brush_para['clean_lens_num']
+            self.clean_lens_period = Clean_Brush_para['clean_lens_period']
+            self.clean_self_start_angle = Clean_Brush_para['clean_self_start_angle']
+            self.clean_self_end_angle = Clean_Brush_para['clean_self_end_angle']
+            self.clean_self_num = Clean_Brush_para['clean_self_num']
+            self.clean_self_period = Clean_Brush_para['clean_self_period']
+            f.close()
+        return
+
+class AcquisitionMain:
+    """
+    acquisition main class
+    """
+    def __init__(self,
+                 rabbitmq_host: str, 
+                 rabbitmq_user: str, 
+                 rabbitmq_password: str,
+                 rabbitmq_queue: str, 
+                 device_id: str, 
+                 api_url: str,
+                 udp_server_host: str,
+                 config: Config) -> None:
+        """
+        init AcquisitionMain class
+        Args:
+            rabbitmq_host: rabbitmq server host
+            rabbitmq_user: rabbitmq user
+            rabbitmq_password: rabbitmq password
+            rabbitmq_queue: rabbitmq upload queue
+            device_id: device id
+            api_url: server api url
+            udp_server_host: udp server host for micro wave, localhost for testing or 192.168.2.65 for production
+            config: config object             
+        """
+        self.rabbitmq_host: str = rabbitmq_host
+        self.udp_server_host: str = udp_server_host
+        self.rabbitmq_user: str = rabbitmq_user
+        self.rabbitmq_password: str = rabbitmq_password
+        self.rabbitmq_queue: str = rabbitmq_queue
+        self.device_id: str = device_id
+        self.message_send: int = 0
+        self.api_url: str = api_url
+        self.config = config
+        self.image_quality: float = 95
+        self.cameras: list = []
+        self.msg_queue = queue.Queue()
+        # thread members
+        self._controll_thread = None
+        self._capture_thread = None
+        self._udp_thread = None
+
+    def _start_udp_thread(self):
+        """start udp thread"""
+        if self._udp_thread is None:
+            self._udp_thread = MicroWaveUDPThread((self.udp_server_host, 9911), self.msg_queue)
+            self._udp_thread.setDaemon(True)
+            self._udp_thread.start()
+
+    def _stop_udp_thread(self):
+        """stop udp thread"""
+        if not self._udp_thread is None:
+            self._udp_thread.stop()
+            self._udp_thread = None
+
+    def _start_capture_thread(self):
+        """start camera capture thread"""
+        if self._capture_thread is None:
+            self._open_cameras()
+            if len(self.cameras) == 0:
+                print("No camera available")
+                return
+            self._capture_thread = CaptureThread(
+                self.cameras, self.msg_queue, self.config.interval, self.image_quality)
+            self._capture_thread.setDaemon(True)
+            self._capture_thread.start()
+
+    def _stop_capture_thread(self):
+        """stop camera capture thread"""
+        if not self._capture_thread is None:
+            self._capture_thread.stop()
+            self._capture_thread = None
+
+    def _start_controll_thread(self):
+        """start controll thread"""
+        if self._controll_thread is None:
+            self._controll_thread = ControllThread(
+                self.rabbitmq_host, self.rabbitmq_user, self.rabbitmq_password, self.device_id + "_callback",
+                self.config)
+            self._controll_thread.setDaemon(True)
+            self._controll_thread.start()
+
+    def _stop_controll_thread(self):
+        """stop controll thread"""
+        if not self._controll_thread is None:
+            self._controll_thread.stop()
+            self._controll_thread = None
+
+    def _connect_message_queue(self) -> None:
+        """connect message queue of rabbitmq"""
+        credentials = pika.PlainCredentials(self.rabbitmq_user, self.rabbitmq_password)
+        parameters = pika.ConnectionParameters(
+            credentials=credentials, host=self.rabbitmq_host)
+        # 杩炴帴闃熷垪鏈嶅姟鍣�
+        self._connection = pika.BlockingConnection(parameters)
+        self.channel = self._connection.channel()
+        self.channel.queue_declare(queue='', exclusive=True)
+
+    def _close_message_queue(self) -> None:
+        """close message queue of rabbitmq"""
+        self._connection.close()
+
+    def _set_logging(self):
+        """set logging"""
+        logging.basicConfig(level=logging.INFO,
+                            format='%(asctime)s %(levelname)s %(message)s',
+                            datefmt='%Y-%m-%d %H:%M:%S')
+        self.logger = logging.getLogger(__name__)
+
+    def _send_data(self, req: pb.RequestCommand) -> None:
+        """send data to rabbitmq"""
+        req.device_id = self.device_id
+        body = req.SerializeToString()
+        self.channel.basic_publish(
+            exchange='',
+            routing_key=self.rabbitmq_queue,
+            body=body)
+        self.message_send += 1
+        return
+
+    def _call(self, req: pb.RequestCommand, timeout: int = 10) -> pb.ResponseCommand:
+        """call server api"""
+        req.device_id = self.device_id
+        self.response = None
+        body = req.SerializeToString()
+        response = requests.post(
+            self.api_url,
+            data=body,
+            headers={'Content-Type': 'application/x-protobuf'},
+            timeout=timeout)
+        resp = pb.ResponseCommand()
+        resp.ParseFromString(response.content)
+        return resp
+
+    def _send_power_on_message(self) -> pb.ResponseCommand:
+        """send power on message"""
+        print("send power on message")
+        req = pb.RequestCommand()
+        req.sys_command = pb.SysCommand.SYS_COMMAND
+        req.sub_command = pb.SubCommand.POWER_ON_SUB_COMMAND
+        return self._call(req)
+
+    def _send_power_off_message(self) -> pb.ResponseCommand:
+        """send power off message"""
+        print("send power off message")
+        req = pb.RequestCommand()
+        req.sys_command = pb.SysCommand.SYS_COMMAND
+        req.sub_command = pb.SubCommand.POWER_OFF_SUB_COMMAND
+        return self._call(req)
+
+    def _open_cameras(self):
+        """
+        Test the ports and returns a tuple with the available ports and the ones that are working.
+        """
+        self.cameras = []
+        self.working_ports = []
+        for dev_port in range(CAMERA_COUNT):
+            camera = cv2.VideoCapture(dev_port, cv2.CAP_DSHOW)
+            camera.set(cv2.CAP_PROP_FRAME_WIDTH, IMAGE_WIDTH)
+            camera.set(cv2.CAP_PROP_FRAME_HEIGHT, IMAGE_HEIGHT)
+            camera.set(cv2.CAP_PROP_FPS, 1)
+            if camera.isOpened():
+                is_reading, img = camera.read()
+                w = camera.get(3)
+                h = camera.get(4)
+                if is_reading:
+                    print("Port %s is working and reads images (%s x %s)" %
+                          (dev_port, h, w))
+                    self.working_ports.append(dev_port)
+                    self.cameras.append(camera)
+
+    def _main_loop(self):
+        # main loop of the acquisition main program
+        self._connect_message_queue()
+        while True:
+            try:
+                if not self.msg_queue.empty():
+                    msg = self.msg_queue.get()
+                    self._send_data(msg)
+                    print("send message")
+                time.sleep(0.1)
+            except Exception as e:
+                break
+        self._close_message_queue()
+        self._stop_udp_thread()
+        self._stop_capture_thread()
+        self._stop_controll_thread()
+        print("Stopped")
+        
+    def start(self) -> None:
+        """start all threads"""
+        self._start_controll_thread()
+        self._start_capture_thread()
+        self._start_udp_thread()
+        self._main_loop()
diff --git a/src/engine/CaptureThread.py b/src/engine/CaptureThread.py
new file mode 100644
index 0000000..f6d588b
--- /dev/null
+++ b/src/engine/CaptureThread.py
@@ -0,0 +1,70 @@
+import sys
+import cv2              # NOQA: E402
+import os
+import numpy as np
+import threading
+import time
+
+
+# add python path of src to sys.path
+src_path = os.path.join(__file__, *(['..'] * 2))
+src_path = os.path.abspath(src_path)
+sys.path.insert(0, src_path)
+
+from protos import aerial_pb2 as pb
+
+
+class CaptureThread(threading.Thread):
+    """
+    camera capture thread
+    """
+    def __init__(self, cameras, msg_queue, interval, image_quality = 95):
+        """init the thread"""
+        threading.Thread.__init__(self)
+        self.cameras = cameras
+        self.msg_queue = msg_queue
+        self.interval = interval
+        self.image_quality = image_quality
+        self.is_stop = False
+    
+    def stop(self):
+        """stop the thread"""
+        self.is_stop = True
+
+    def run(self):
+        print("CaptureThread started")
+        self.next_time = time.time() + self.interval
+        while not self.is_stop:
+            this_time = time.time()
+            if this_time > self.next_time:
+                data1 = pb.ImagesMessage()
+                for i in range(len(self.cameras)):
+                    _, frame = self.cameras[i].read()
+                    encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), self.image_quality]
+                    _, encimg = cv2.imencode('.jpg', frame, encode_param)
+                    if frame is not None:
+                        np_array = np.asarray(encimg)  # frame 杞崲涓簄umpy鏁扮粍
+                        byte_array = np_array.tobytes()  # numpy鏁扮粍杞崲涓篵yte鏁扮粍
+                        if i == 0:
+                            data1.image1 = byte_array
+                        elif i == 1:
+                            data1.image2 = byte_array
+                        elif i == 2:
+                            data1.image3 = byte_array
+                        elif i == 3:
+                            data1.image4 = byte_array
+                data1_str = data1.SerializeToString()
+
+                # create protobuf message
+                req = pb.RequestCommand()
+                req.sys_command = pb.SysCommand.FIVE_G_COMMAND
+                req.sub_command = pb.SubCommand.FIVE_G_SEND_IMAGE_SUB_COMMAND
+                req.data1.length = len(data1_str)
+                req.data1.data = data1_str
+                # add message to queue
+                self.msg_queue.put(req)
+                self.next_time += self.interval
+            time.sleep(0.1)
+        for camera in self.cameras:
+            camera.release()
+        print("CaptureThread stopped")
\ No newline at end of file
diff --git a/src/engine/ControllThread.py b/src/engine/ControllThread.py
new file mode 100644
index 0000000..3de30b8
--- /dev/null
+++ b/src/engine/ControllThread.py
@@ -0,0 +1,86 @@
+
+import os, sys
+import queue
+import time
+import pika
+import threading
+from .DustCleanerThread import DustCleanerThread
+
+# add python path of src to sys.path
+src_path = os.path.join(__file__, *(['..'] * 2))
+src_path = os.path.abspath(src_path)
+sys.path.insert(0, src_path)
+
+from protos import aerial_pb2 as pb
+
+class ControllThread(threading.Thread):
+    """
+    controll thread
+    accept controll message from rabbitmq
+    """
+    def __init__(self, host: str, user: str, password: str, callback_queue: str, config):
+        threading.Thread.__init__(self)
+        # setup dust cleaner command queue and thread
+        self.config = config
+        self._dust_cleaner_cmd_queue = queue.Queue()
+        self._dust_cleaner_thread = DustCleanerThread(self._dust_cleaner_cmd_queue, self.config)
+        self._dust_cleaner_thread.setDaemon(True)
+        self._dust_cleaner_thread.start()
+
+        self._init_callback(host, user, password, callback_queue)
+
+    def _def_routes(self):
+        """define controll message routes"""
+        self.routes = {
+            pb.ControlSysCommand.DUST_CLEANER_COMMAND: self._on_dust_cleaner_command,
+        }
+
+    def _on_dust_cleaner_command(self, msg: pb.ControllMessage):
+        # add message to dust cleaner command queue
+        self._dust_cleaner_cmd_queue.put(msg)
+        return
+        
+    def _default(self, req: pb.ControllMessage):
+        print('default controll handler')
+        return
+
+    def _init_callback(self, host: str, user: str, password: str, queue: str):
+        """init rabbitmq channel"""
+        credentials = pika.PlainCredentials(user, password)
+        parameters = pika.ConnectionParameters(
+            credentials=credentials, host=host)
+        connection = pika.BlockingConnection(parameters)
+        self._channel = connection.channel()
+        self._channel.queue_declare(queue=queue, durable=False)
+        self._channel.basic_qos(prefetch_count=1)
+        self._channel.basic_consume(
+            on_message_callback=self._on_callback_request, queue=queue)
+
+    def _on_callback_request(self, ch, method, properties, body):
+        print('callback received')
+        msg = pb.ControllMessage()
+        msg.ParseFromString(body)
+        dispatcher = self.routes.get(msg.sys_command, self._default)
+        dispatcher(msg)
+
+        ch.basic_ack(delivery_tag=method.delivery_tag)
+        return
+
+    def stop(self):
+        """stop controll thread"""
+        self._channel.stop_consuming()
+        self._dust_cleaner_thread.stop()
+
+    def run(self):
+        print("ControllThread started")
+        self._channel.start_consuming()
+
+
+if __name__ == "__main__":
+    print("Begin CallbackThread Test")
+    cb_thread = ControllThread("localhost", "chai", "password123", "pod1")
+    cb_thread.start()
+
+    time.sleep(3)
+
+    cb_thread.stop()
\ No newline at end of file
diff --git a/src/engine/DeviceManager.py b/src/engine/DeviceManager.py
new file mode 100644
index 0000000..7909d32
--- /dev/null
+++ b/src/engine/DeviceManager.py
@@ -0,0 +1,40 @@
+import datetime
+import os
+import pika
+
+class DeviceManager:
+    def __init__(self, host, user, password):
+        self.host = host
+        self.user = user
+        self.password = password
+        self._devices = {}
+
+    def _create_controll_channel(self, device_id: str):
+        credentials = pika.PlainCredentials(self.user, self.password)
+        parameters = pika.ConnectionParameters(
+            credentials=credentials, host=self.host)
+        # 杩炴帴闃熷垪鏈嶅姟鍣�
+        connection = pika.BlockingConnection(parameters)
+        channel = connection.channel()
+        channel.queue_declare(queue=device_id + "_callback")
+        return channel
+
+    def get_device(self, device_id: str):
+        device = self._devices.get(device_id, None)
+        if device is None:
+            start = datetime.datetime.now()
+            if not os.path.exists('./Images'):
+                os.mkdir('./Images')
+            path = f"./Images/{device_id}-{start.strftime('%Y-%m-%d-%H-%M-%S')}"
+            if not os.path.exists(path):
+                os.mkdir(path)
+            device = dict(
+                power_on=True, 
+                srart=start, 
+                path=path, 
+                images=0, 
+                micro_wave=0,
+                controll_channel=self._create_controll_channel(device_id)
+                )
+            self._devices[device_id] = device
+        return device
\ No newline at end of file
diff --git a/src/engine/DustCleaner.py b/src/engine/DustCleaner.py
new file mode 100644
index 0000000..620a086
--- /dev/null
+++ b/src/engine/DustCleaner.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+__author__ = 'wangzhibo 2022.01.20'
+
+import serial
+import serial.tools.list_ports
+
+
+class DustCleaner():
+    # 鍒濆鍖�
+    def __init__(self, com, bps, timeout):
+        self.port = com
+        self.bps = bps
+        self.timeout = timeout
+        self.com_port = None
+        self.Open()
+
+    # 鎵撳嵃璁惧鍩烘湰淇℃伅
+    def Print_Name(self):
+        print(self.com_port.name)  # 璁惧鍚嶅瓧
+        print(self.com_port.port)  # 璇绘垨鑰呭啓绔彛
+        print(self.com_port.baudrate)  # 娉㈢壒鐜�
+        print(self.com_port.bytesize)  # 瀛楄妭澶у皬
+        print(self.com_port.parity)  # 鏍¢獙浣�
+        print(self.com_port.stopbits)  # 鍋滄浣�
+        print(self.com_port.timeout)  # 璇昏秴鏃惰缃�
+        print(self.com_port.writeTimeout)  # 鍐欒秴鏃�
+        print(self.com_port.xonxoff)  # 杞欢娴佹帶
+        print(self.com_port.rtscts)  # 杞欢娴佹帶
+        print(self.com_port.dsrdtr)  # 纭欢娴佹帶
+        print(self.com_port.interCharTimeout)  # 瀛楃闂撮殧瓒呮椂
+
+    # 鎵撳紑涓插彛
+    def Open(self) -> bool:
+        try:
+            # 鎵撳紑涓插彛锛屽苟寰楀埌涓插彛瀵硅薄
+            self.com_port = serial.Serial(self.port, self.bps, timeout=self.timeout)
+            # 鍒ゆ柇鏄惁鎵撳紑鎴愬姛
+            if (self.com_port.is_open):
+                return True
+            else:
+                return False
+        except Exception as _:
+            return False
+
+    # 鍏抽棴涓插彛
+    def Close(self) -> None:
+        self.com_port.close()
+        print(self.com_port.is_open)  # 妫�楠屼覆鍙f槸鍚︽墦寮�
+
+    # 鎵撳嵃鍙敤涓插彛鍒楄〃
+    @classmethod
+    def Print_Used_Com():
+        port_list = list(serial.tools.list_ports.comports())
+        print(port_list)
+
+    # 鍙戞暟鎹�
+    def Send(self, data):
+        if (not self.com_port is None) and self.com_port.is_open():
+            self.com_port.write(data)
+            self.Recive()
+
+    def SendNoChecksum(self, data):
+        checksum = 0
+        array = bytearray(data)
+        for i in array[:-1]:
+            checksum += i
+        checksum = checksum % 256
+        array[-1] = checksum
+        self.Send(bytes(array))
+
+    def Send_onetime_clean_self(self):   # 鍙戦�佸嚱鏁�
+        self.Send(bytes([0x5A, 0x04, 0x70, 0xCE]))    # 鐢╳rite鍑芥暟鍚戜覆鍙e彂閫佹暟鎹�
+
+    def Send_onetime_clean_camera(self):   # 鍙戦�佸嚱鏁�
+        self.Send(bytes([0x5A, 0x04, 0x71, 0xCF]))    # 鐢╳rite鍑芥暟鍚戜覆鍙e彂閫佹暟鎹�
+
+    def Send_close_period_clean_self(self):
+        self.Send(bytes([0x5A, 0x06, 0x74, 0x00, 0x00, 0xD4]))    # close self period clean
+
+    def Send_close_period_clean_camera(self):
+        self.Send(bytes([0x5A, 0x06, 0x75, 0x00, 0x00, 0xD5]))    # close self period clean
+
+    def Send_period_clean_self(self, period: int):
+        minute_H = period // 256
+        minute_L = period % 256
+        self.SendNoChecksum(bytes([0x5A, 0x06, 0x74, minute_H, minute_L, 0x00]))
+
+    def Send_period_clean_camera(self, period: int):
+        minute_H = period // 256
+        minute_L = period % 256
+        self.SendNoChecksum(bytes([0x5A, 0x06, 0x75, minute_H, minute_L, 0x00]))
+
+    def Send_self_clean_params(self, start_angle: int, end_angle: int, num: int):
+        self.SendNoChecksum(bytes([0x5A, 0x07, 0x72, start_angle, end_angle, num, 0x00]))
+
+    def Send_camera_clean_params(self, start_angle: int, end_angle: int, num: int):
+        self.SendNoChecksum(bytes([0x5A, 0x07, 0x73, start_angle, end_angle, num, 0x00]))
+
+    def Recive(self):
+        return self.main_engine.read_all()
+
+
+if __name__ == '__main__':
+    # DustCleaner.Print_Used_Com()
+    cleaner = DustCleaner("com11", 115200, 1)
+    if cleaner.Open():
+        cleaner.Send_close_period_clean_camera()
+        cleaner.Recive()
diff --git a/src/engine/DustCleanerThread.py b/src/engine/DustCleanerThread.py
new file mode 100644
index 0000000..2405305
--- /dev/null
+++ b/src/engine/DustCleanerThread.py
@@ -0,0 +1,67 @@
+import sys              # NOQA: E402
+import os
+import threading
+import time
+
+from .DustCleaner import DustCleaner
+
+# add python path of src to sys.path
+src_path = os.path.join(__file__, *(['..'] * 2))
+src_path = os.path.abspath(src_path)
+sys.path.insert(0, src_path)
+
+from protos import aerial_pb2 as pb
+
+
+
+class DustCleanerThread (threading.Thread):
+    """
+    dust cleaner threads
+    """
+    def __init__(self, cmd_queue, config):
+        threading.Thread.__init__(self)
+        self.config = config
+        self.cmd_queue = cmd_queue
+        self.stop = False
+        self.cleaner = DustCleaner("com11", 115200, 1)
+        self._init_dust_cleaner()
+
+    def _init_dust_cleaner(self):
+        """init dust cleaner"""
+        # send dust cleaner self clean command
+        self.cleaner.Send_self_clean_params(
+            self.config.clean_self_start_angle,
+            self.config.clean_self_end_angle,
+            self.config.clean_self_num)
+        # send dust cleaner period clean self command
+        self.cleaner.Send_period_clean_self(self.config.clean_self_period)
+        # send dust cleaner camera clean command
+        self.cleaner.Send_camera_clean_params(
+            self.config.clean_lens_start_angle,
+            self.config.clean_lens_end_angle,
+            self.config.clean_lens_num)
+        # send dust cleaner period clean camera command
+        self.cleaner.Send_period_clean_camera(self.config.clean_lens_period)
+
+    def run(self):
+        print("DustCleanerThread started")
+        while not self.stop:
+            # check if there is a command in the queue
+            if not self.cmd_queue.empty():
+                cmd = self.cmd_queue.get()
+                dust_cleaner_msg = pb.DustCleanerMessage()
+                dust_cleaner_msg.ParseFromString(cmd.data1.data)
+                if cmd.sub_command == pb.ControlSubCommand.DUST_CLEANER_ONETIME_CLEAN_SELF:
+                    self.cleaner.Send_onetime_clean_self()
+                elif cmd.sub_command == pb.ControlSubCommand.DUST_CLEANER_ONETIME_CLEAN_CAMERA:
+                    self.cleaner.Send_onetime_clean_camera()
+                elif cmd.sub_command == pb.ControlSubCommand.DUST_CLEANER_CLOSE_PERIOD_CLEAN_SELF:
+                    self.cleaner.Send_close_period_clean_self()
+                elif cmd.sub_command == pb.ControlSubCommand.DUST_CLEANER_CLOSE_PERIOD_CLEAN_CAMERA:
+                    self.cleaner.Send_close_period_clean_camera()
+                elif cmd.sub_command == pb.ControlSubCommand.DUST_CLEANER_PERIOD_CLEAN_SELF:
+                    self.cleaner.Send_close_period_clean_self(dust_cleaner_msg.period)
+                elif cmd.sub_command == pb.ControlSubCommand.DUST_CLEANER_PERIOD_CLEAN_CAMERA:
+                    self.cleaner.Send_period_clean_camera(dust_cleaner_msg.period)
+            time.sleep(0.1)
+        print("DustCleanerThread stopped")
diff --git a/src/engine/MessageQueueThread.py b/src/engine/MessageQueueThread.py
new file mode 100644
index 0000000..d72f7df
--- /dev/null
+++ b/src/engine/MessageQueueThread.py
@@ -0,0 +1,108 @@
+import sys              # NOQA: E402
+import os 
+import pika
+import threading
+from ctypes import sizeof
+from .DeviceManager import DeviceManager
+
+# add python path of src to sys.path
+src_path = os.path.join(__file__, *(['..'] * 2))
+src_path = os.path.abspath(src_path)
+sys.path.insert(0, src_path)
+
+from microwave.components import HeaderBlock
+from protos import aerial_pb2 as pb
+
+class MessageQueueThread(threading.Thread):
+    def __init__(self,
+                 host: str, 
+                 user: str, 
+                 password: str, 
+                 queue: str, 
+                 device_manager: DeviceManager):
+        threading.Thread.__init__(self)
+        self._device_manager = device_manager
+        self._def_routes()
+        self.message_received: int = 0
+
+        credentials = pika.PlainCredentials(user, password)
+        parameters = pika.ConnectionParameters(
+            credentials=credentials, host=host)
+        connection = pika.BlockingConnection(parameters)
+        self._channel = connection.channel()
+        self._channel.queue_declare(queue=queue, durable=True)
+        self._channel.basic_qos(prefetch_count=1)
+        self._channel.basic_consume(
+            on_message_callback=self._on_request, queue=queue)
+
+    def _def_routes(self):
+        self._routes = {
+            pb.SysCommand.FIVE_G_COMMAND: self._on_five_g_command,
+            pb.SysCommand.MICRO_WAVE_COMMAND: self._on_micro_wave_command,
+        }
+
+    def _default(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
+        print('onknown command')
+
+    def _write_image(self, device: dict, idx: int, image: bytes):
+        if (image is None) | (len(image) == 0):
+            return
+        filename = f'{device["path"]}/{idx}-{device["images"]}.jpg'
+        with open(filename, 'wb') as f:
+            f.write(image)
+            f.close()
+
+    def _on_five_g_command(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
+        # print('five g command')
+        if pb.SubCommand.FIVE_G_SEND_IMAGE_SUB_COMMAND == req.sub_command:
+            print(
+                f'send image sub command received, length={req.data1.length}')
+            if req.data1.length > 0:
+                device = self._device_manager.get_device(req.device_id)
+                img_msg = pb.ImagesMessage()
+                img_msg.ParseFromString(req.data1.data)
+                # print(img_msg.image1)
+                count = device["images"]
+                self._write_image(device, 1, img_msg.image1)
+                self._write_image(device, 2, img_msg.image2)
+                self._write_image(device, 3, img_msg.image3)
+                self._write_image(device, 4, img_msg.image4)
+                self._write_image(device, 5, img_msg.image5)
+                device['images'] += 1
+
+    def _on_micro_wave_command(self, req: pb.RequestCommand, resp: pb.ResponseCommand):
+        print('micro wave command')
+        if pb.SubCommand.MICRO_WAVE_SUB_COMMAND == req.sub_command:
+            print('micro wave sub command received')
+            device = self._device_manager.get_device(req.device_id)
+            micro_wave_msg = pb.MicroWaveMessage()
+            micro_wave_msg.ParseFromString(req.data1.data)
+            if (micro_wave_msg.length == len(micro_wave_msg.data)):
+                header = HeaderBlock.from_buffer_copy(micro_wave_msg[:sizeof(HeaderBlock)])
+                filename = f'{device["path"]}/{device["micro_wave"]}-{header.FrameNumber}.micro_wave'
+                with open(filename, 'wb') as f:
+                    f.write(micro_wave_msg.data)
+                    f.close()
+                device['micro_wave'] += 1
+
+    def _on_request(self, ch, method, properties, body):
+        req = pb.RequestCommand()
+        req.ParseFromString(body)
+        dispatcher = self._routes.get(req.sys_command, self._default)
+        resp = pb.ResponseCommand()
+        dispatcher(req, resp)
+        resp.checksum = 12345
+        resp.state = pb.ResponseState.SUCCESS
+        if properties.reply_to != None:
+            ch.basic_publish(exchange='',
+                             routing_key=properties.reply_to,  # 鏀舵秷鎭殑闃熷垪
+                             properties=pika.BasicProperties(
+                                 correlation_id=properties.correlation_id),  # 杩斿洖娑堟伅鐨勯槦鍒�
+                             body=resp.SerializeToString())  # 杩斿洖缁撴灉鏁版嵁
+        ch.basic_ack(delivery_tag=method.delivery_tag)  # 纭繚娑堟伅琚� 瀹㈡埛绔帴鏀�
+        self.message_received += 1
+        return
+
+    def run(self):
+        print(' [*] RabbitMQ Server started. Waiting for messages.')
+        self._channel.start_consuming()
diff --git a/src/engine/MicroWaveUDPThread.py b/src/engine/MicroWaveUDPThread.py
new file mode 100644
index 0000000..7ec90ba
--- /dev/null
+++ b/src/engine/MicroWaveUDPThread.py
@@ -0,0 +1,22 @@
+import os, sys
+import threading
+
+# add python path of src to sys.path
+src_path = os.path.join(__file__, *(['..'] * 2))
+src_path = os.path.abspath(src_path)
+sys.path.insert(0, src_path)
+
+from microwave.servers import MicroWaveUDPServer
+
+
+class MicroWaveUDPThread (threading.Thread):
+    def __init__(self, server_address, msg_queue):
+        threading.Thread.__init__(self)
+        self.udp_server = MicroWaveUDPServer(server_address, msg_queue)
+
+    def run(self):
+        print("MicroWaveUDPThread started")
+        self.udp_server.serve_forever()
+
+    def stop(self):
+        self.udp_server.server_close()
\ No newline at end of file
diff --git a/src/engine/__init__.py b/src/engine/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/engine/__init__.py
diff --git a/proto/aerial.proto b/src/protos/aerial.proto
similarity index 83%
rename from proto/aerial.proto
rename to src/protos/aerial.proto
index 8be6467..2f56188 100644
--- a/proto/aerial.proto
+++ b/src/protos/aerial.proto
@@ -120,3 +120,34 @@
     bytes data = 6;              // 鏁版嵁
     int32 checksum = 7;          // 鏍¢獙鐮�
 }
+
+
+// 鏈嶅姟绔悜閲囬泦绔彂閫佺殑涓绘寚浠�
+enum ControlSysCommand {
+  DUST_CLEANER_COMMAND = 0;
+}
+
+// 鏈嶅姟绔悜閲囬泦绔彂閫佺殑瀛愬懡浠�
+enum ControlSubCommand {
+  // dust cleaner sub commands
+  DUST_CLEANER_ONETIME_CLEAN_SELF         = 0;
+  DUST_CLEANER_ONETIME_CLEAN_CAMERA       = 1;
+  DUST_CLEANER_CLOSE_PERIOD_CLEAN_SELF    = 2;
+  DUST_CLEANER_CLOSE_PERIOD_CLEAN_CAMERA  = 3;
+  DUST_CLEANER_PERIOD_CLEAN_SELF          = 4;
+  DUST_CLEANER_PERIOD_CLEAN_CAMERA        = 5;
+}
+
+// dust cleaner command message
+message DustCleanerMessage  {
+  uint32 period = 1;
+}
+
+// 鎺у埗涓婁綅鏈烘秷鎭�
+message ControllMessage {
+  int32 sync_word = 1;                // 鍚屾瀛�
+  ControlSysCommand sys_command = 3;  // 涓诲懡浠�
+  ControlSubCommand sub_command = 4;  // 瀛愬懡浠�
+  DataField data = 5;                 // 鏁版嵁
+  int32 checksum = 7;                 // 鏍¢獙鐮�
+}
diff --git a/src/protos/compile.bat b/src/protos/compile.bat
new file mode 100644
index 0000000..745ae31
--- /dev/null
+++ b/src/protos/compile.bat
@@ -0,0 +1,11 @@
+@echo off
+
+@REM change to the filepath
+cd %~dp0
+echo change to path: %cd%
+
+set protoc=%cd%\..\..\env\aerial_deploy\Library\bin\protoc.exe
+
+%protoc% aerial.proto --python_out=%cd%
+
+echo compile done!
\ No newline at end of file
diff --git a/src/requirements.yaml b/src/requirements.yaml
new file mode 100644
index 0000000..f5a66c7
--- /dev/null
+++ b/src/requirements.yaml
@@ -0,0 +1,149 @@
+name: aerial
+channels:
+  - conda-forge
+  - defaults
+dependencies:
+  - bitarray=2.3.6=py39hb82d6ee_0
+  - bzip2=1.0.8=h8ffe710_4
+  - ca-certificates=2021.10.8=h5b45459_0
+  - click=8.0.3=py39hcbf5309_1
+  - colorama=0.4.4=pyh9f0ad1d_0
+  - flask=2.0.2=pyhd8ed1ab_0
+  - freetype=2.10.4=h546665d_1
+  - fribidi=1.0.10=h8d14728_0
+  - itsdangerous=2.0.1=pyhd8ed1ab_0
+  - jbig=2.1=h8d14728_2003
+  - jinja2=3.0.3=pyhd8ed1ab_0
+  - jpeg=9e=h8ffe710_0
+  - lcms2=2.12=h2a16943_0
+  - lerc=3.0=h0e60522_0
+  - libdeflate=1.8=h8ffe710_0
+  - libffi=3.4.2=h8ffe710_5
+  - libimagequant=2.17.0=hcfcfb64_1
+  - libpng=1.6.37=h1d00b33_2
+  - libprotobuf=3.19.4=h7755175_0
+  - libtiff=4.3.0=hd413186_2
+  - libwebp=1.2.2=h57928b3_0
+  - libwebp-base=1.2.2=h8ffe710_1
+  - libxcb=1.13=hcd874cb_1004
+  - libzlib=1.2.11=h8ffe710_1013
+  - llvm-openmp=12.0.1=h2d74725_1
+  - lz4-c=1.9.3=h8ffe710_1
+  - m2w64-gcc-libgfortran=5.3.0=6
+  - m2w64-gcc-libs=5.3.0=7
+  - m2w64-gcc-libs-core=5.3.0=7
+  - m2w64-gmp=6.1.0=2
+  - m2w64-libwinpthread-git=5.0.0.4634.697f757=2
+  - markupsafe=2.0.1=py39hb82d6ee_1
+  - msys2-conda-epoch=20160418=1
+  - openjpeg=2.4.0=hb211442_1
+  - openssl=3.0.0=h8ffe710_2
+  - pika=1.2.0=pyh44b312d_0
+  - pillow=9.0.1=py39ha53f419_0
+  - pip=22.0.3=pyhd8ed1ab_0
+  - protobuf=3.19.4=py39h415ef7b_0
+  - pthread-stubs=0.4=hcd874cb_1001
+  - python=3.9.10=hcf16a7b_2_cpython
+  - python_abi=3.9=2_cp39
+  - setuptools=60.8.1=py39hcbf5309_0
+  - six=1.16.0=pyh6c4a22f_0
+  - sqlite=3.37.0=h8ffe710_0
+  - tk=8.6.11=h8ffe710_1
+  - tzdata=2021e=he74cb21_0
+  - ucrt=10.0.20348.0=h57928b3_0
+  - vc=14.2=hb210afc_6
+  - vs2015_runtime=14.29.30037=h902a5da_6
+  - werkzeug=2.0.3=pyhd8ed1ab_1
+  - wheel=0.37.1=pyhd8ed1ab_0
+  - xorg-libxau=1.0.9=hcd874cb_0
+  - xorg-libxdmcp=1.1.3=hcd874cb_0
+  - xz=5.2.5=h62dcd97_1
+  - zlib=1.2.11=h8ffe710_1013
+  - zstd=1.5.2=h6255e5f_0
+  - pip:
+    - anyio==3.5.0
+    - argon2-cffi==21.3.0
+    - argon2-cffi-bindings==21.2.0
+    - asttokens==2.0.5
+    - attrs==21.4.0
+    - babel==2.9.1
+    - backcall==0.2.0
+    - black==22.1.0
+    - bleach==4.1.0
+    - certifi==2021.10.8
+    - cffi==1.15.0
+    - charset-normalizer==2.0.12
+    - debugpy==1.5.1
+    - decorator==5.1.1
+    - defusedxml==0.7.1
+    - deprecation==2.1.0
+    - entrypoints==0.4
+    - executing==0.8.2
+    - future==0.18.2
+    - idna==3.3
+    - ipykernel==6.9.0
+    - ipython==8.0.1
+    - ipython-genutils==0.2.0
+    - ipywidgets==7.6.5
+    - iso8601==1.0.2
+    - jedi==0.18.1
+    - json5==0.9.6
+    - jsonschema==4.4.0
+    - jupyter-client==7.1.2
+    - jupyter-core==4.9.1
+    - jupyter-packaging==0.11.1
+    - jupyter-server==1.13.5
+    - jupyterlab==3.2.9
+    - jupyterlab-pygments==0.1.2
+    - jupyterlab-server==2.10.3
+    - jupyterlab-widgets==1.0.2
+    - matplotlib-inline==0.1.3
+    - mistune==0.8.4
+    - mypy-extensions==0.4.3
+    - nbclassic==0.3.5
+    - nbclient==0.5.10
+    - nbconvert==6.4.2
+    - nbformat==5.1.3
+    - nest-asyncio==1.5.4
+    - notebook==6.4.8
+    - numpy==1.22.2
+    - open3d==0.14.1
+    - opencv-contrib-python==4.5.5.62
+    - packaging==21.3
+    - pandocfilters==1.5.0
+    - parso==0.8.3
+    - pathspec==0.9.0
+    - pickleshare==0.7.5
+    - platformdirs==2.5.0
+    - prometheus-client==0.13.1
+    - prompt-toolkit==3.0.28
+    - pure-eval==0.2.2
+    - pycparser==2.21
+    - pygments==2.11.2
+    - pyparsing==3.0.7
+    - pyrsistent==0.18.1
+    - pyserial==3.5
+    - python-dateutil==2.8.2
+    - pytz==2021.3
+    - pywin32==303
+    - pywinpty==1.1.6
+    - pyyaml==6.0
+    - pyzmq==22.3.0
+    - requests==2.27.1
+    - send2trash==1.8.0
+    - serial==0.0.97
+    - sniffio==1.2.0
+    - stack-data==0.1.4
+    - terminado==0.13.1
+    - testpath==0.5.0
+    - tomli==2.0.1
+    - tomlkit==0.9.2
+    - tornado==6.1
+    - traitlets==5.1.1
+    - typing-extensions==4.1.1
+    - urllib3==1.26.8
+    - wcwidth==0.2.5
+    - webencodings==0.5.1
+    - websocket-client==1.2.3
+    - widgetsnbextension==3.5.2
+prefix: C:\Users\wuxinjun\miniconda3\envs\aerial
diff --git a/src/udp_test/ParserTest.py b/src/udp_test/ParserTest.py
deleted file mode 100644
index a7e4397..0000000
--- a/src/udp_test/ParserTest.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import MicroWaveParser
-
-
-if __name__ == '__main__':
-    files = ["../../MicroWaveData/packet{}.udp".format(i) for i in range(0, 16)]
-    parser = MicroWaveParser.MicroWaveParser()
-    for file in files:
-        with open(file, "rb") as f:
-            data = f.read()
-            parser.AddBuf(data)
-            print(parser.PointCloudReady)
-
-
diff --git a/src/udp_test/parser.py b/src/udp_test/parser.py
deleted file mode 100644
index b2c49a1..0000000
--- a/src/udp_test/parser.py
+++ /dev/null
@@ -1,67 +0,0 @@
-from ctypes import Structure, c_byte, c_ubyte, c_uint32, c_uint16, sizeof
-from sys import argv
-import os 
-
-class Handshake(Structure):
-    _fields_=[
-        ('Magic', c_ubyte * 8),
-        ('FrameDataLength',c_uint32),
-        ('Reserved',c_ubyte * 12)
-    ]
-
-class Header(Structure):
-     _fields_=[
-        ('Magic', c_ubyte * 8),
-        ('FrameNumber',c_uint32),
-        ('VersionNumber',c_uint32),
-        ('NumberOfDetection',c_uint16),
-        ('NumberOfTrack',c_uint16),
-        ('HostSpeed',c_uint16),
-        ('HostAngle',c_uint16),
-        ('Reserved',c_byte * 8),
-        ('RangeAccuracyIdx',c_uint16),
-        ('DopplerAccuracyIdx',c_uint16),
-        ('AzimuthAccuracyIdx',c_uint16),
-        ('ElevationAccuracyIdx',c_uint16),
-        ('DspWorkload',c_byte),
-        ('ARMWorkload', c_byte),
-        ('Reserved2',c_byte * 6),
-    ]
-
-def bytes_hex(bytes):
-    return ' '.join(["%02x" % b for b in bytes])
-
-if __name__ == '__main__':
-    if len(argv) != 2:
-        print('Usage: python parser.py [filename]')
-        exit(1)
-
-    print(sizeof(Handshake))
-    filename = argv[1]
-    file_size = os.path.getsize(filename)
-    with open(filename, "rb") as udp_file:
-        if file_size == 24:
-            handshake = Handshake.from_buffer_copy(udp_file.read(sizeof(Handshake)))
-            udp_file.close()
-            print('Magic: ', bytes_hex(handshake.Magic))
-            print('FrameDataLength: ', handshake.FrameDataLength)
-            print('Reserved: ', bytes_hex(handshake.Reserved))
-        elif file_size == 256:
-            header = Header.from_buffer_copy(udp_file.read(sizeof(Header)))
-            udp_file.close()
-            print('Magic: ', bytes_hex(header.Magic))
-            print('FrameNumber: ', header.FrameNumber)
-            print('VersionNumber: ', header.VersionNumber)
-            print('NumberOfDetection: ', header.NumberOfDetection)
-            print('NumberOfTrack: ', header.NumberOfTrack)
-            print('HostSpeed: ', header.HostSpeed)
-            print('HostAngle: ', header.HostAngle)
-            print('Reserved: ', bytes_hex(header.Reserved)) 
-            print('RangeAccuracyIdx: ', header.RangeAccuracyIdx)
-            print('DopplerAccuracyIdx: ', header.DopplerAccuracyIdx)
-            print('AzimuthAccuracyIdx: ', header.AzimuthAccuracyIdx)
-            print('ElevationAccuracyIdx: ', header.ElevationAccuracyIdx)
-            print('DspWorkload: ', header.DspWorkload)
-            print('ARMWorkload: ', header.ARMWorkload)
-            print('Reserved2: ', bytes_hex(header.Reserved2))
-        
\ No newline at end of file
diff --git a/src/udp_test/send.py b/src/udp_test/send.py
deleted file mode 100644
index 7a8aa1c..0000000
--- a/src/udp_test/send.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import time
-import socket
-
-packed_count = 0
-if __name__ == '__main__':
-    files = ["../../MicroWaveData/packet{}.udp".format(i) for i in range(0, 16)]
-
-    for file in files:
-        with open(file, "rb") as f:
-            message = f.read()
-            client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-            client_socket.settimeout(1.0)
-            addr = ("127.0.0.1", 9911)
-            client_socket.sendto(message, addr)
-            time.sleep(0.1)
diff --git a/startClient.bat b/startClient.bat
new file mode 100644
index 0000000..87ded5b
--- /dev/null
+++ b/startClient.bat
@@ -0,0 +1,16 @@
+@echo off
+
+@REM change to the filepath
+cd %~dp0
+echo change to path: %cd%
+
+set aerialpython=%cd%/env/aerial_deploy/python.exe
+set envsetting=%cd%/env/SettingEnv.bat
+
+@REM Setting the env
+if not exist %aerialpython% (
+    %envsetting% 
+)
+
+@REM Launch the Client
+%aerialpython% ./src/Client.py
diff --git a/startServer.bat b/startServer.bat
new file mode 100644
index 0000000..49b8ce6
--- /dev/null
+++ b/startServer.bat
@@ -0,0 +1,16 @@
+@echo off
+
+@REM change to the filepath
+cd %~dp0
+echo change to path: %cd%
+
+set aerialpython=%cd%/env/aerial_deploy/python.exe
+set envsetting=%cd%/env/SettingEnv.bat
+
+@REM Setting the env
+if not exist %aerialpython% (
+    %envsetting% 
+)
+
+@REM Launch the Server
+%aerialpython% ./src/Server.py

--
Gitblit v1.9.1